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 31PTHREAD_NOEXPORT void pthread_workqueue_atfork_child(void); 32PTHREAD_NOEXPORT void __pthread_fork_child_internal(pthread_t); 33 34int 35pthread_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. 96void 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. 118void 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. 139void 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. 150void 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} 163