s390: Use dl-symbol-redir-ifunc.h on cpu-tunables
[glibc.git] / posix / register-atfork.c
blob14ebc2587a59dff702f8134732ff13887b318df1
1 /* Copyright (C) 2002-2023 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
18 #include <libc-lock.h>
19 #include <stdbool.h>
20 #include <register-atfork.h>
21 #include <intprops.h>
22 #include <stdio.h>
24 #define DYNARRAY_ELEMENT struct fork_handler
25 #define DYNARRAY_STRUCT fork_handler_list
26 #define DYNARRAY_PREFIX fork_handler_list_
27 #define DYNARRAY_INITIAL_SIZE 48
28 #include <malloc/dynarray-skeleton.c>
30 static struct fork_handler_list fork_handlers;
31 static uint64_t fork_handler_counter;
33 static int atfork_lock = LLL_LOCK_INITIALIZER;
35 int
36 __register_atfork (void (*prepare) (void), void (*parent) (void),
37 void (*child) (void), void *dso_handle)
39 lll_lock (atfork_lock, LLL_PRIVATE);
41 if (fork_handler_counter == 0)
42 fork_handler_list_init (&fork_handlers);
44 struct fork_handler *newp = fork_handler_list_emplace (&fork_handlers);
45 if (newp != NULL)
47 newp->prepare_handler = prepare;
48 newp->parent_handler = parent;
49 newp->child_handler = child;
50 newp->dso_handle = dso_handle;
52 /* IDs assigned to handlers start at 1 and increment with handler
53 registration. Un-registering a handlers discards the corresponding
54 ID. It is not reused in future registrations. */
55 if (INT_ADD_OVERFLOW (fork_handler_counter, 1))
56 __libc_fatal ("fork handler counter overflow");
57 newp->id = ++fork_handler_counter;
60 /* Release the lock. */
61 lll_unlock (atfork_lock, LLL_PRIVATE);
63 return newp == NULL ? ENOMEM : 0;
65 libc_hidden_def (__register_atfork)
67 static struct fork_handler *
68 fork_handler_list_find (struct fork_handler_list *fork_handlers,
69 void *dso_handle)
71 for (size_t i = 0; i < fork_handler_list_size (fork_handlers); i++)
73 struct fork_handler *elem = fork_handler_list_at (fork_handlers, i);
74 if (elem->dso_handle == dso_handle)
75 return elem;
77 return NULL;
80 void
81 __unregister_atfork (void *dso_handle)
83 lll_lock (atfork_lock, LLL_PRIVATE);
85 struct fork_handler *first = fork_handler_list_find (&fork_handlers,
86 dso_handle);
87 /* Removing is done by shifting the elements in the way the elements
88 that are not to be removed appear in the beginning in dynarray.
89 This avoid the quadradic run-time if a naive strategy to remove and
90 shift one element at time. */
91 if (first != NULL)
93 struct fork_handler *new_end = first;
94 first++;
95 for (; first != fork_handler_list_end (&fork_handlers); ++first)
97 if (first->dso_handle != dso_handle)
99 *new_end = *first;
100 ++new_end;
104 ptrdiff_t removed = first - new_end;
105 for (size_t i = 0; i < removed; i++)
106 fork_handler_list_remove_last (&fork_handlers);
109 lll_unlock (atfork_lock, LLL_PRIVATE);
112 uint64_t
113 __run_prefork_handlers (_Bool do_locking)
115 uint64_t lastrun;
117 if (do_locking)
118 lll_lock (atfork_lock, LLL_PRIVATE);
120 /* We run prepare handlers from last to first. After fork, only
121 handlers up to the last handler found here (pre-fork) will be run.
122 Handlers registered during __run_prefork_handlers or
123 __run_postfork_handlers will be positioned after this last handler, and
124 since their prepare handlers won't be run now, their parent/child
125 handlers should also be ignored. */
126 lastrun = fork_handler_counter;
128 size_t sl = fork_handler_list_size (&fork_handlers);
129 for (size_t i = sl; i > 0;)
131 struct fork_handler *runp
132 = fork_handler_list_at (&fork_handlers, i - 1);
134 uint64_t id = runp->id;
136 if (runp->prepare_handler != NULL)
138 if (do_locking)
139 lll_unlock (atfork_lock, LLL_PRIVATE);
141 runp->prepare_handler ();
143 if (do_locking)
144 lll_lock (atfork_lock, LLL_PRIVATE);
147 /* We unlocked, ran the handler, and locked again. In the
148 meanwhile, one or more deregistrations could have occurred leading
149 to the current (just run) handler being moved up the list or even
150 removed from the list itself. Since handler IDs are guaranteed to
151 to be in increasing order, the next handler has to have: */
153 /* A. An earlier position than the current one has. */
154 i--;
156 /* B. A lower ID than the current one does. The code below skips
157 any newly added handlers with higher IDs. */
158 while (i > 0
159 && fork_handler_list_at (&fork_handlers, i - 1)->id >= id)
160 i--;
163 return lastrun;
166 void
167 __run_postfork_handlers (enum __run_fork_handler_type who, _Bool do_locking,
168 uint64_t lastrun)
170 size_t sl = fork_handler_list_size (&fork_handlers);
171 for (size_t i = 0; i < sl;)
173 struct fork_handler *runp = fork_handler_list_at (&fork_handlers, i);
174 uint64_t id = runp->id;
176 /* prepare handlers were not run for handlers with ID > LASTRUN.
177 Thus, parent/child handlers will also not be run. */
178 if (id > lastrun)
179 break;
181 if (do_locking)
182 lll_unlock (atfork_lock, LLL_PRIVATE);
184 if (who == atfork_run_child && runp->child_handler)
185 runp->child_handler ();
186 else if (who == atfork_run_parent && runp->parent_handler)
187 runp->parent_handler ();
189 if (do_locking)
190 lll_lock (atfork_lock, LLL_PRIVATE);
192 /* We unlocked, ran the handler, and locked again. In the meanwhile,
193 one or more [de]registrations could have occurred. Due to this,
194 the list size must be updated. */
195 sl = fork_handler_list_size (&fork_handlers);
197 /* The just-run handler could also have moved up the list. */
199 if (sl > i && fork_handler_list_at (&fork_handlers, i)->id == id)
200 /* The position of the recently run handler hasn't changed. The
201 next handler to be run is an easy increment away. */
202 i++;
203 else
205 /* The next handler to be run is the first handler in the list
206 to have an ID higher than the current one. */
207 for (i = 0; i < sl; i++)
209 if (fork_handler_list_at (&fork_handlers, i)->id > id)
210 break;
215 if (do_locking)
216 lll_unlock (atfork_lock, LLL_PRIVATE);
220 void
221 __libc_atfork_freemem (void)
223 lll_lock (atfork_lock, LLL_PRIVATE);
225 fork_handler_list_free (&fork_handlers);
227 lll_unlock (atfork_lock, LLL_PRIVATE);