mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 17:51:53 +00:00
Garbage collector for parallel execution
This commit is contained in:
parent
2a0d6c32ac
commit
60267ddeca
@ -160,10 +160,11 @@ $(client) $(daemon):
|
|||||||
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||||
|
|
||||||
$(objdir)/sysdep/paths.h: Makefile
|
$(objdir)/sysdep/paths.h: Makefile
|
||||||
echo >$@ "/* Generated by Makefile, don't edit manually! */"
|
$(E)echo GEN $@
|
||||||
echo >>$@ "#define PATH_CONFIG_FILE \"@CONFIG_FILE@\""
|
$(Q)echo >$@ "/* Generated by Makefile, don't edit manually! */"
|
||||||
echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\""
|
$(Q)echo >>$@ "#define PATH_CONFIG_FILE \"@CONFIG_FILE@\""
|
||||||
if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi
|
$(Q)echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\""
|
||||||
|
$(Q)if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi
|
||||||
|
|
||||||
# Unit tests rules
|
# Unit tests rules
|
||||||
|
|
||||||
|
@ -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)
|
obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#ifndef _BIRD_BIRDLIB_H_
|
#ifndef _BIRD_BIRDLIB_H_
|
||||||
#define _BIRD_BIRDLIB_H_
|
#define _BIRD_BIRDLIB_H_
|
||||||
|
|
||||||
|
#include "sysdep/config.h"
|
||||||
#include "lib/alloca.h"
|
#include "lib/alloca.h"
|
||||||
|
|
||||||
/* Ugly structure offset handling macros */
|
/* Ugly structure offset handling macros */
|
||||||
|
187
lib/gc.c
Normal file
187
lib/gc.c
Normal file
@ -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);
|
||||||
|
}
|
42
lib/gc.h
Normal file
42
lib/gc.h
Normal file
@ -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
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#define _BIRD_LISTS_C_
|
#define _BIRD_LISTS_C_
|
||||||
|
|
||||||
#include "nest/bird.h"
|
#include "lib/birdlib.h"
|
||||||
#include "lib/lists.h"
|
#include "lib/lists.h"
|
||||||
|
|
||||||
LIST_INLINE int
|
LIST_INLINE int
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#ifndef _BIRD_BIRD_H_
|
#ifndef _BIRD_BIRD_H_
|
||||||
#define _BIRD_BIRD_H_
|
#define _BIRD_BIRD_H_
|
||||||
|
|
||||||
#include "sysdep/config.h"
|
|
||||||
#include "lib/birdlib.h"
|
#include "lib/birdlib.h"
|
||||||
#include "lib/ip.h"
|
#include "lib/ip.h"
|
||||||
#include "lib/net.h"
|
#include "lib/net.h"
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "proto/bfd/io.h"
|
#include "proto/bfd/io.h"
|
||||||
|
|
||||||
#include "lib/buffer.h"
|
#include "lib/buffer.h"
|
||||||
|
#include "lib/gc.h"
|
||||||
#include "lib/lists.h"
|
#include "lib/lists.h"
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
#include "lib/event.h"
|
#include "lib/event.h"
|
||||||
@ -482,6 +483,7 @@ birdloop_main(void *arg)
|
|||||||
|
|
||||||
birdloop_set_current(loop);
|
birdloop_set_current(loop);
|
||||||
|
|
||||||
|
gc_enter();
|
||||||
pthread_mutex_lock(&loop->mutex);
|
pthread_mutex_lock(&loop->mutex);
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@ -501,6 +503,7 @@ birdloop_main(void *arg)
|
|||||||
|
|
||||||
loop->poll_active = 1;
|
loop->poll_active = 1;
|
||||||
pthread_mutex_unlock(&loop->mutex);
|
pthread_mutex_unlock(&loop->mutex);
|
||||||
|
gc_exit();
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
|
rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
|
||||||
@ -511,6 +514,7 @@ birdloop_main(void *arg)
|
|||||||
die("poll: %m");
|
die("poll: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gc_enter();
|
||||||
pthread_mutex_lock(&loop->mutex);
|
pthread_mutex_lock(&loop->mutex);
|
||||||
loop->poll_active = 0;
|
loop->poll_active = 0;
|
||||||
|
|
||||||
@ -528,6 +532,7 @@ birdloop_main(void *arg)
|
|||||||
|
|
||||||
loop->stop_called = 0;
|
loop->stop_called = 0;
|
||||||
pthread_mutex_unlock(&loop->mutex);
|
pthread_mutex_unlock(&loop->mutex);
|
||||||
|
gc_exit();
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include <netinet/icmp6.h>
|
#include <netinet/icmp6.h>
|
||||||
|
|
||||||
#include "nest/bird.h"
|
#include "nest/bird.h"
|
||||||
|
#include "lib/gc.h"
|
||||||
#include "lib/lists.h"
|
#include "lib/lists.h"
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
#include "lib/socket.h"
|
#include "lib/socket.h"
|
||||||
@ -2258,11 +2259,19 @@ io_loop(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gc_exit();
|
||||||
|
|
||||||
|
/* Run the garbage collector before waiting for new data */
|
||||||
|
while (gc_cleanup())
|
||||||
|
;
|
||||||
|
|
||||||
/* And finally enter poll() to find active sockets */
|
/* And finally enter poll() to find active sockets */
|
||||||
watchdog_stop();
|
watchdog_stop();
|
||||||
pout = poll(pfd, nfds, poll_tout);
|
pout = poll(pfd, nfds, poll_tout);
|
||||||
watchdog_start();
|
watchdog_start();
|
||||||
|
|
||||||
|
gc_enter();
|
||||||
|
|
||||||
if (pout < 0)
|
if (pout < 0)
|
||||||
{
|
{
|
||||||
if (errno == EINTR || errno == EAGAIN)
|
if (errno == EINTR || errno == EAGAIN)
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "nest/bird.h"
|
#include "nest/bird.h"
|
||||||
|
#include "lib/gc.h"
|
||||||
#include "lib/lists.h"
|
#include "lib/lists.h"
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
#include "lib/socket.h"
|
#include "lib/socket.h"
|
||||||
@ -584,6 +585,13 @@ async_shutdown(void)
|
|||||||
void
|
void
|
||||||
sysdep_shutdown_done(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_pid_file();
|
||||||
unlink(path_control_socket);
|
unlink(path_control_socket);
|
||||||
log_msg(L_FATAL "Shutdown completed");
|
log_msg(L_FATAL "Shutdown completed");
|
||||||
@ -909,6 +917,8 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
signal_init();
|
signal_init();
|
||||||
|
|
||||||
|
gc_enter();
|
||||||
|
|
||||||
config_commit(conf, RECONFIG_HARD, 0);
|
config_commit(conf, RECONFIG_HARD, 0);
|
||||||
|
|
||||||
graceful_restart_init();
|
graceful_restart_init();
|
||||||
|
Loading…
Reference in New Issue
Block a user