Reduce code duplication in atfork
[kopensolaris-gnu/glibc.git] / nptl / sysdeps / unix / sysv / linux / register-atfork.c
1 /* Copyright (C) 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <fork.h>
24 #include <pthreadP.h>
25
26
27 /* Lock to protect allocation and deallocation of fork handlers.  */
28 #ifndef lll_define_initialized
29 int __fork_lock = LLL_LOCK_INITIALIZER;
30 #else
31 lll_define_initialized (, __fork_lock);
32 #endif
33
34
35 /* Number of pre-allocated handler entries.  */
36 #define NHANDLER 48
37
38 /* Memory pool for fork handler structures.  */
39 static struct fork_handler_pool
40 {
41   struct fork_handler_pool *next;
42   struct fork_handler mem[NHANDLER];
43 } fork_handler_pool;
44
45
46 static struct fork_handler *
47 fork_handler_alloc (void)
48 {
49   struct fork_handler_pool *runp = &fork_handler_pool;
50   struct fork_handler *result = NULL;
51   unsigned int i;
52
53   do
54     {
55       /* Search for an empty entry.  */
56       for (i = 0; i < NHANDLER; ++i)
57         if (runp->mem[i].refcntr == 0)
58           goto found;
59     }
60   while ((runp = runp->next) != NULL);
61
62   /* We have to allocate a new entry.  */
63   runp = (struct fork_handler_pool *) calloc (1, sizeof (*runp));
64   if (runp != NULL)
65     {
66       /* Enqueue the new memory pool into the list.  */
67       runp->next = fork_handler_pool.next;
68       fork_handler_pool.next = runp;
69
70       /* We use the last entry on the page.  This means when we start
71          searching from the front the next time we will find the first
72          entry unused.  */
73       i = NHANDLER - 1;
74
75     found:
76       result = &runp->mem[i];
77       result->refcntr = 1;
78       result->need_signal = 0;
79     }
80
81   return result;
82 }
83
84
85 int
86 __register_atfork (prepare, parent, child, dso_handle)
87      void (*prepare) (void);
88      void (*parent) (void);
89      void (*child) (void);
90      void *dso_handle;
91 {
92   /* Get the lock to not conflict with other allocations.  */
93   lll_lock (__fork_lock, LLL_PRIVATE);
94
95   struct fork_handler *newp = fork_handler_alloc ();
96
97   if (newp != NULL)
98     {
99       /* Initialize the new record.  */
100       newp->prepare_handler = prepare;
101       newp->parent_handler = parent;
102       newp->child_handler = child;
103       newp->dso_handle = dso_handle;
104
105       newp->next = __fork_handlers;
106       __fork_handlers = newp;
107     }
108
109   /* Release the lock.  */
110   lll_unlock (__fork_lock, LLL_PRIVATE);
111
112   return newp == NULL ? ENOMEM : 0;
113 }
114 libc_hidden_def (__register_atfork)
115
116
117 libc_freeres_fn (free_mem)
118 {
119   /* Get the lock to not conflict with running forks.  */
120   lll_lock (__fork_lock, LLL_PRIVATE);
121
122   /* No more fork handlers.  */
123   __fork_handlers = NULL;
124
125   /* Free eventually alloated memory blocks for the object pool.  */
126   struct fork_handler_pool *runp = fork_handler_pool.next;
127
128   memset (&fork_handler_pool, '\0', sizeof (fork_handler_pool));
129
130   /* Release the lock.  */
131   lll_unlock (__fork_lock, LLL_PRIVATE);
132
133   /* We can free the memory after releasing the lock.  */
134   while (runp != NULL)
135     {
136       struct fork_handler_pool *oldp = runp;
137       runp = runp->next;
138       free (oldp);
139     }
140 }