m3core/src/runtime/common/RTProcessC.c
typedef void (*ForkHandler)(void);
#include "m3core.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef M3_USER_THREADS
typedef struct _fork_handlers_t {
ForkHandler prepare;
ForkHandler parent;
ForkHandler child;
} fork_handlers_t;
typedef struct _vector_t {
void* p;
size_t count_allocated;
size_t count_used;
} vector_t;
#define fork_handlers RTProcess__fork_handlers
static vector_t fork_handlers;
M3_DLL_EXPORT
INTEGER
__cdecl
RTProcess__RegisterForkHandlers(ForkHandler prepare,
ForkHandler parent,
ForkHandler child)
/* This is a replacement for pthread_atfork.
pthread_atfork could/should work, but apparently using -pthread
on some systems causes problems with user threads. That
doesn't really make sense, but oh well. */
{
typedef fork_handlers_t T;
T* p = { 0 };
size_t count_allocated = { 0 };
size_t count_used = { 0 };
int ret = { 0 };
Scheduler__DisableSwitching();
count_used = fork_handlers.count_used;
count_allocated = fork_handlers.count_allocated;
if (count_used + 1 >= count_allocated)
{
size_t new_allocated = count_allocated ? (count_allocated * 3 / 2) : 16;
p = (T*)calloc(new_allocated, sizeof(T));
if (!p)
{
ret = ENOMEM;
goto Exit;
}
memcpy(p, fork_handlers.p, count_used * sizeof(T));
free(fork_handlers.p);
fork_handlers.count_allocated = new_allocated;
fork_handlers.p = p;
p += count_used;
}
else
{
p = count_used + (T*)fork_handlers.p;
}
p->prepare = prepare;
p->parent = parent;
p->child = child;
fork_handlers.count_used = (count_used + 1);
Exit:
Scheduler__EnableSwitching();
return ret;
}
#else /* M3_USER_THREADS */
M3_DLL_EXPORT
INTEGER
__cdecl
RTProcess__RegisterForkHandlers(ForkHandler prepare,
ForkHandler parent,
ForkHandler child)
{
/* FreeBSD < 6 lacks pthread_atfork. Would be good to use autoconf.
* VMS lacks pthread_atfork? Would be good to use autoconf.
* Win32 lacks pthread_atfork and fork. OK.
*
* As well, for all Posix systems, we could implement
* atfork ourselves, as long as we provide a fork()
* wrapper that code uses.
*/
#if defined(_WIN32) \
|| defined(__vms) \
|| (defined(__FreeBSD__) && (__FreeBSD__ < 6))
return 0;
#else
while (1)
{
int i = pthread_atfork(prepare, parent, child);
if (i != EAGAIN)
return i;
sleep(0);
}
#endif
}
#endif /* M3_USER_THREADS */
#ifndef _WIN32
M3_DLL_EXPORT
INTEGER
__cdecl
RTProcess__Fork(void)
{
#ifdef M3_USER_THREADS
int new_pid = { 0 };
fork_handlers_t* p = { 0 };
size_t count_used = { 0 };
size_t i = { 0 };
int err = { 0 };
Scheduler__DisableSwitching();
p = (fork_handlers_t*)fork_handlers.p;
count_used = fork_handlers.count_used;
for (i = 0; < i < count_used; ++i)
{
ForkHandler handler = p[i].prepare;
if (handler) handler();
}
new_pid = fork();
if (new_pid == -1)
err = errno;
for (i = 0; < i < count_used; ++i)
{
ForkHandler handler = new_pid ? p[i].parent : p[i].child;
if (handler) handler();
}
if (new_pid == -1)
errno = err;
Exit:
Scheduler__EnableSwitching();
return new_pid;
#elif defined(__sun)
/* Prior to Solaris 2.10, fork() was fork1() or forkall() depending
* on which library used. In Solaris 2.10, fork() is always fork1(),
* and a separate forkall() is available. fork1()'s declaration
* does have some #ifdef guards around it; hopefully ok.
*/
return fork1();
#else
return fork();
#endif
}
#endif /* Win32 */
#ifdef __cplusplus
} /* extern "C" */
#endif