about summary refs log tree commit diff
path: root/nptl/unregister-atfork.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@hack.frob.com>2014-07-07 09:28:38 -0700
committerRoland McGrath <roland@hack.frob.com>2014-07-07 09:28:38 -0700
commit08192659bbeae149e7cb1f4c43547257f7099bb0 (patch)
tree9f602a4e9f0aefdb06bc15a76be46e958e91a58f /nptl/unregister-atfork.c
parent96baf6ffc57b811bbc424a1fc72ef77dd56d7a18 (diff)
downloadglibc-08192659bbeae149e7cb1f4c43547257f7099bb0.tar.gz
glibc-08192659bbeae149e7cb1f4c43547257f7099bb0.tar.xz
glibc-08192659bbeae149e7cb1f4c43547257f7099bb0.zip
Get rid of nptl/sysdeps/ entirely!
Diffstat (limited to 'nptl/unregister-atfork.c')
-rw-r--r--nptl/unregister-atfork.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/nptl/unregister-atfork.c b/nptl/unregister-atfork.c
new file mode 100644
index 0000000000..9005160f99
--- /dev/null
+++ b/nptl/unregister-atfork.c
@@ -0,0 +1,121 @@
+/* Copyright (C) 2002-2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <fork.h>
+#include <atomic.h>
+
+
+void
+__unregister_atfork (dso_handle)
+     void *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
+      {
+	lastp = runp;
+	runp = runp->next;
+      }
+
+  if (runp == NULL)
+    /* Nothing to do.  */
+    return;
+
+  /* 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, LLL_PRIVATE);
+
+  /* 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
+    {
+    again:
+      if (runp->dso_handle == dso_handle)
+	{
+	  if (lastp == NULL)
+	    {
+	      /* We have to use an atomic operation here because
+		 __linkin_atfork also uses one.  */
+	      if (catomic_compare_and_exchange_bool_acq (&__fork_handlers,
+							 runp->next, runp)
+		  != 0)
+		{
+		  runp = __fork_handlers;
+		  goto again;
+		}
+	    }
+	  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, LLL_PRIVATE);
+
+  /* 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, LLL_PRIVATE);
+
+      deleted = deleted->next;
+    }
+}