libpthread/src/pthread_atfork.c
1/*
2 * Copyright (c) 1999, 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "internal.h"
25
26#include <mach/mach_init.h>
27#include <mach/mach_vm.h>
28
29#if !VARIANT_DYLD
30int
31pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
32{
33 int res = 0;
34 pthread_globals_t globals = _pthread_globals();
35
36 _pthread_lock_lock(&globals->pthread_atfork_lock);
37
38 if (globals->atfork_count == 0) {
39 // Initialize pointer to inline storage.
40 globals->atfork = globals->atfork_storage;
41 } else if (globals->atfork_count == PTHREAD_ATFORK_INLINE_MAX) {
42 if (globals->atfork != globals->atfork_storage) {
43 PTHREAD_INTERNAL_CRASH(globals->atfork, "expected atfork to be inline");
44 }
45
46 // Migrate to out-of-line storage.
47 kern_return_t kr;
48 mach_vm_address_t storage = 0;
49 mach_vm_size_t size = PTHREAD_ATFORK_MAX * sizeof(struct pthread_atfork_entry);
50 kr = mach_vm_map(mach_task_self(),
51 &storage,
52 size,
53 vm_page_size - 1,
54 VM_MAKE_TAG(VM_MEMORY_OS_ALLOC_ONCE)| VM_FLAGS_ANYWHERE,
55 MEMORY_OBJECT_NULL,
56 0,
57 FALSE,
58 VM_PROT_DEFAULT,
59 VM_PROT_ALL,
60 VM_INHERIT_DEFAULT);
61
62 if (kr != KERN_SUCCESS) {
63 res = ENOMEM;
64 goto unlock;
65 }
66
67 globals->atfork = storage;
68 memmove(globals->atfork, globals->atfork_storage, sizeof(globals->atfork_storage));
69 bzero(globals->atfork_storage, sizeof(globals->atfork_storage));
70 } else if (globals->atfork_count >= PTHREAD_ATFORK_MAX) {
71 res = ENOMEM;
72 goto unlock;
73 }
74
75 struct pthread_atfork_entry *e = &globals->atfork[globals->atfork_count];
76 e->prepare = prepare;
77 e->parent = parent;
78 e->child = child;
79
80 globals->atfork_count++;
81
82unlock:
83 _pthread_lock_unlock(&globals->pthread_atfork_lock);
84
85 return res;
86}
87
88// Called before the fork(2) system call is made in the parent process.
89// Iterate pthread_atfork prepare handlers.
90// Called first in libSystem_atfork_prepare().
91void
92_pthread_atfork_prepare_handlers(void)
93{
94 pthread_globals_t globals = _pthread_globals();
95
96 _pthread_lock_lock(&globals->pthread_atfork_lock);
97 size_t idx;
98 for (idx = globals->atfork_count; idx > 0; --idx) {
99 struct pthread_atfork_entry *e = &globals->atfork[idx-1];
100 if (e->prepare != NULL) {
101 e->prepare();
102 }
103 }
104}
105
106// Take pthread-internal locks.
107// Called last in libSystem_atfork_prepare().
108void
109_pthread_atfork_prepare(void)
110{
111 pthread_globals_t globals = _pthread_globals();
112
113 _pthread_lock_lock(&globals->psaved_self_global_lock);
114 globals->psaved_self = pthread_self();
115 _pthread_lock_lock(&globals->psaved_self->lock);
116}
117
118// Called after the fork(2) system call returns to the parent process.
119// Release pthread-internal locks
120// Called first in libSystem_atfork_parent().
121void
122_pthread_atfork_parent(void)
123{
124 pthread_globals_t globals = _pthread_globals();
125
126 _pthread_lock_unlock(&globals->psaved_self->lock);
127 _pthread_lock_unlock(&globals->psaved_self_global_lock);
128}
129
130// Iterate pthread_atfork parent handlers.
131// Called last in libSystem_atfork_parent().
132void
133_pthread_atfork_parent_handlers(void)
134{
135 pthread_globals_t globals = _pthread_globals();
136
137 size_t idx;
138 for (idx = 0; idx < globals->atfork_count; ++idx) {
139 struct pthread_atfork_entry *e = &globals->atfork[idx];
140 if (e->parent != NULL) {
141 e->parent();
142 }
143 }
144 _pthread_lock_unlock(&globals->pthread_atfork_lock);
145}
146
147// Called after the fork(2) system call returns to the new child process.
148// Clean up data structures of other threads which no longer exist in the child.
149// Make the current thread the main thread.
150// Called first in libSystem_atfork_child() (after _dyld_fork_child)
151void
152_pthread_atfork_child(void)
153{
154 pthread_globals_t globals = _pthread_globals();
155 _pthread_lock_init(&globals->psaved_self_global_lock);
156 __is_threaded = 0;
157 _pthread_main_thread_postfork_init(globals->psaved_self);
158
159 struct _pthread_registration_data registration_data;
160 _pthread_bsdthread_init(®istration_data);
161}
162
163// Iterate pthread_atfork child handlers.
164// Called last in libSystem_atfork_child().
165void
166_pthread_atfork_child_handlers(void)
167{
168 pthread_globals_t globals = _pthread_globals();
169 size_t idx;
170 for (idx = 0; idx < globals->atfork_count; ++idx) {
171 struct pthread_atfork_entry *e = &globals->atfork[idx];
172 if (e->child != NULL) {
173 e->child();
174 }
175 }
176 _pthread_lock_init(&globals->pthread_atfork_lock);
177}
178
179// Preserve legacy symbols for older iOS simulators
180void
181_pthread_fork_prepare(void)
182{
183 _pthread_atfork_prepare_handlers();
184 _pthread_atfork_prepare();
185}
186
187void
188_pthread_fork_parent(void)
189{
190 _pthread_atfork_parent();
191 _pthread_atfork_parent_handlers();
192}
193
194void
195_pthread_fork_child(void)
196{
197 _pthread_atfork_child();
198}
199
200void
201_pthread_fork_child_postinit(void)
202{
203 _pthread_atfork_child_handlers();
204}
205#endif // !VARIANT_DYLD