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