From: Maria Matejka Date: Wed, 22 Apr 2020 21:09:19 +0000 (+0200) Subject: Garbage collector for parallel execution X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60267ddeca067de3502c0cf967edbeccad8afb42;p=thirdparty%2Fbird.git Garbage collector for parallel execution --- diff --git a/Makefile.in b/Makefile.in index da6cd206a..e6f50b4c1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/lib/Makefile b/lib/Makefile index 5c78b2a9b..00db0c679 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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) diff --git a/lib/birdlib.h b/lib/birdlib.h index e18107e90..770444433 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -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 index 000000000..49f65f522 --- /dev/null +++ b/lib/gc.c @@ -0,0 +1,187 @@ +/* + * BIRD Library -- Garbage Collector + * + * (c) 2020 Maria Matejka + * (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 +#include +#include + +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; ienter, 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; iexit, 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; icleanup, 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 + * (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 diff --git a/lib/lists.c b/lib/lists.c index 200576cf6..7e24291a0 100644 --- a/lib/lists.c +++ b/lib/lists.c @@ -26,7 +26,7 @@ #define _BIRD_LISTS_C_ -#include "nest/bird.h" +#include "lib/birdlib.h" #include "lib/lists.h" LIST_INLINE int diff --git a/nest/bird.h b/nest/bird.h index 55712abe8..931974a0e 100644 --- a/nest/bird.h +++ b/nest/bird.h @@ -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" diff --git a/proto/bfd/io.c b/proto/bfd/io.c index 1cd9365ad..5d28d01f5 100644 --- a/proto/bfd/io.c +++ b/proto/bfd/io.c @@ -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; } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 9d54a2c35..b5696ac44 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -32,6 +32,7 @@ #include #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) diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 1d258f4c7..7bc2b25d2 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -23,6 +23,7 @@ #include #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();