summaryrefslogtreecommitdiff
path: root/fork.c
diff options
context:
space:
mode:
authorrpj <rpj>1998-07-24 09:45:04 +0000
committerrpj <rpj>1998-07-24 09:45:04 +0000
commit99acb3fb113a739fe65a3593d86dabaf9d676b67 (patch)
tree172a2585f08833a5feab290153579feb3f2d807f /fork.c
parent82fa43b768e09bf30669351b61eb2f94cb1c71a3 (diff)
Fri Jul 24 16:33:17 1998 Ross Johnson <rpj@swan.canberra.edu.au>
* fork.c (pthread_atfork): Add all the necessary push calls. Local implementation semantics: If we get an ENOMEM at any time then ALL handlers (including those from previous pthread_atfork() calls) will be popped off each of the three atfork stacks before we return. (fork): Add all the necessary pop calls. Add the thread cancellation and join calls to the child fork. Add #includes. * implement.h: (_pthread_handler_push): Fix return type and stack arg type in prototype. (_pthread_handler_pop): Fix stack arg type in prototype. (_pthread_handler_pop_all): Fix stack arg type in prototype. * cleanup.c (_pthread_handler_push): Change return type to int and return ENOMEM if malloc() fails.
Diffstat (limited to 'fork.c')
-rw-r--r--fork.c84
1 files changed, 74 insertions, 10 deletions
diff --git a/fork.c b/fork.c
index 0b27558..534effc 100644
--- a/fork.c
+++ b/fork.c
@@ -5,33 +5,73 @@
* Implementation of fork() for POSIX threads.
*/
+#include "pthread.h"
+#include "implement.h"
+
int
pthread_atfork(void (*prepare)(void),
void (*parent)(void),
void (*child)(void))
{
- /* Push handlers (unless NULL) onto their respective stacks. */
+ /* Push handlers (unless NULL) onto their respective stacks.
+
+ Local implementation semantics:
+ If we get an ENOMEM at any time in here then ALL handlers
+ (including those from previous pthread_atfork() calls) will be
+ popped off each of the three atfork stacks before we return. */
+
+ int ret = 0;
if (prepare != NULL)
{
/* Push prepare. */
- /* If push fails, return ENOMEM. */
+ if (_pthread_handler_push(_PTHREAD_FORKPREPARE_STACK,
+ _PTHREAD_HANDLER_POP_FIFO,
+ (void (*prepare)(void *)), NULL) == ENOMEM)
+ {
+ ret = ENOMEM;
+ }
}
- if (parent != NULL)
+ if (parent != NULL &&
+ ret != ENOMEM)
{
/* Push parent. */
- /* If push fails, return ENOMEM. */
+ if (_pthread_handler_push(_PTHREAD_FORKPARENT_STACK,
+ _PTHREAD_HANDLER_POP_LIFO,
+ (void (*parent)(void *)), NULL) == ENOMEM)
+ {
+ ret = ENOMEM;
+ }
}
- if (child != NULL)
+ if (child != NULL &&
+ ret != ENOMEM)
{
/* Push child. */
- /* If push fails, return ENOMEM. */
+ if (_pthread_handler_push(_PTHREAD_FORKCHILD_STACK,
+ _PTHREAD_HANDLER_POP_LIFO,
+ (void (*child)(void *)), arg) == ENOMEM)
+ {
+ ret = ENOMEM;
+ }
+ }
+
+ if (ret == ENOMEM)
+ {
+ /* Pop all handlers without executing them before we return
+ the error. */
+ _pthread_handler_pop_all(_PTHREAD_FORKPREPARE_STACK,
+ _PTHREAD_HANDLER_NOEXECUTE);
+
+ _pthread_handler_pop_all(_PTHREAD_FORKPARENT_STACK,
+ _PTHREAD_HANDLER_NOEXECUTE);
+
+ _pthread_handler_pop_all(_PTHREAD_FORKCHILD_STACK,
+ _PTHREAD_HANDLER_NOEXECUTE);
}
- /* Everything is okay. */
- return 0;
+ return ret;
}
/* It looks like the GNU linker is capable of selecting this version of
@@ -44,19 +84,43 @@ fork()
pid_t pid;
/* Pop prepare handlers here. */
+ _pthread_handler_pop_all(_PTHREAD_FORKPREPARE_STACK,
+ _PTHREAD_HANDLER_EXECUTE);
/* Now call Cygwin32's fork(). */
if ((pid = _fork()) > 0)
{
- /* Pop parent handlers. */
+ /* PARENT */
+ /* Clear the child handler stack. */
+ _pthread_handler_pop_all(_PTHREAD_FORKCHILD_STACK,
+ _PTHREAD_HANDLER_NOEXECUTE);
+
+ /* Pop parent handlers and execute them. */
+ _pthread_handler_pop_all(_PTHREAD_FORKPARENT_STACK,
+ _PTHREAD_HANDLER_EXECUTE);
+
+ /* At this point all three atfork stacks are empty. */
return pid;
}
else
{
- /* Pop child handlers. */
+ /* CHILD */
+ /* Clear the parent handler stack. */
+ _pthread_handler_pop_all(_PTHREAD_FORKPARENT_STACK,
+ _PTHREAD_HANDLER_NOEXECUTE);
+
+ /* Pop child handlers and execute them. */
+ _pthread_handler_pop_all(_PTHREAD_FORKCHILD_STACK,
+ _PTHREAD_HANDLER_EXECUTE);
+
+ /* At this point all three atfork stacks are empty. */
+
/* Terminate all threads except pthread_self() using
pthread_cancel(). */
+ _pthread_cancel_all_not_self();
+ _pthread_join_all_not_self();
+
return 0;
}