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(&registration_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