#include "internal.h"
#include <mach/mach_init.h>
#include <mach/mach_vm.h>
#include <platform/compat.h>
int
pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
{
int res = 0;
size_t idx;
pthread_globals_t globals = _pthread_globals();
_PTHREAD_LOCK(globals->pthread_atfork_lock);
idx = globals->atfork_count++;
if (idx == 0) {
globals->atfork = globals->atfork_storage;
} else if (idx == PTHREAD_ATFORK_INLINE_MAX) {
kern_return_t kr;
mach_vm_address_t storage = 0;
mach_vm_size_t size = PTHREAD_ATFORK_MAX * sizeof(struct pthread_atfork_entry);
_PTHREAD_UNLOCK(globals->pthread_atfork_lock);
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,
VM_INHERIT_DEFAULT);
_PTHREAD_LOCK(globals->pthread_atfork_lock);
if (kr == KERN_SUCCESS) {
if (globals->atfork == globals->atfork_storage) {
globals->atfork = storage;
memmove(globals->atfork, globals->atfork_storage, sizeof(globals->atfork_storage));
bzero(globals->atfork_storage, sizeof(globals->atfork_storage));
} else {
_PTHREAD_UNLOCK(globals->pthread_atfork_lock);
mach_vm_deallocate(mach_task_self(), storage, size);
_PTHREAD_LOCK(globals->pthread_atfork_lock);
}
} else {
res = ENOMEM;
}
} else if (idx >= PTHREAD_ATFORK_MAX) {
res = ENOMEM;
}
if (res == 0) {
struct pthread_atfork_entry *e = &globals->atfork[idx];
e->prepare = prepare;
e->parent = parent;
e->child = child;
}
_PTHREAD_UNLOCK(globals->pthread_atfork_lock);
return res;
}
void
_pthread_atfork_prepare_handlers(void)
{
pthread_globals_t globals = _pthread_globals();
_PTHREAD_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();
}
}
}
void
_pthread_atfork_prepare(void)
{
pthread_globals_t globals = _pthread_globals();
_PTHREAD_LOCK(globals->psaved_self_global_lock);
globals->psaved_self = pthread_self();
_PTHREAD_LOCK(globals->psaved_self->lock);
}
void
_pthread_atfork_parent(void)
{
pthread_globals_t globals = _pthread_globals();
_PTHREAD_UNLOCK(globals->psaved_self->lock);
_PTHREAD_UNLOCK(globals->psaved_self_global_lock);
}
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_UNLOCK(globals->pthread_atfork_lock);
}
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_init(globals->psaved_self);
struct _pthread_registration_data registration_data;
_pthread_bsdthread_init(®istration_data);
}
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);
}
void
_pthread_fork_prepare(void)
{
_pthread_atfork_prepare_handlers();
_pthread_atfork_prepare();
}
void
_pthread_fork_parent(void)
{
_pthread_atfork_parent();
_pthread_atfork_parent_handlers();
}
void
_pthread_fork_child(void)
{
_pthread_atfork_child();
}
void
_pthread_fork_child_postinit(void)
{
_pthread_atfork_child_handlers();
}