From f740445a9b5bf0a5e5090f0a2ddaffb2b803bab7 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Fri, 24 Feb 2012 13:18:48 +0100 Subject: [PATCH] run finalizers asynchronously in asyncs * libguile/finalizers.c: New excitement! We'll be running finalizers asynchronously, from asyncs. This will make it safer to allocate while holding a mutex. (GC_set_finalizer_notifier): Add back-compat shim. * libguile/init.c (scm_i_init_guile): Init the async finalizer mechanism during boot. * libguile/gc.c (scm_storage_prehistory): Tell libgc we'll be finalizing on demand. (scm_gc): Explicitly run finalizers here. * libguile/threads.c (guilify_self_2): Run finalizers here if queue_finalizer_async happened to run during guilify_self_1. * configure.ac: Add check for GC_set_finalizer_notifier. --- configure.ac | 2 +- libguile/finalizers.c | 56 +++++++++++++++++++++++++++++++++++++++++++ libguile/gc.c | 6 ++++- libguile/init.c | 2 ++ libguile/threads.c | 3 +++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 66d735e31..c9a443b56 100644 --- a/configure.ac +++ b/configure.ac @@ -1232,7 +1232,7 @@ save_LIBS="$LIBS" LIBS="$BDW_GC_LIBS $LIBS" CFLAGS="$BDW_GC_CFLAGS $CFLAGS" -AC_CHECK_FUNCS([GC_do_blocking GC_call_with_gc_active GC_pthread_exit GC_pthread_cancel GC_allow_register_threads GC_pthread_sigmask GC_set_start_callback GC_get_heap_usage_safe GC_get_free_space_divisor GC_gcollect_and_unmap GC_get_unmapped_bytes]) +AC_CHECK_FUNCS([GC_do_blocking GC_call_with_gc_active GC_pthread_exit GC_pthread_cancel GC_allow_register_threads GC_pthread_sigmask GC_set_start_callback GC_get_heap_usage_safe GC_get_free_space_divisor GC_gcollect_and_unmap GC_get_unmapped_bytes GC_set_finalizer_notifier]) # Though the `GC_do_blocking ()' symbol is present in GC 7.1, it is not # declared, and has a different type (returning void instead of diff --git a/libguile/finalizers.c b/libguile/finalizers.c index 8b4178ffc..295f977b8 100644 --- a/libguile/finalizers.c +++ b/libguile/finalizers.c @@ -31,6 +31,11 @@ +static size_t finalization_count; + + + + void scm_i_set_finalizer (void *obj, scm_t_finalizer_proc proc, void *data) { @@ -117,10 +122,61 @@ scm_i_add_finalizer (void *obj, scm_t_finalizer_proc proc, void *data) shuffle_resuscitators_to_front (chained_data); } + + + +static SCM finalizer_async_cell; + +static SCM +run_finalizers_async_thunk (void) +{ + finalization_count += GC_invoke_finalizers (); + return SCM_UNSPECIFIED; +} + + +/* The function queue_finalizer_async is run by the GC when there are + * objects to finalize. It will enqueue an asynchronous call to + * GC_invoke_finalizers() at the next SCM_TICK in this thread. + */ +static void +queue_finalizer_async (void) +{ + scm_i_thread *t = SCM_I_CURRENT_THREAD; + static scm_i_pthread_mutex_t lock = SCM_I_PTHREAD_MUTEX_INITIALIZER; + + scm_i_pthread_mutex_lock (&lock); + /* If t is NULL, that could be because we're allocating in + threads.c:guilify_self_1. In that case, rely on the + GC_invoke_finalizers call there after the thread spins up. */ + if (t && scm_is_false (SCM_CDR (finalizer_async_cell))) + { + SCM_SETCDR (finalizer_async_cell, t->active_asyncs); + t->active_asyncs = finalizer_async_cell; + t->pending_asyncs = 1; + } + scm_i_pthread_mutex_unlock (&lock); +} + + +#ifndef HAVE_GC_SET_FINALIZER_NOTIFIER +static void +GC_set_finalizer_notifier (void (*notifier) (void)) +{ + GC_finalizer_notifier = notifier; +} +#endif void scm_init_finalizers (void) { + /* When the async is to run, the cdr of the pair gets set to the + asyncs queue of the current thread. */ + finalizer_async_cell = + scm_cons (scm_c_make_gsubr ("%run-finalizers", 0, 0, 0, + run_finalizers_async_thunk), + SCM_BOOL_F); + GC_set_finalizer_notifier (queue_finalizer_async); } diff --git a/libguile/gc.c b/libguile/gc.c index fd37046af..b1f160597 100644 --- a/libguile/gc.c +++ b/libguile/gc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2006, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2006, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -391,6 +391,9 @@ SCM_DEFINE (scm_gc, "gc", 0, 0, 0, #define FUNC_NAME s_scm_gc { scm_i_gc ("call"); + /* If you're calling scm_gc(), you probably want synchronous + finalization. */ + GC_invoke_finalizers (); return SCM_UNSPECIFIED; } #undef FUNC_NAME @@ -623,6 +626,7 @@ scm_storage_prehistory () minimum_free_space_divisor = free_space_divisor; target_free_space_divisor = free_space_divisor; GC_set_free_space_divisor (free_space_divisor); + GC_set_finalize_on_demand (1); GC_INIT (); diff --git a/libguile/init.c b/libguile/init.c index a08621182..17791e2c7 100644 --- a/libguile/init.c +++ b/libguile/init.c @@ -59,6 +59,7 @@ #include "libguile/expand.h" #include "libguile/feature.h" #include "libguile/filesys.h" +#include "libguile/finalizers.h" #include "libguile/fluids.h" #include "libguile/fports.h" #include "libguile/frames.h" @@ -423,6 +424,7 @@ scm_i_init_guile (void *base) scm_init_dynwind (); /* requires smob_prehistory */ scm_init_eq (); scm_init_error (); + scm_init_finalizers (); scm_init_fluids (); scm_init_control (); /* requires fluids */ scm_init_feature (); diff --git a/libguile/threads.c b/libguile/threads.c index 7944f4868..e8305b40d 100644 --- a/libguile/threads.c +++ b/libguile/threads.c @@ -620,6 +620,9 @@ guilify_self_2 (SCM parent) t->join_queue = make_queue (); t->block_asyncs = 0; + + /* See note in finalizers.c:queue_finalizer_async(). */ + GC_invoke_finalizers (); } -- 2.20.1