diff options
Diffstat (limited to 'linuxthreads')
-rw-r--r-- | linuxthreads/ChangeLog | 18 | ||||
-rw-r--r-- | linuxthreads/internals.h | 4 | ||||
-rw-r--r-- | linuxthreads/linuxthreads.texi | 9 | ||||
-rw-r--r-- | linuxthreads/mutex.c | 38 | ||||
-rw-r--r-- | linuxthreads/ptfork.c | 4 |
5 files changed, 65 insertions, 8 deletions
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog index 8a0310dadb..551fbce21b 100644 --- a/linuxthreads/ChangeLog +++ b/linuxthreads/ChangeLog @@ -1,5 +1,23 @@ 2000-05-06 Kaz Kylheku <kaz@ashi.footprints.net> + * mutex.c (pthread_once): IN_PROGRESS state of pthread_once_t + object state is represented with additional bits which distinguish + whether that state was set up in the current process, or + in an ancestor process. If that state was set in an ancestor, + it means that a fork happened while thread was executing the init + function. In that case, the state is reset to NEVER. + * mutex.c (__pthread_once_fork_prepare): New function. + (__pthread_once_fork_child): Likewise + (__pthread_once_fork_parent): Likewise + (__pthread_reset_pthread_once): Removed. + * ptfork.c (__fork): Call new handlers in mutex.c. + * internals.h: Declarations of new mutex.c functions added. + Declaration of removed function deleted. + * linuxthreads.texi: Updated documentation about pthread_once + to clarify what happens under cancellation and forking. + +2000-05-06 Kaz Kylheku <kaz@ashi.footprints.net> + * internals.h: New thread manager request type, REQ_KICK. * join.c (pthread_exit): main thread now calls exit() instead of _exit() in order to proper process cleanup. diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h index e4cda4b66c..4d273f8917 100644 --- a/linuxthreads/internals.h +++ b/linuxthreads/internals.h @@ -414,7 +414,9 @@ int __pthread_manager(void *reqfd); int __pthread_manager_event(void *reqfd); void __pthread_manager_sighandler(int sig); void __pthread_reset_main_thread(void); -void __pthread_reset_pthread_once(void); +void __pthread_once_fork_prepare(void); +void __pthread_once_fork_parent(void); +void __pthread_once_fork_child(void); void __fresetlockfiles(void); void __pthread_manager_adjust_prio(int thread_prio); void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif); diff --git a/linuxthreads/linuxthreads.texi b/linuxthreads/linuxthreads.texi index 7a98103b3e..2b3647aed0 100644 --- a/linuxthreads/linuxthreads.texi +++ b/linuxthreads/linuxthreads.texi @@ -1368,6 +1368,15 @@ record that initialization has been performed. Subsequent calls to @code{pthread_once} with the same @code{once_control} argument do nothing. +If a thread is cancelled while executing @var{init_routine} +the state of the @var{once_control} variable is reset so that +a future call to @code{pthread_once} will call the routine again. + +If the process forks while one or more threads are executing +@code{pthread_once} initialization routines, the states of their respective +@var{once_control} variables will appear to be reset in the child process so +that if the child calls @code{pthread_once}, the routines will be executed. + @code{pthread_once} always returns 0. @end deftypefun diff --git a/linuxthreads/mutex.c b/linuxthreads/mutex.c index 4d9019ec4e..a42167cce0 100644 --- a/linuxthreads/mutex.c +++ b/linuxthreads/mutex.c @@ -17,6 +17,7 @@ #include <errno.h> #include <sched.h> #include <stddef.h> +#include <limits.h> #include "pthread.h" #include "internals.h" #include "spinlock.h" @@ -170,6 +171,7 @@ weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np) static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER; +static int fork_generation = 0; /* Child process increments this after fork. */ enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 }; @@ -199,14 +201,20 @@ int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void)) state_changed = 0; pthread_mutex_lock(&once_masterlock); + + /* If this object was left in an IN_PROGRESS state in a parent + process (indicated by stale generation field), reset it to NEVER. */ + if ((*once_control & 3) == IN_PROGRESS && (*once_control & ~3) != fork_generation) + *once_control = NEVER; + /* If init_routine is being called from another routine, wait until it completes. */ - while (*once_control == IN_PROGRESS) { + while ((*once_control & 3) == IN_PROGRESS) { pthread_cond_wait(&once_finished, &once_masterlock); } /* Here *once_control is stable and either NEVER or DONE. */ if (*once_control == NEVER) { - *once_control = IN_PROGRESS; + *once_control = IN_PROGRESS | fork_generation; pthread_mutex_unlock(&once_masterlock); pthread_cleanup_push(pthread_once_cancelhandler, once_control); init_routine(); @@ -225,13 +233,31 @@ int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void)) strong_alias (__pthread_once, pthread_once) /* - * This is called in the child process after a fork to make - * sure that the global mutex pthread_once is not held, - * and that the condition variable is reset to an initial state. + * Handle the state of the pthread_once mechanism across forks. The + * once_masterlock is acquired in the parent process prior to a fork to ensure + * that no thread is in the critical region protected by the lock. After the + * fork, the lock is released. In the child, the lock and the condition + * variable are simply reset. The child also increments its generation + * counter which lets pthread_once calls detect stale IN_PROGRESS states + * and reset them back to NEVER. */ -void __pthread_reset_pthread_once(void) +void __pthread_once_fork_prepare(void) +{ + pthread_mutex_lock(&once_masterlock); +} + +void __pthread_once_fork_parent(void) +{ + pthread_mutex_unlock(&once_masterlock); +} + +void __pthread_once_fork_child(void) { pthread_mutex_init(&once_masterlock, NULL); pthread_cond_init(&once_finished, NULL); + if (fork_generation <= INT_MAX - 4) + fork_generation += 4; /* leave least significant two bits zero */ + else + fork_generation = 0; } diff --git a/linuxthreads/ptfork.c b/linuxthreads/ptfork.c index 4cd883fd23..cb6d46db8c 100644 --- a/linuxthreads/ptfork.c +++ b/linuxthreads/ptfork.c @@ -86,14 +86,16 @@ pid_t __fork(void) parent = pthread_atfork_parent; pthread_mutex_unlock(&pthread_atfork_lock); pthread_call_handlers(prepare); + __pthread_once_fork_prepare(); pid = __libc_fork(); if (pid == 0) { __pthread_reset_main_thread(); - __pthread_reset_pthread_once(); __fresetlockfiles(); pthread_call_handlers(child); + __pthread_once_fork_child(); } else { pthread_call_handlers(parent); + __pthread_once_fork_parent(); } return pid; } |