#include <fork.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <errno_private.h>
#include <locks.h>
#include <libroot_private.h>
#include <pthread_private.h>
#include <runtime_loader.h>
#include <syscalls.h>
#include <user_thread.h>
typedef struct fork_hook {
struct fork_hook *next;
void (*function)(void);
} fork_hook;
#define FORK_LOCK_NAME "fork lock"
static fork_hook *sPrepareHooks, *sParentHooks, *sChildHooks;
static fork_hook *sLastParentHook, *sLastChildHook;
static mutex sForkLock = MUTEX_INITIALIZER(FORK_LOCK_NAME);
extern thread_id __main_thread_id;
static status_t
add_fork_hook(fork_hook **_hooks, fork_hook **_lastHook, void (*function)(void))
{
fork_hook *hook = (fork_hook *)malloc(sizeof(struct fork_hook));
if (hook == NULL)
return B_NO_MEMORY;
hook->function = function;
if (_lastHook) {
if (*_hooks == NULL) {
*_hooks = hook;
*_lastHook = hook;
} else {
#if 0
if (*_lastHook == NULL) {
fork_hook *last = *_hooks;
while (last->next)
last = last->next;
*_lastHook = last;
}
#endif
(*_lastHook)->next = hook;
*_lastHook = hook;
}
hook->next = NULL;
} else {
hook->next = *_hooks;
*_hooks = hook;
}
return B_OK;
}
static void
call_fork_hooks(fork_hook *hook)
{
while (hook) {
hook->function();
hook = hook->next;
}
}
status_t
__register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
{
status_t status;
defer_signals();
status = mutex_lock(&sForkLock);
if (status != B_OK) {
undefer_signals();
return status;
}
if (prepare)
status = add_fork_hook(&sPrepareHooks, NULL, prepare);
if (status == B_OK && parent)
status = add_fork_hook(&sParentHooks, &sLastParentHook, parent);
if (status == B_OK && child)
status = add_fork_hook(&sChildHooks, &sLastChildHook, child);
mutex_unlock(&sForkLock);
undefer_signals();
return status;
}
pid_t
fork(void)
{
thread_id thread;
status_t status;
defer_signals();
status = mutex_lock(&sForkLock);
if (status != B_OK) {
undefer_signals();
return status;
}
call_fork_hooks(sPrepareHooks);
__heap_before_fork();
thread = _kern_fork();
if (thread == 0) {
__main_thread_id = find_thread(NULL);
pthread_self()->id = __main_thread_id;
mutex_init(&sForkLock, FORK_LOCK_NAME);
__gRuntimeLoader->reinit_after_fork();
__heap_after_fork_child();
__reinit_pwd_backend_after_fork();
call_fork_hooks(sChildHooks);
} else {
__heap_after_fork_parent();
call_fork_hooks(sParentHooks);
mutex_unlock(&sForkLock);
}
undefer_signals();
if (thread < 0) {
__set_errno(thread);
thread = -1;
}
return thread;
}
pid_t
vfork(void)
{
thread_id thread = _kern_fork();
if (thread < 0) {
__set_errno(thread);
thread = -1;
}
return thread;
}