Commit 60267dde authored by Maria Matejka's avatar Maria Matejka Committed by Maria Matejka
Browse files

Garbage collector for parallel execution

parent 2a0d6c32
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -160,10 +160,11 @@ $(client) $(daemon):
	$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

$(objdir)/sysdep/paths.h: Makefile
	echo  >$@ "/* Generated by Makefile, don't edit manually! */"
	echo >>$@ "#define PATH_CONFIG_FILE \"@CONFIG_FILE@\""
	echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\""
	if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi
	$(E)echo GEN $@
	$(Q)echo  >$@ "/* Generated by Makefile, don't edit manually! */"
	$(Q)echo >>$@ "#define PATH_CONFIG_FILE \"@CONFIG_FILE@\""
	$(Q)echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\""
	$(Q)if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi

# Unit tests rules

+1 −1
Original line number Diff line number Diff line
src := bitmap.c bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
src := bitmap.c bitops.c checksum.c event.c flowspec.c gc.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
obj := $(src-o-files)
$(all-daemon)

+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#ifndef _BIRD_BIRDLIB_H_
#define _BIRD_BIRDLIB_H_

#include "sysdep/config.h"
#include "lib/alloca.h"

/* Ugly structure offset handling macros */

lib/gc.c

0 → 100644
+187 −0
Original line number Diff line number Diff line
/*
 *	BIRD Library -- Garbage Collector
 *
 *	(c) 2020 Maria Matejka <mq@jmq.cz>
 *	(c) 2020 CZ.NIC z.s.p.o.
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include "lib/gc.h"
#include "lib/resource.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>

static pthread_mutex_t gc_mutex = PTHREAD_MUTEX_INITIALIZER;
static u64 gc_last_round = 0, gc_oldest_round = 1, gc_oldest_running_round = 1;
_Thread_local u64 gc_current_round = 0;

#define DEFAULT_CONCURRENT_GC_ROUNDS  32

static u64 *gc_end = NULL;
static u64 gc_end_size = 0;

static u64 gc_offset = 0;
#define RTOI(round) ((round) - gc_offset)
#define ITOR(index) ((index) + gc_offset)

#define DEFAULT_GC_CALLBACKS  4

struct gc_callback_set **gc_callbacks = NULL;
static uint gc_callbacks_cnt = 0, gc_callbacks_size = 0;

#define GDBG(what) debug(what " last:%d oldest:%d oldest_running:%d current:%d\n", \
    gc_last_round, gc_oldest_round, gc_oldest_running_round, gc_current_round)

void gc_enter(void)
{
  /* Everything is done locked. */
  pthread_mutex_lock(&gc_mutex);

  GDBG("gc_enter in");

  ASSERT(gc_current_round == 0);

  /* No round keeper? Just create some. */
  if (!gc_end)
  {
    gc_end = xmalloc(sizeof(u64) * DEFAULT_CONCURRENT_GC_ROUNDS);
    gc_end_size = DEFAULT_CONCURRENT_GC_ROUNDS;
  }

  /* Update current round ID */
  gc_current_round = ++gc_last_round;

  /* We're at the end of the array */
  if (RTOI(gc_current_round) >= gc_end_size)
  {
    /* How much space is in the beginning? */
    u64 skip = RTOI(gc_oldest_round);

    if (skip >= gc_end_size/2)
    {
      /* Enough. Move to the beginning. */
      memcpy(gc_end, gc_end + skip, (gc_current_round - gc_oldest_round) * sizeof(u64));
      gc_offset += skip;
    }
    else
      /* Not enough. Realloc. */
      gc_end = xrealloc(gc_end, (gc_end_size *= 2) * sizeof(u64));
  }

  u64 index = RTOI(gc_current_round);
  gc_end[index] = 0;

  /* Run hooks */
  for (uint i=0; i<gc_callbacks_cnt; i++)
    if (gc_callbacks[i])
      CALL(gc_callbacks[i]->enter, gc_current_round, gc_callbacks[i]);

  GDBG("gc_enter out");

  /* We're done now. Unlock. */
  pthread_mutex_unlock(&gc_mutex);
}

