0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-03 15:41:54 +00:00
bird/lib/gc.c
2020-04-30 17:38:30 +02:00

188 lines
4.4 KiB
C

/*
* 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);
}