]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Garbage collector for parallel execution
authorMaria Matejka <mq@jmq.cz>
Wed, 22 Apr 2020 21:09:19 +0000 (23:09 +0200)
committerMaria Matejka <mq@ucw.cz>
Thu, 30 Apr 2020 15:38:30 +0000 (17:38 +0200)
Makefile.in
lib/Makefile
lib/birdlib.h
lib/gc.c [new file with mode: 0644]
lib/gc.h [new file with mode: 0644]
lib/lists.c
nest/bird.h
proto/bfd/io.c
sysdep/unix/io.c
sysdep/unix/main.c

index da6cd206a5c48d8fd1d01e561cc085f9b5f89a42..e6f50b4c13946cb638d8da376dc63b52e214fca4 100644 (file)
@@ -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
 
index 5c78b2a9b01344e6a092e2e647aa6bf6775818d3..00db0c679bbe07708f6f33abae564d8684f7b3ba 100644 (file)
@@ -1,4 +1,4 @@
-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)
 
index e18107e90d3154b60ae64edcd3c36c5a1c766cfd..77044443331c8a4e4a4103018eee74d67bffa77a 100644 (file)
@@ -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 */
diff --git a/lib/gc.c b/lib/gc.c
new file mode 100644 (file)
index 0000000..49f65f5
--- /dev/null
+++ b/lib/gc.c
@@ -0,0 +1,187 @@
+/*
+ *     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);
+}
diff --git a/lib/gc.h b/lib/gc.h
new file mode 100644 (file)
index 0000000..a4ffec0
--- /dev/null
+++ b/lib/gc.h
@@ -0,0 +1,42 @@
+/*
+ *     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
index 200576cf659c18153c9e7cc270943d00dc9138c2..7e24291a0c3c41bb114de7c971b2c9462ee26ea1 100644 (file)
@@ -26,7 +26,7 @@
 
 #define _BIRD_LISTS_C_
 
-#include "nest/bird.h"
+#include "lib/birdlib.h"
 #include "lib/lists.h"
 
 LIST_INLINE int
index 55712abe82450c0d050846168f6f6a7a144bc976..931974a0e8f84a8bfe6422b1526656d871053f42 100644 (file)
@@ -9,7 +9,6 @@
 #ifndef _BIRD_BIRD_H_
 #define _BIRD_BIRD_H_
 
-#include "sysdep/config.h"
 #include "lib/birdlib.h"
 #include "lib/ip.h"
 #include "lib/net.h"
index 1cd9365adb4df763c6665aae9134cbc19c24bc98..5d28d01f59bc7eda6642bebda0830b5acde0ac8c 100644 (file)
@@ -18,6 +18,7 @@
 #include "proto/bfd/io.h"
 
 #include "lib/buffer.h"
+#include "lib/gc.h"
 #include "lib/lists.h"
 #include "lib/resource.h"
 #include "lib/event.h"
@@ -482,6 +483,7 @@ birdloop_main(void *arg)
 
   birdloop_set_current(loop);
 
+  gc_enter();
   pthread_mutex_lock(&loop->mutex);
   while (1)
   {
@@ -501,6 +503,7 @@ birdloop_main(void *arg)
 
     loop->poll_active = 1;
     pthread_mutex_unlock(&loop->mutex);
+    gc_exit();
 
   try:
     rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
@@ -511,6 +514,7 @@ birdloop_main(void *arg)
       die("poll: %m");
     }
 
+    gc_enter();
     pthread_mutex_lock(&loop->mutex);
     loop->poll_active = 0;
 
@@ -528,6 +532,7 @@ birdloop_main(void *arg)
 
   loop->stop_called = 0;
   pthread_mutex_unlock(&loop->mutex);
+  gc_exit();
 
   return NULL;
 }
index 9d54a2c3574b77a70647e5ccc8aa3ab58b1d075f..b5696ac44d362af6ee89f3f060300fe094eb4f04 100644 (file)
@@ -32,6 +32,7 @@
 #include <netinet/icmp6.h>
 
 #include "nest/bird.h"
+#include "lib/gc.h"
 #include "lib/lists.h"
 #include "lib/resource.h"
 #include "lib/socket.h"
@@ -2258,11 +2259,19 @@ io_loop(void)
          continue;
        }
 
+      gc_exit();
+
+      /* Run the garbage collector before waiting for new data */
+      while (gc_cleanup())
+       ;
+
       /* And finally enter poll() to find active sockets */
       watchdog_stop();
       pout = poll(pfd, nfds, poll_tout);
       watchdog_start();
 
+      gc_enter();
+
       if (pout < 0)
        {
          if (errno == EINTR || errno == EAGAIN)
index 1d258f4c7a81e6d549b9bd752e21997d8b0f490e..7bc2b25d279c201c9907065358ffe36ad3da7fa8 100644 (file)
@@ -23,6 +23,7 @@
 #include <libgen.h>
 
 #include "nest/bird.h"
+#include "lib/gc.h"
 #include "lib/lists.h"
 #include "lib/resource.h"
 #include "lib/socket.h"
@@ -584,6 +585,13 @@ async_shutdown(void)
 void
 sysdep_shutdown_done(void)
 {
+#if 0
+  /* Garbage collector needn't be run on exit */
+  gc_exit();
+  while (gc_cleanup())
+    ;
+#endif
+
   unlink_pid_file();
   unlink(path_control_socket);
   log_msg(L_FATAL "Shutdown completed");
@@ -909,6 +917,8 @@ main(int argc, char **argv)
 
   signal_init();
 
+  gc_enter();
+
   config_commit(conf, RECONFIG_HARD, 0);
 
   graceful_restart_init();