void gc_exit(void)
{
  /* Everything is done locked. */
  pthread_mutex_lock(&gc_mutex);

  GDBG("gc_exit in");

  ASSERT(gc_current_round <= gc_last_round);
  ASSERT(gc_current_round >= gc_oldest_round);

  u64 index = RTOI(gc_current_round);
  gc_end[index] = gc_last_round;

  /* Run hooks */
  for (uint i=0; i<gc_callbacks_cnt; i++)
    if (gc_callbacks[i])
      CALL(gc_callbacks[i]->exit, gc_current_round, gc_callbacks[i]);

  gc_current_round = 0;

  GDBG("gc_exit out");

  /* Done. Unlock. */
  pthread_mutex_unlock(&gc_mutex);

  /* Do some cleanup */
  uint max = 4;
  while (max-- && gc_cleanup())
    ;
}

_Bool gc_cleanup(void)
{
  pthread_mutex_lock(&gc_mutex);

  GDBG("gc_cleanup in");

  /* There is nothing to clean up */
  if (gc_last_round < gc_oldest_round)
    goto fail;

  /* The oldest round is still running */
  u64 oldest_round_end = gc_end[RTOI(gc_oldest_round)];
  if (oldest_round_end == 0)
    goto fail;

  if (gc_oldest_running_round < gc_oldest_round)
    gc_oldest_running_round = gc_oldest_round + 1;

  /* Some overlapping round is still running */
  while (gc_oldest_running_round <= oldest_round_end)
    if (gc_end[RTOI(gc_oldest_running_round++)] == 0)
      goto fail;

  /* Run hooks */
  for (uint i=0; i<gc_callbacks_cnt; i++)
    if (gc_callbacks[i])
      CALL(gc_callbacks[i]->cleanup, gc_oldest_round, gc_callbacks[i]);

  gc_oldest_round++;

  GDBG("gc_exit out cleaned up");

  pthread_mutex_unlock(&gc_mutex);
  return 1;

fail:
  GDBG("gc_exit out nothing to clean");

  pthread_mutex_unlock(&gc_mutex);
  return 0;
}

void gc_register(struct gc_callback_set *gcs)
{
  pthread_mutex_lock(&gc_mutex);

  if (!gc_callbacks)
    gc_callbacks = xmalloc(sizeof(struct gc_callback_set *) * (gc_callbacks_size = DEFAULT_GC_CALLBACKS));

  if (gc_callbacks_cnt == gc_callbacks_size)
    gc_callbacks = xrealloc(gc_callbacks, sizeof(struct gc_callback_set *) * (gc_callbacks_size *= 2));

  gc_callbacks[gc_callbacks_cnt++] = gcs;

  pthread_mutex_unlock(&gc_mutex);
}

void gc_unregister(struct gc_callback_set *gcs)
{
  pthread_mutex_lock(&gc_mutex);

  for (uint i=0; i<gc_callbacks_cnt; i++)
    if (gc_callbacks[i] == gcs)
    {
      gc_callbacks[i] = NULL;
      break;
    }

  pthread_mutex_unlock(&gc_mutex);
}

lib/gc.h

0 → 100644
+42 −0
Original line number Diff line number Diff line
/*
 *	BIRD Library -- Garbage Collector for Threads
 *
 *	(c) 2020 Maria Matejka <mq@jmq.cz>
 *	(c) 2020 CZ.NIC z.s.p.o.
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#ifndef _BIRD_GC_H_
#define _BIRD_GC_H_

#include "lib/birdlib.h"

/* Call gc_enter() before any code where a shared structure
 * may be accessed by this thread. Mostly this should be called
 * immediately after a poll() returns.  */
void gc_enter(void);

/* Call gc_exit() when no shared structure is being held
 * by the current thread. Mostly this means calling this
 * before calling poll() which may wait for a long time. */
void gc_exit(void);

/* Clean up all the data which has been freed in the oldest gc round that has been already exited.
 * Returns 1 on success (there was something to clean), 0 when there is no gc round available to cleanup.
 * It is recommended to run this outside any gc round. */
_Bool gc_cleanup(void);

/* GC callback type */
struct gc_callback_set {
  void (*enter)(u64, struct gc_callback_set *);
  void (*exit)(u64, struct gc_callback_set *);
  void (*cleanup)(u64, struct gc_callback_set *);
};

/* Register callbacks. These are called for each entered, exited and cleaned-up round.
 * The caller must keep the structure. */
void gc_register(struct gc_callback_set *);
void gc_unregister(struct gc_callback_set *);

#endif
Loading