Back to index

glibc  2.9
unregister-atfork.c
Go to the documentation of this file.
00001 /* Copyright (C) 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <errno.h>
00021 #include <stdlib.h>
00022 #include <fork.h>
00023 #include <atomic.h>
00024 
00025 
00026 void
00027 __unregister_atfork (dso_handle)
00028      void *dso_handle;
00029 {
00030   /* Check whether there is any entry in the list which we have to
00031      remove.  It is likely that this is not the case so don't bother
00032      getting the lock.
00033 
00034      We do not worry about other threads adding entries for this DSO
00035      right this moment.  If this happens this is a race and we can do
00036      whatever we please.  The program will crash anyway seen.  */
00037   struct fork_handler *runp = __fork_handlers;
00038   struct fork_handler *lastp = NULL;
00039 
00040   while (runp != NULL)
00041     if (runp->dso_handle == dso_handle)
00042       break;
00043     else
00044       {
00045        lastp = runp;
00046        runp = runp->next;
00047       }
00048 
00049   if (runp == NULL)
00050     /* Nothing to do.  */
00051     return;
00052 
00053   /* Get the lock to not conflict with additions or deletions.  Note
00054      that there couldn't have been another thread deleting something.
00055      The __unregister_atfork function is only called from the
00056      dlclose() code which itself serializes the operations.  */
00057   lll_lock (__fork_lock, LLL_PRIVATE);
00058 
00059   /* We have to create a new list with all the entries we don't remove.  */
00060   struct deleted_handler
00061   {
00062     struct fork_handler *handler;
00063     struct deleted_handler *next;
00064   } *deleted = NULL;
00065 
00066   /* Remove the entries for the DSO which is unloaded from the list.
00067      It's a single linked list so readers are.  */
00068   do
00069     {
00070     again:
00071       if (runp->dso_handle == dso_handle)
00072        {
00073          if (lastp == NULL)
00074            {
00075              /* We have to use an atomic operation here because
00076                __linkin_atfork also uses one.  */
00077              if (catomic_compare_and_exchange_bool_acq (&__fork_handlers,
00078                                                   runp->next, runp)
00079                 != 0)
00080               {
00081                 runp = __fork_handlers;
00082                 goto again;
00083               }
00084            }
00085          else
00086            lastp->next = runp->next;
00087 
00088          /* We cannot overwrite the ->next element now.  Put the deleted
00089             entries in a separate list.  */
00090          struct deleted_handler *newp = alloca (sizeof (*newp));
00091          newp->handler = runp;
00092          newp->next = deleted;
00093          deleted = newp;
00094        }
00095       else
00096        lastp = runp;
00097 
00098       runp = runp->next;
00099     }
00100   while (runp != NULL);
00101 
00102   /* Release the lock.  */
00103   lll_unlock (__fork_lock, LLL_PRIVATE);
00104 
00105   /* Walk the list of all entries which have to be deleted.  */
00106   while (deleted != NULL)
00107     {
00108       /* We need to be informed by possible current users.  */
00109       deleted->handler->need_signal = 1;
00110       /* Make sure this gets written out first.  */
00111       atomic_write_barrier ();
00112 
00113       /* Decrement the reference counter.  If it does not reach zero
00114         wait for the last user.  */
00115       atomic_decrement (&deleted->handler->refcntr);
00116       unsigned int val;
00117       while ((val = deleted->handler->refcntr) != 0)
00118        lll_futex_wait (&deleted->handler->refcntr, val, LLL_PRIVATE);
00119 
00120       deleted = deleted->next;
00121     }
00122 }