mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
Fix finalizer resuscitation causing excessive GC
* libguile/finalizers.c (async_gc_finalizer): (scm_i_register_async_gc_callback): Replace "weak gc callback" mechanism with "async gc callback" mechanism. Very similar but the new API is designed to be called a bounded number of times, to avoid running afoul of libgc heuristics. * libguile/weak-list.h: New internal header. * libguile/Makefile.am (noinst_HEADERS): Add weak-list.h. * libguile/weak-set.c (vacuum_all_weak_sets): (scm_c_make_weak_set, scm_init_weak_set): * libguile/weak-table.c (vacuum_all_weak_tables): (scm_c_make_weak_table, scm_init_weak_table): Arrange to vacuum all weak sets from a single async GC callback, and likewise for weak tables. Thanks to Ludovic Courtès for tracking this bug down!
This commit is contained in:
parent
e337432041
commit
c9910c6042
6 changed files with 137 additions and 43 deletions
|
@ -296,59 +296,46 @@ scm_i_finalizer_pre_fork (void)
|
|||
|
||||
|
||||
|
||||
static void*
|
||||
weak_pointer_ref (void *weak_pointer)
|
||||
{
|
||||
return *(void **) weak_pointer;
|
||||
}
|
||||
|
||||
static void
|
||||
weak_gc_finalizer (void *ptr, void *data)
|
||||
async_gc_finalizer (void *ptr, void *data)
|
||||
{
|
||||
void **weak = ptr;
|
||||
void *val;
|
||||
void (*callback) (SCM) = weak[1];
|
||||
void **obj = ptr;
|
||||
void (*callback) (void) = obj[0];
|
||||
|
||||
val = GC_call_with_alloc_lock (weak_pointer_ref, &weak[0]);
|
||||
callback ();
|
||||
|
||||
if (!val)
|
||||
return;
|
||||
|
||||
callback (SCM_PACK_POINTER (val));
|
||||
|
||||
scm_i_set_finalizer (ptr, weak_gc_finalizer, data);
|
||||
scm_i_set_finalizer (ptr, async_gc_finalizer, data);
|
||||
}
|
||||
|
||||
/* CALLBACK will be called on OBJ, as long as OBJ is accessible. It
|
||||
will be called from a finalizer, which may be from an async or from
|
||||
/* Arrange to call CALLBACK asynchronously after each GC. The callback
|
||||
will be invoked from a finalizer, which may be from an async or from
|
||||
another thread.
|
||||
|
||||
As an implementation detail, the way this works is that we allocate
|
||||
a fresh pointer-less object holding two words. We know that this
|
||||
As an implementation detail, the way this works is that we allocate a
|
||||
fresh object and put the callback in the object. We know that this
|
||||
object should get collected the next time GC is run, so we attach a
|
||||
finalizer to it so that we get a callback after GC happens.
|
||||
finalizer to it to trigger the callback.
|
||||
|
||||
The first word of the object holds a weak reference to OBJ, and the
|
||||
second holds the callback pointer. When the callback is called, we
|
||||
check if the weak reference on OBJ still holds. If it doesn't hold,
|
||||
then OBJ is no longer accessible, and we're done. Otherwise we call
|
||||
the callback and re-register a finalizer for our two-word GC object,
|
||||
effectively resuscitating the object so that we will get a callback
|
||||
on the next GC.
|
||||
Once the callback runs, we re-attach a finalizer to that fresh object
|
||||
to prepare for the next GC, and the process repeats indefinitely.
|
||||
|
||||
We could use the scm_after_gc_hook, but using a finalizer has the
|
||||
advantage of potentially running in another thread, decreasing pause
|
||||
time. */
|
||||
time.
|
||||
|
||||
Note that libgc currently has a heuristic that adding 500 finalizable
|
||||
objects will cause GC to collect rather than expand the heap,
|
||||
drastically reducing performance on workloads that actually need to
|
||||
expand the heap. Therefore scm_i_register_async_gc_callback is
|
||||
inappropriate for using on unbounded numbers of callbacks. */
|
||||
void
|
||||
scm_i_register_weak_gc_callback (SCM obj, void (*callback) (SCM))
|
||||
scm_i_register_async_gc_callback (void (*callback) (void))
|
||||
{
|
||||
void **weak = GC_MALLOC_ATOMIC (sizeof (void*) * 2);
|
||||
void **obj = GC_MALLOC_ATOMIC (sizeof (void*));
|
||||
|
||||
weak[0] = SCM_UNPACK_POINTER (obj);
|
||||
weak[1] = (void*)callback;
|
||||
GC_GENERAL_REGISTER_DISAPPEARING_LINK (weak, SCM2PTR (obj));
|
||||
obj[0] = (void*)callback;
|
||||
|
||||
scm_i_set_finalizer (weak, weak_gc_finalizer, NULL);
|
||||
scm_i_set_finalizer (obj, async_gc_finalizer, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue