]> git.saurik.com Git - apple/libpthread.git/blob - src/pthread_atfork.c
8c04ef6f192b5997cfff1cdffb7f4e41b8724b94
[apple/libpthread.git] / 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 <libkern/OSAtomic.h>
27 #include <mach/mach_init.h>
28 #include <mach/mach_vm.h>
29 #include <platform/compat.h>
30
31 PTHREAD_NOEXPORT void pthread_workqueue_atfork_child(void);
32 PTHREAD_NOEXPORT void __pthread_fork_child_internal(pthread_t);
33
34 int
35 pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
36 {
37 int res = 0;
38 size_t idx;
39 pthread_globals_t globals = _pthread_globals();
40
41 _PTHREAD_LOCK(globals->pthread_atfork_lock);
42 idx = globals->atfork_count++;
43
44 if (idx == 0) {
45 // Initialize pointer to inline storage.
46 globals->atfork = globals->atfork_storage;
47 } else if (idx == PTHREAD_ATFORK_INLINE_MAX) {
48 // Migrate to out-of-line storage.
49 kern_return_t kr;
50 mach_vm_address_t storage = 0;
51 mach_vm_size_t size = PTHREAD_ATFORK_MAX * sizeof(struct pthread_atfork_entry);
52 _PTHREAD_UNLOCK(globals->pthread_atfork_lock);
53 kr = mach_vm_map(mach_task_self(),
54 &storage,
55 size,
56 vm_page_size - 1,
57 VM_MAKE_TAG(VM_MEMORY_OS_ALLOC_ONCE)| VM_FLAGS_ANYWHERE,
58 MEMORY_OBJECT_NULL,
59 0,
60 FALSE,
61 VM_PROT_DEFAULT,
62 VM_PROT_ALL,
63 VM_INHERIT_DEFAULT);
64 _PTHREAD_LOCK(globals->pthread_atfork_lock);
65 if (kr == KERN_SUCCESS) {
66 if (globals->atfork == globals->atfork_storage) {
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 {
71 // Another thread did vm_map first.
72 _PTHREAD_UNLOCK(globals->pthread_atfork_lock);
73 mach_vm_deallocate(mach_task_self(), storage, size);
74 _PTHREAD_LOCK(globals->pthread_atfork_lock);
75 }
76 } else {
77 res = ENOMEM;
78 }
79 } else if (idx >= PTHREAD_ATFORK_MAX) {
80 res = ENOMEM;
81 }
82
83 if (res == 0) {
84 struct pthread_atfork_entry *e = &globals->atfork[idx];
85 e->prepare = prepare;
86 e->parent = parent;
87 e->child = child;
88 }
89 _PTHREAD_UNLOCK(globals->pthread_atfork_lock);
90
91 return res;
92 }
93
94 // Called before the fork(2) system call is made in the parent process.
95 // Iterate pthread_atfork prepare handlers.
96 // Called first in libSystem_atfork_prepare().
97 void
98 _pthread_atfork_prepare_handlers(void)
99 {
100 pthread_globals_t globals = _pthread_globals();
101
102 _PTHREAD_LOCK(globals->pthread_atfork_lock);
103 size_t idx;
104 for (idx = globals->atfork_count; idx > 0; --idx) {
105 struct pthread_atfork_entry *e = &globals->atfork[idx-1];
106 if (e->prepare != NULL) {
107 e->prepare();
108 }
109 }
110 }
111
112 // Take pthread-internal locks.
113 // Called last in libSystem_atfork_prepare().
114 void
115 _pthread_atfork_prepare(void)
116 {
117 pthread_globals_t globals = _pthread_globals();
118
119 _PTHREAD_LOCK(globals->psaved_self_global_lock);
120 globals->psaved_self = pthread_self();
121 _PTHREAD_LOCK(globals->psaved_self->lock);
122 }
123
124 // Called after the fork(2) system call returns to the parent process.
125 // Release pthread-internal locks
126 // Called first in libSystem_atfork_parent().
127 void
128 _pthread_atfork_parent(void)
129 {
130 pthread_globals_t globals = _pthread_globals();
131
132 _PTHREAD_UNLOCK(globals->psaved_self->lock);
133 _PTHREAD_UNLOCK(globals->psaved_self_global_lock);
134 }
135
136 // Iterate pthread_atfork parent handlers.
137 // Called last in libSystem_atfork_parent().
138 void
139 _pthread_atfork_parent_handlers(void)
140 {
141 pthread_globals_t globals = _pthread_globals();
142
143 size_t idx;
144 for (idx = 0; idx < globals->atfork_count; ++idx) {
145 struct pthread_atfork_entry *e = &globals->atfork[idx];
146 if (e->parent != NULL) {
147 e->parent();
148 }
149 }
150 _PTHREAD_UNLOCK(globals->pthread_atfork_lock);
151 }
152
153 // Called after the fork(2) system call returns to the new child process.
154 // Clean up data structures of other threads which no longer exist in the child.
155 // Make the current thread the main thread.
156 // Called first in libSystem_atfork_child() (after _dyld_fork_child)
157 void
158 _pthread_atfork_child(void)
159 {
160 pthread_globals_t globals = _pthread_globals();
161 _PTHREAD_LOCK_INIT(globals->psaved_self_global_lock);
162 __pthread_fork_child_internal(globals->psaved_self);
163 __is_threaded = 0;
164 pthread_workqueue_atfork_child();
165 }
166
167 // Iterate pthread_atfork child handlers.
168 // Called last in libSystem_atfork_child().
169 void
170 _pthread_atfork_child_handlers(void)
171 {
172 pthread_globals_t globals = _pthread_globals();
173 size_t idx;
174 for (idx = 0; idx < globals->atfork_count; ++idx) {
175 struct pthread_atfork_entry *e = &globals->atfork[idx];
176 if (e->child != NULL) {
177 e->child();
178 }
179 }
180 _PTHREAD_LOCK_INIT(globals->pthread_atfork_lock);
181 }
182
183 // Preserve legacy symbols for older iOS simulators
184 void
185 _pthread_fork_prepare(void)
186 {
187 _pthread_atfork_prepare_handlers();
188 _pthread_atfork_prepare();
189 }
190
191 void
192 _pthread_fork_parent(void)
193 {
194 _pthread_atfork_parent();
195 _pthread_atfork_parent_handlers();
196 }
197
198 void
199 _pthread_fork_child(void)
200 {
201 _pthread_atfork_child();
202 }
203
204 void
205 _pthread_fork_child_postinit(void)
206 {
207 _pthread_atfork_child_handlers();
208 }