summaryrefslogtreecommitdiff
path: root/nptl/sysdeps/unix/sysv/linux/unregister-atfork.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2003-05-09 03:17:42 +0000
committerUlrich Drepper <drepper@redhat.com>2003-05-09 03:17:42 +0000
commit92d83c725e09dbc76acfedc1cf85a01f9f54452d (patch)
tree039357dd8df0ad0c249d28e098e921ff15fb1eb0 /nptl/sysdeps/unix/sysv/linux/unregister-atfork.c
parent29b095a1561f866b995b23621d1e2ee78e473dc4 (diff)
Update.
2003-05-08 Ulrich Drepper <drepper@redhat.com> * malloc/thread-m.h: Remove special handling of thread_atfork if HAVE_register_atfork_malloc is defined.
Diffstat (limited to 'nptl/sysdeps/unix/sysv/linux/unregister-atfork.c')
-rw-r--r--nptl/sysdeps/unix/sysv/linux/unregister-atfork.c112
1 files changed, 74 insertions, 38 deletions
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 <errno.h>
#include <stdlib.h>
#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 <atomic.h>
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;
+ }
}