Fork handlers

libpthread/src/pthread_atfork.c · 206 lines · browse source
pthread_atfork source · pthread_atfork reference
int
pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
{
	int res = 0;
	pthread_globals_t globals = _pthread_globals();

	_pthread_lock_lock(&globals->pthread_atfork_lock);

	if (globals->atfork_count == 0) {
		// Initialize pointer to inline storage.
		globals->atfork = globals->atfork_storage;
	} else if (globals->atfork_count == PTHREAD_ATFORK_INLINE_MAX) {
		if (globals->atfork != globals->atfork_storage) {
			PTHREAD_INTERNAL_CRASH(globals->atfork, "expected atfork to be inline");
		}

		// Migrate to out-of-line storage.
		kern_return_t kr;
		mach_vm_address_t storage = 0;
		mach_vm_size_t size = PTHREAD_ATFORK_MAX * sizeof(struct pthread_atfork_entry);
		kr = mach_vm_map(mach_task_self(),
				 &storage,
				 size,
				 vm_page_size - 1,
				 VM_MAKE_TAG(VM_MEMORY_OS_ALLOC_ONCE)| VM_FLAGS_ANYWHERE,
				 MEMORY_OBJECT_NULL,
				 0,
				 FALSE,
				 VM_PROT_DEFAULT,
				 VM_PROT_ALL,
… more in source
_pthread_atfork_prepare_handlers source
Called before the fork(2) system call is made in the parent process. Iterate pthread_atfork prepare handlers. Called first in libSystem_atfork_prepare().
void
_pthread_atfork_prepare_handlers(void)
{
	pthread_globals_t globals = _pthread_globals();

	_pthread_lock_lock(&globals->pthread_atfork_lock);
	size_t idx;
	for (idx = globals->atfork_count; idx > 0; --idx) {
		struct pthread_atfork_entry *e = &globals->atfork[idx-1];
		if (e->prepare != NULL) {
			e->prepare();
		}
	}
}
_pthread_atfork_prepare source
Take pthread-internal locks. Called last in libSystem_atfork_prepare().
void
_pthread_atfork_prepare(void)
{
	pthread_globals_t globals = _pthread_globals();

	_pthread_lock_lock(&globals->psaved_self_global_lock);
	globals->psaved_self = pthread_self();
	_pthread_lock_lock(&globals->psaved_self->lock);
}
_pthread_atfork_parent source
Called after the fork(2) system call returns to the parent process. Release pthread-internal locks Called first in libSystem_atfork_parent().
void
_pthread_atfork_parent(void)
{
	pthread_globals_t globals = _pthread_globals();

	_pthread_lock_unlock(&globals->psaved_self->lock);
	_pthread_lock_unlock(&globals->psaved_self_global_lock);
}
_pthread_atfork_parent_handlers source
Iterate pthread_atfork parent handlers. Called last in libSystem_atfork_parent().
void
_pthread_atfork_parent_handlers(void)
{
	pthread_globals_t globals = _pthread_globals();

	size_t idx;
	for (idx = 0; idx < globals->atfork_count; ++idx) {
		struct pthread_atfork_entry *e = &globals->atfork[idx];
		if (e->parent != NULL) {
			e->parent();
		}
	}
	_pthread_lock_unlock(&globals->pthread_atfork_lock);
}
_pthread_atfork_child source
Called after the fork(2) system call returns to the new child process. Clean up data structures of other threads which no longer exist in the child. Make the current thread the main thread. Called first in libSystem_atfork_child() (after _dyld_fork_child)
void
_pthread_atfork_child(void)
{
	pthread_globals_t globals = _pthread_globals();
	_pthread_lock_init(&globals->psaved_self_global_lock);
	__is_threaded = 0;
	_pthread_main_thread_postfork_init(globals->psaved_self);

	struct _pthread_registration_data registration_data;
	_pthread_bsdthread_init(&registration_data);
}
_pthread_atfork_child_handlers source
Iterate pthread_atfork child handlers. Called last in libSystem_atfork_child().
void
_pthread_atfork_child_handlers(void)
{
	pthread_globals_t globals = _pthread_globals();
	size_t idx;
	for (idx = 0; idx < globals->atfork_count; ++idx) {
		struct pthread_atfork_entry *e = &globals->atfork[idx];
		if (e->child != NULL) {
			e->child();
		}
	}
	_pthread_lock_init(&globals->pthread_atfork_lock);
}
_pthread_fork_prepare source
Preserve legacy symbols for older iOS simulators
void
_pthread_fork_prepare(void)
{
	_pthread_atfork_prepare_handlers();
	_pthread_atfork_prepare();
}