]> git.saurik.com Git - apple/libpthread.git/blob - src/pthread_atfork.c
724a7fb2ea5eb43cd84da9fc1e98d2b84ec89b9d
[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 OSSpinLockLock(&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 OSSpinLockUnlock(&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 OSSpinLockLock(&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 OSSpinLockUnlock(&globals->pthread_atfork_lock);
73 mach_vm_deallocate(mach_task_self(), storage, size);
74 OSSpinLockLock(&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 OSSpinLockUnlock(&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 void
97 _pthread_fork_prepare(void)
98 {
99 pthread_globals_t globals = _pthread_globals();
100
101 OSSpinLockLock(&globals->pthread_atfork_lock);
102
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 OSSpinLockLock(&globals->psaved_self_global_lock);
112 globals->psaved_self = pthread_self();
113 OSSpinLockLock(&globals->psaved_self->lock);
114 }
115
116 // Called after the fork(2) system call returns to the parent process.
117 // Iterate pthread_atfork parent handlers.
118 void
119 _pthread_fork_parent(void)
120 {
121 pthread_globals_t globals = _pthread_globals();
122
123 OSSpinLockUnlock(&globals->psaved_self->lock);
124 OSSpinLockUnlock(&globals->psaved_self_global_lock);
125
126 size_t idx;
127 for (idx = 0; idx < globals->atfork_count; ++idx) {
128 struct pthread_atfork_entry *e = &globals->atfork[idx];
129 if (e->parent != NULL) {
130 e->parent();
131 }
132 }
133 OSSpinLockUnlock(&globals->pthread_atfork_lock);
134 }
135
136 // Called after the fork(2) system call returns to the new child process.
137 // Clean up data structures of other threads which no longer exist in the child.
138 // Make the current thread the main thread.
139 void
140 _pthread_fork_child(void)
141 {
142 pthread_globals_t globals = _pthread_globals();
143 globals->psaved_self_global_lock = OS_SPINLOCK_INIT;
144 __pthread_fork_child_internal(globals->psaved_self);
145 __is_threaded = 0;
146 pthread_workqueue_atfork_child();
147 }
148
149 // Iterate pthread_atfork child handlers.
150 void
151 _pthread_fork_child_postinit(void)
152 {
153 pthread_globals_t globals = _pthread_globals();
154 size_t idx;
155 for (idx = 0; idx < globals->atfork_count; ++idx) {
156 struct pthread_atfork_entry *e = &globals->atfork[idx];
157 if (e->child != NULL) {
158 e->child();
159 }
160 }
161 globals->pthread_atfork_lock = OS_SPINLOCK_INIT;
162 }