From 92d83c725e09dbc76acfedc1cf85a01f9f54452d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Fri, 9 May 2003 03:17:42 +0000 Subject: Update. 2003-05-08 Ulrich Drepper * malloc/thread-m.h: Remove special handling of thread_atfork if HAVE_register_atfork_malloc is defined. --- nptl/sysdeps/unix/sysv/linux/unregister-atfork.c | 112 +++++++++++++++-------- 1 file changed, 74 insertions(+), 38 deletions(-) (limited to 'nptl/sysdeps/unix/sysv/linux/unregister-atfork.c') diff --git a/nptl/sysdeps/unix/sysv/linux/unregister-atfork.c b/nptl/sysdeps/unix/sysv/linux/unregister-atfork.c index e9eb1918f6..ac4b3c5d4c 100644 --- a/nptl/sysdeps/unix/sysv/linux/unregister-atfork.c +++ b/nptl/sysdeps/unix/sysv/linux/unregister-atfork.c @@ -20,56 +20,92 @@ #include #include #include "fork.h" - - -/* Defined in libc_pthread_init.c. */ -extern struct fork_handler __pthread_child_handler attribute_hidden; -/* Three static memory blocks used when registering malloc. */ -static struct fork_handler malloc_prepare; -static struct fork_handler malloc_parent; -static struct fork_handler malloc_child; +#include void __unregister_atfork (dso_handle) void *dso_handle; { - /* Get the lock to not conflict with running forks. */ - lll_lock (__fork_lock); - - list_t *runp; - list_t *prevp; - - list_for_each_prev_safe (runp, prevp, &__fork_prepare_list) - if (list_entry (runp, struct fork_handler, list)->dso_handle == dso_handle) + /* Check whether there is any entry in the list which we have to + remove. It is likely that this is not the case so don't bother + getting the lock. + + We do not worry about other threads adding entries for this DSO + right this moment. If this happens this is a race and we can do + whatever we please. The program will crash anyway seen. */ + struct fork_handler *runp = __fork_handlers; + struct fork_handler *lastp = NULL; + + while (runp != NULL) + if (runp->dso_handle == dso_handle) + break; + else { - list_del (runp); - - struct fork_handler *p = list_entry (runp, struct fork_handler, list); - if (p != &malloc_prepare) - free (p); + lastp = runp; + runp = runp->next; } - list_for_each_prev_safe (runp, prevp, &__fork_parent_list) - if (list_entry (runp, struct fork_handler, list)->dso_handle == dso_handle) - { - list_del (runp); + if (runp == NULL) + /* Nothing to do. */ + return; - struct fork_handler *p = list_entry (runp, struct fork_handler, list); - if (p != &malloc_parent) - free (p); - } - - list_for_each_prev_safe (runp, prevp, &__fork_child_list) - if (list_entry (runp, struct fork_handler, list)->dso_handle == dso_handle) - { - list_del (runp); + /* Get the lock to not conflict with additions or deletions. Note + that there couldn't have been another thread deleting something. + The __unregister_atfork function is only called from the + dlclose() code which itself serializes the operations. */ + lll_lock (__fork_lock); - struct fork_handler *p = list_entry (runp, struct fork_handler, list); - if (p != &__pthread_child_handler && p != &malloc_child) - free (p); - } + /* We have to create a new list with all the entries we don't remove. */ + struct deleted_handler + { + struct fork_handler *handler; + struct deleted_handler *next; + } *deleted = NULL; + + /* Remove the entries for the DSO which is unloaded from the list. + It's a single linked list so readers are. */ + do + { + if (runp->dso_handle == dso_handle) + { + if (lastp == NULL) + __fork_handlers = runp->next; + else + lastp->next = runp->next; + + /* We cannot overwrite the ->next element now. Put the deleted + entries in a separate list. */ + struct deleted_handler *newp = alloca (sizeof (*newp)); + newp->handler = runp; + newp->next = deleted; + deleted = newp; + } + else + lastp = runp; + + runp = runp->next; + } + while (runp != NULL); /* Release the lock. */ lll_unlock (__fork_lock); + + /* Walk the list of all entries which have to be deleted. */ + while (deleted != NULL) + { + /* We need to be informed by possible current users. */ + deleted->handler->need_signal = 1; + /* Make sure this gets written out first. */ + atomic_write_barrier (); + + /* Decrement the reference counter. If it does not reach zero + wait for the last user. */ + atomic_decrement (&deleted->handler->refcntr); + unsigned int val; + while ((val = deleted->handler->refcntr) != 0) + lll_futex_wait (deleted->handler->refcntr, val); + + deleted = deleted->next; + } } -- cgit v1.2.3