summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2012-02-24 13:18:48 +0100
committerAndy Wingo <wingo@pobox.com>2012-03-08 13:12:10 +0100
commitf740445a9b5bf0a5e5090f0a2ddaffb2b803bab7 (patch)
tree0b52f7545433314fd41e83fcca8db5108fdfd87a
parent75ba64d6797f5857cc9885eb753126119a8c8b68 (diff)
downloadguile-f740445a9b5bf0a5e5090f0a2ddaffb2b803bab7.tar.gz
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.
-rw-r--r--configure.ac2
-rw-r--r--libguile/finalizers.c56
-rw-r--r--libguile/gc.c6
-rw-r--r--libguile/init.c2
-rw-r--r--libguile/threads.c3
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 ();
}