Added thread-safe pthread_atfork() support.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9309 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2004-10-12 16:44:40 +00:00
parent 81d4f788a2
commit 2740dedc5b

View File

@ -5,22 +5,145 @@
#include <syscalls.h>
#include <fork.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
typedef struct fork_hook {
struct fork_hook *next;
void (*function)(void);
} fork_hook;
static fork_hook *sPrepareHooks, *sParentHooks, *sChildHooks;
static fork_hook *sLastParentHook, *sLastChildHook;
static sem_id sForkLock;
/** Adds a hook to the specified list.
* If \a _lastHook is NULL, the hook will be added at the head of the list,
* else it will be added at the tail of the list.
* Since this function allocates memory, it can fail, and returns B_NO_MEMORY
* in that case. It returns B_OK on success.
*/
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) {
// add hook at the end of the list
if (*_hooks == NULL) {
// first entry of this list
*_hooks = hook;
*_lastHook = hook;
} else {
// any other item
(*_lastHook)->next = hook;
*_lastHook = hook;
}
hook->next = NULL;
} else {
// add hook at the beginning of the list
hook->next = *_hooks;
*_hooks = hook;
}
return B_OK;
}
/** Calls all hooks in the specified list in ascending order.
*/
static void
call_fork_hooks(fork_hook *hook)
{
while (hook) {
hook->function();
hook = hook->next;
}
}
status_t
__init_fork(void)
{
sForkLock = create_sem(1, "fork lock");
if (sForkLock < B_OK)
return sForkLock;
return B_OK;
}
/** Private support function that registers the hooks that will be executed
* before and after the team is fork()ed.
* It is called from pthread_atfork() and atfork().
*/
status_t
__register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
{
status_t status;
while ((status = acquire_sem(sForkLock)) == B_INTERRUPTED);
if (status != B_OK)
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);
release_sem(sForkLock);
return status;
}
pid_t
fork(void)
{
thread_id thread = _kern_fork();
thread_id thread;
status_t status;
while ((status = acquire_sem(sForkLock)) == B_INTERRUPTED);
if (status != B_OK)
return status;
// call preparation hooks
call_fork_hooks(sPrepareHooks);
thread = _kern_fork();
if (thread < 0) {
// something went wrong
errno = thread;
return -1;
}
// ToDo: initialize child
// ToDo: atfork() support
if (thread == 0) {
// we are the child
// ToDo: initialize child
__init_fork();
call_fork_hooks(sChildHooks);
} else {
// we are the parent
call_fork_hooks(sParentHooks);
release_sem(sForkLock);
}
return thread;
}