From 9f6ac5d71de191146c9958725b16e19876cb84dc Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Wed, 8 Feb 2012 15:47:22 +0100 Subject: [PATCH] add atfork interface * libguile/posix.h: * libguile/posix.c (scm_c_atfork): New interface. (scm_fork): Wrap fork calls in atfork pre/post invocations, and finally a GC_call_with_alloc_lock. It's an attempt to grab the interesting mutexes in Guile. * libguile/weak-set.c (make_weak_set): * libguile/weak-table.c (make_weak_table): Use atfork mechanism to lock and unlock weak sets and weak tables during a fork(), in such a way that does not prevent those tables from being gc'd. --- libguile/init.c | 1 + libguile/posix.c | 67 ++++++++++++++++++++++++++- libguile/posix.h | 8 +++- libguile/weak-set.c | 102 ++++++++++++++++++++++++++++++++++++------ libguile/weak-set.h | 5 ++- libguile/weak-table.c | 101 +++++++++++++++++++++++++++++++++++------ 6 files changed, 253 insertions(+), 31 deletions(-) diff --git a/libguile/init.c b/libguile/init.c index 3509ba315..35fbedb8b 100644 --- a/libguile/init.c +++ b/libguile/init.c @@ -383,6 +383,7 @@ scm_i_init_guile (void *base) scm_storage_prehistory (); scm_threads_prehistory (base); /* requires storage_prehistory */ + scm_weak_set_prehistory (); /* requires storage_prehistory */ scm_weak_table_prehistory (); /* requires storage_prehistory */ #ifdef GUILE_DEBUG_MALLOC scm_debug_malloc_prehistory (); diff --git a/libguile/posix.c b/libguile/posix.c index 154d26ad9..f721540c9 100644 --- a/libguile/posix.c +++ b/libguile/posix.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2003, 2004, 2005, 2006, 2007, 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 @@ -1238,6 +1238,65 @@ SCM_DEFINE (scm_execle, "execle", 2, 0, 1, #undef FUNC_NAME #ifdef HAVE_FORK +struct scm_t_atfork_entry { + struct scm_t_atfork_entry *next; + struct scm_t_atfork_entry *prev; + scm_t_atfork_callback pre; + scm_t_atfork_callback post; + void *data; +}; + +static scm_i_pthread_mutex_t atfork_lock = SCM_I_PTHREAD_MUTEX_INITIALIZER; +static struct scm_t_atfork_entry *atfork_entries; + +void +scm_c_atfork (scm_t_atfork_callback pre, scm_t_atfork_callback post, void *data) +{ + struct scm_t_atfork_entry *new_entry; + + new_entry = scm_gc_malloc (sizeof (*new_entry), "atfork entry"); + + scm_i_pthread_mutex_lock (&atfork_lock); + new_entry->next = atfork_entries; + new_entry->prev = NULL; + if (atfork_entries) + atfork_entries->prev = new_entry; + new_entry->pre = pre; + new_entry->post = post; + new_entry->data = data; + atfork_entries = new_entry; + scm_i_pthread_mutex_unlock (&atfork_lock); +} + +static void +before_fork (void) +{ + struct scm_t_atfork_entry *ent; + + scm_i_pthread_mutex_lock (&atfork_lock); + for (ent = atfork_entries; ent; ent = ent->next) + ent->pre (ent->data); +} + +static void +after_fork (void) +{ + struct scm_t_atfork_entry *ent; + + for (ent = atfork_entries; ent && ent->next; ent = ent->next); + for (; ent; ent = ent->prev) + ent->post (ent->data); + scm_i_pthread_mutex_unlock (&atfork_lock); +} + +static void* +do_fork (void *data) +{ + int *pid = data; + *pid = fork(); + return NULL; +} + SCM_DEFINE (scm_fork, "primitive-fork", 0, 0, 0, (), "Creates a new \"child\" process by duplicating the current \"parent\" process.\n" @@ -1248,7 +1307,11 @@ SCM_DEFINE (scm_fork, "primitive-fork", 0, 0, 0, #define FUNC_NAME s_scm_fork { int pid; - pid = fork (); + + before_fork (); + GC_call_with_alloc_lock (do_fork, &pid); + after_fork (); + if (pid == -1) SCM_SYSERROR; return scm_from_int (pid); diff --git a/libguile/posix.h b/libguile/posix.h index 92f8b3514..e0cc0ed69 100644 --- a/libguile/posix.h +++ b/libguile/posix.h @@ -4,7 +4,7 @@ #define SCM_POSIX_H /* Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2003, 2006, 2008, - * 2009, 2010, 2011 Free Software Foundation, Inc. + * 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 @@ -64,7 +64,13 @@ SCM_API SCM scm_ttyname (SCM port); SCM_API SCM scm_execl (SCM filename, SCM args); SCM_API SCM scm_execlp (SCM filename, SCM args); SCM_API SCM scm_execle (SCM filename, SCM env, SCM args); + +typedef void (*scm_t_atfork_callback) (void *data); +SCM_API void scm_c_atfork (scm_t_atfork_callback pre, + scm_t_atfork_callback post, + void *data); SCM_API SCM scm_fork (void); + SCM_API SCM scm_uname (void); SCM_API SCM scm_environ (SCM env); SCM_API SCM scm_tmpnam (void); diff --git a/libguile/weak-set.c b/libguile/weak-set.c index 004fedb82..0660322ec 100644 --- a/libguile/weak-set.c +++ b/libguile/weak-set.c @@ -614,6 +614,70 @@ weak_set_remove_x (scm_t_weak_set *set, unsigned long hash, + +static void +lock_weak_set (scm_t_weak_set *set) +{ + scm_i_pthread_mutex_lock (&set->lock); +} + +static void +unlock_weak_set (scm_t_weak_set *set) +{ + scm_i_pthread_mutex_unlock (&set->lock); +} + +/* A weak set of weak sets, for use in the pthread_atfork handler. */ +static SCM all_weak_sets = SCM_BOOL_F; + +static void +lock_all_weak_sets (void *unused) +{ + scm_t_weak_set *s; + scm_t_weak_entry *entries; + unsigned long k, size; + scm_t_weak_entry copy; + + s = SCM_WEAK_SET (all_weak_sets); + lock_weak_set (s); + size = s->size; + entries = s->entries; + + for (k = 0; k < size; k++) + if (entries[k].hash) + { + copy_weak_entry (&entries[k], ©); + if (copy.key) + lock_weak_set (SCM_WEAK_SET (SCM_PACK (copy.key))); + } +} + +static void +unlock_all_weak_sets (void *unused) +{ + scm_t_weak_set *s; + scm_t_weak_entry *entries; + unsigned long k, size; + scm_t_weak_entry copy; + + s = SCM_WEAK_SET (all_weak_sets); + size = s->size; + entries = s->entries; + + for (k = 0; k < size; k++) + if (entries[k].hash) + { + copy_weak_entry (&entries[k], ©); + if (copy.key) + unlock_weak_set (SCM_WEAK_SET (SCM_PACK (copy.key))); + } + + unlock_weak_set (s); +} + + + + static SCM make_weak_set (unsigned long k) { @@ -660,7 +724,7 @@ do_vacuum_weak_set (SCM set) if (scm_i_pthread_mutex_trylock (&s->lock) == 0) { vacuum_weak_set (s); - scm_i_pthread_mutex_unlock (&s->lock); + unlock_weak_set (s); } return; @@ -725,6 +789,9 @@ scm_c_make_weak_set (unsigned long k) scm_c_register_weak_gc_callback (ret, do_vacuum_weak_set); + if (scm_is_true (all_weak_sets)) + scm_weak_set_add_x (all_weak_sets, ret); + return ret; } @@ -739,12 +806,12 @@ scm_weak_set_clear_x (SCM set) { scm_t_weak_set *s = SCM_WEAK_SET (set); - scm_i_pthread_mutex_lock (&s->lock); + lock_weak_set (s); memset (s->entries, 0, sizeof (scm_t_weak_entry) * s->size); s->n_items = 0; - scm_i_pthread_mutex_unlock (&s->lock); + unlock_weak_set (s); return SCM_UNSPECIFIED; } @@ -757,11 +824,11 @@ scm_c_weak_set_lookup (SCM set, unsigned long raw_hash, SCM ret; scm_t_weak_set *s = SCM_WEAK_SET (set); - scm_i_pthread_mutex_lock (&s->lock); + lock_weak_set (s); ret = weak_set_lookup (s, raw_hash, pred, closure, dflt); - scm_i_pthread_mutex_unlock (&s->lock); + unlock_weak_set (s); return ret; } @@ -774,11 +841,11 @@ scm_c_weak_set_add_x (SCM set, unsigned long raw_hash, SCM ret; scm_t_weak_set *s = SCM_WEAK_SET (set); - scm_i_pthread_mutex_lock (&s->lock); + lock_weak_set (s); ret = weak_set_add_x (s, raw_hash, pred, closure, obj); - scm_i_pthread_mutex_unlock (&s->lock); + unlock_weak_set (s); return ret; } @@ -790,11 +857,11 @@ scm_c_weak_set_remove_x (SCM set, unsigned long raw_hash, { scm_t_weak_set *s = SCM_WEAK_SET (set); - scm_i_pthread_mutex_lock (&s->lock); + lock_weak_set (s); weak_set_remove_x (s, raw_hash, pred, closure); - scm_i_pthread_mutex_unlock (&s->lock); + unlock_weak_set (s); } static int @@ -829,7 +896,7 @@ scm_c_weak_set_fold (scm_t_set_fold_fn proc, void *closure, s = SCM_WEAK_SET (set); - scm_i_pthread_mutex_lock (&s->lock); + lock_weak_set (s); size = s->size; entries = s->entries; @@ -845,14 +912,14 @@ scm_c_weak_set_fold (scm_t_set_fold_fn proc, void *closure, if (copy.key) { /* Release set lock while we call the function. */ - scm_i_pthread_mutex_unlock (&s->lock); + unlock_weak_set (s); init = proc (closure, SCM_PACK (copy.key), init); - scm_i_pthread_mutex_lock (&s->lock); + lock_weak_set (s); } } } - scm_i_pthread_mutex_unlock (&s->lock); + unlock_weak_set (s); return init; } @@ -897,6 +964,15 @@ scm_weak_set_map_to_list (SCM proc, SCM set) } + + +void +scm_weak_set_prehistory (void) +{ + all_weak_sets = scm_c_make_weak_set (0); + scm_c_atfork (lock_all_weak_sets, unlock_all_weak_sets, NULL); +} + void scm_init_weak_set () { diff --git a/libguile/weak-set.h b/libguile/weak-set.h index 86781c78a..6a1c00d78 100644 --- a/libguile/weak-set.h +++ b/libguile/weak-set.h @@ -3,7 +3,7 @@ #ifndef SCM_WEAK_SET_H #define SCM_WEAK_SET_H -/* Copyright (C) 2011 Free Software Foundation, Inc. +/* Copyright (C) 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 @@ -57,7 +57,10 @@ SCM_INTERNAL SCM scm_weak_set_fold (SCM proc, SCM init, SCM set); SCM_INTERNAL SCM scm_weak_set_for_each (SCM proc, SCM set); SCM_INTERNAL SCM scm_weak_set_map_to_list (SCM proc, SCM set); +SCM_INTERNAL void scm_i_weak_set_lock (SCM set); +SCM_INTERNAL void scm_i_weak_set_unlock (SCM set); SCM_INTERNAL void scm_i_weak_set_print (SCM exp, SCM port, scm_print_state *pstate); +SCM_INTERNAL void scm_weak_set_prehistory (void); SCM_INTERNAL void scm_init_weak_set (void); #endif /* SCM_WEAK_SET_H */ diff --git a/libguile/weak-table.c b/libguile/weak-table.c index 38fd23d30..e2acdcad1 100644 --- a/libguile/weak-table.c +++ b/libguile/weak-table.c @@ -736,10 +736,75 @@ weak_table_remove_x (scm_t_weak_table *table, unsigned long hash, + +static void +lock_weak_table (scm_t_weak_table *table) +{ + scm_i_pthread_mutex_lock (&table->lock); +} + +static void +unlock_weak_table (scm_t_weak_table *table) +{ + scm_i_pthread_mutex_unlock (&table->lock); +} + +/* A weak table of weak tables, for use in the pthread_atfork handler. */ +static SCM all_weak_tables = SCM_BOOL_F; + +static void +lock_all_weak_tables (void *unused) +{ + scm_t_weak_table *s; + scm_t_weak_entry *entries; + unsigned long k, size; + scm_t_weak_entry copy; + + s = SCM_WEAK_TABLE (all_weak_tables); + lock_weak_table (s); + size = s->size; + entries = s->entries; + + for (k = 0; k < size; k++) + if (entries[k].hash) + { + copy_weak_entry (&entries[k], ©); + if (copy.key) + lock_weak_table (SCM_WEAK_TABLE (SCM_PACK (copy.key))); + } +} + +static void +unlock_all_weak_tables (void *unused) +{ + scm_t_weak_table *s; + scm_t_weak_entry *entries; + unsigned long k, size; + scm_t_weak_entry copy; + + s = SCM_WEAK_TABLE (all_weak_tables); + size = s->size; + entries = s->entries; + + for (k = 0; k < size; k++) + if (entries[k].hash) + { + copy_weak_entry (&entries[k], ©); + if (copy.key) + unlock_weak_table (SCM_WEAK_TABLE (SCM_PACK (copy.key))); + } + + unlock_weak_table (s); +} + + + + static SCM make_weak_table (unsigned long k, scm_t_weak_table_kind kind) { scm_t_weak_table *table; + SCM ret; int i = 0, n = k ? k : 31; while (i + 1 < HASHTABLE_SIZE_N && n > hashtable_size[i]) @@ -757,7 +822,12 @@ make_weak_table (unsigned long k, scm_t_weak_table_kind kind) table->min_size_index = i; scm_i_pthread_mutex_init (&table->lock, NULL); - return scm_cell (scm_tc7_weak_table, (scm_t_bits)table); + ret = scm_cell (scm_tc7_weak_table, (scm_t_bits)table); + + if (scm_is_true (all_weak_tables)) + scm_weak_table_putq_x (all_weak_tables, ret, SCM_BOOL_T); + + return ret; } void @@ -781,7 +851,7 @@ do_vacuum_weak_table (SCM table) if (scm_i_pthread_mutex_trylock (&t->lock) == 0) { vacuum_weak_table (t); - scm_i_pthread_mutex_unlock (&t->lock); + unlock_weak_table (t); } return; @@ -868,11 +938,11 @@ scm_c_weak_table_ref (SCM table, unsigned long raw_hash, t = SCM_WEAK_TABLE (table); - scm_i_pthread_mutex_lock (&t->lock); + lock_weak_table (t); ret = weak_table_ref (t, raw_hash, pred, closure, dflt); - scm_i_pthread_mutex_unlock (&t->lock); + unlock_weak_table (t); return ret; } @@ -890,11 +960,11 @@ scm_c_weak_table_put_x (SCM table, unsigned long raw_hash, t = SCM_WEAK_TABLE (table); - scm_i_pthread_mutex_lock (&t->lock); + lock_weak_table (t); weak_table_put_x (t, raw_hash, pred, closure, key, value); - scm_i_pthread_mutex_unlock (&t->lock); + unlock_weak_table (t); } #undef FUNC_NAME @@ -910,11 +980,11 @@ scm_c_weak_table_remove_x (SCM table, unsigned long raw_hash, t = SCM_WEAK_TABLE (table); - scm_i_pthread_mutex_lock (&t->lock); + lock_weak_table (t); weak_table_remove_x (t, raw_hash, pred, closure); - scm_i_pthread_mutex_unlock (&t->lock); + unlock_weak_table (t); } #undef FUNC_NAME @@ -962,12 +1032,12 @@ scm_weak_table_clear_x (SCM table) t = SCM_WEAK_TABLE (table); - scm_i_pthread_mutex_lock (&t->lock); + lock_weak_table (t); memset (t->entries, 0, sizeof (scm_t_weak_entry) * t->size); t->n_items = 0; - scm_i_pthread_mutex_unlock (&t->lock); + unlock_weak_table (t); return SCM_UNSPECIFIED; } @@ -983,7 +1053,7 @@ scm_c_weak_table_fold (scm_t_table_fold_fn proc, void *closure, t = SCM_WEAK_TABLE (table); - scm_i_pthread_mutex_lock (&t->lock); + lock_weak_table (t); size = t->size; entries = t->entries; @@ -999,16 +1069,16 @@ scm_c_weak_table_fold (scm_t_table_fold_fn proc, void *closure, if (copy.key && copy.value) { /* Release table lock while we call the function. */ - scm_i_pthread_mutex_unlock (&t->lock); + unlock_weak_table (t); init = proc (closure, SCM_PACK (copy.key), SCM_PACK (copy.value), init); - scm_i_pthread_mutex_lock (&t->lock); + lock_weak_table (t); } } } - scm_i_pthread_mutex_unlock (&t->lock); + unlock_weak_table (t); return init; } @@ -1163,6 +1233,9 @@ scm_weak_table_prehistory (void) GC_new_kind (GC_new_free_list (), GC_MAKE_PROC (GC_new_proc (mark_weak_value_table), 0), 0, 0); + + all_weak_tables = scm_c_make_weak_table (0, SCM_WEAK_TABLE_KIND_KEY); + scm_c_atfork (lock_all_weak_tables, unlock_all_weak_tables, NULL); } void