aboutsummaryrefslogtreecommitdiff
path: root/linuxthreads/pthread.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2000-01-05 02:09:12 +0000
committerUlrich Drepper <drepper@redhat.com>2000-01-05 02:09:12 +0000
commit1d2fc9b3c59d0e83e04139ddf633731264b76ea2 (patch)
treec738cf2a40851dc25be2c252ba5dbb7f335b5e14 /linuxthreads/pthread.c
parentf19f2b34439145daf300bf12789bbc61c8d4db28 (diff)
downloadglibc-1d2fc9b3c59d0e83e04139ddf633731264b76ea2.tar
glibc-1d2fc9b3c59d0e83e04139ddf633731264b76ea2.tar.gz
glibc-1d2fc9b3c59d0e83e04139ddf633731264b76ea2.tar.bz2
glibc-1d2fc9b3c59d0e83e04139ddf633731264b76ea2.zip
Redesigned how cancellation unblocks a thread from internal cancellation points (sem_wait, pthread_join, pthread_cond_{wait,timedwait}). Cancellation won't eat a signal in any of these functions (*required* by POSIX and Single Unix Spec!).
2000-01-03 Kaz Kylheku <kaz@ashi.footprints.net> Redesigned how cancellation unblocks a thread from internal cancellation points (sem_wait, pthread_join, pthread_cond_{wait,timedwait}). Cancellation won't eat a signal in any of these functions (*required* by POSIX and Single Unix Spec!). * condvar.c: spontaneous wakeup on pthread_cond_timedwait won't eat a simultaneous condition variable signal (not required by POSIX or Single Unix Spec, but nice). * spinlock.c: __pthread_lock queues back any received restarts that don't belong to it instead of assuming ownership of lock upon any restart; fastlock can no longer be acquired by two threads simultaneously. * restart.h: restarts queue even on kernels that don't have queued real time signals (2.0, early 2.1), thanks to atomic counter, avoiding a rare race condition in pthread_cond_timedwait.
Diffstat (limited to 'linuxthreads/pthread.c')
-rw-r--r--linuxthreads/pthread.c66
1 files changed, 65 insertions, 1 deletions
diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c
index dfde08f432..f082e95478 100644
--- a/linuxthreads/pthread.c
+++ b/linuxthreads/pthread.c
@@ -43,6 +43,7 @@ struct _pthread_descr_struct __pthread_initial_thread = {
0, /* int p_priority */
&__pthread_handles[0].h_lock, /* struct _pthread_fastlock * p_lock */
0, /* int p_signal */
+ ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */
NULL, /* sigjmp_buf * p_signal_buf */
NULL, /* sigjmp_buf * p_cancel_buf */
0, /* char p_terminated */
@@ -55,6 +56,8 @@ struct _pthread_descr_struct __pthread_initial_thread = {
0, /* char p_cancelstate */
0, /* char p_canceltype */
0, /* char p_canceled */
+ 0, /* char p_woken_by_cancel */
+ NULL, /* struct pthread_extricate_if *p_extricate */
NULL, /* int *p_errnop */
0, /* int p_errno */
NULL, /* int *p_h_errnop */
@@ -86,6 +89,7 @@ struct _pthread_descr_struct __pthread_manager_thread = {
0, /* int p_priority */
&__pthread_handles[1].h_lock, /* struct _pthread_fastlock * p_lock */
0, /* int p_signal */
+ ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */
NULL, /* sigjmp_buf * p_signal_buf */
NULL, /* sigjmp_buf * p_cancel_buf */
0, /* char p_terminated */
@@ -98,6 +102,8 @@ struct _pthread_descr_struct __pthread_manager_thread = {
0, /* char p_cancelstate */
0, /* char p_canceltype */
0, /* char p_canceled */
+ 0, /* char p_woken_by_cancel */
+ NULL, /* struct pthread_extricate_if *p_extricate */
&__pthread_manager_thread.p_errno, /* int *p_errnop */
0, /* int p_errno */
NULL, /* int *p_h_errnop */
@@ -144,6 +150,12 @@ char *__pthread_manager_thread_tos = NULL;
int __pthread_exit_requested = 0;
int __pthread_exit_code = 0;
+/* Pointers that select new or old suspend/resume functions
+ based on availability of rt signals. */
+
+void (*__pthread_restart)(pthread_descr) = __pthread_restart_old;
+void (*__pthread_suspend)(pthread_descr) = __pthread_suspend_old;
+
/* Communicate relevant LinuxThreads constants to gdb */
const int __pthread_threads_max = PTHREAD_THREADS_MAX;
@@ -215,13 +227,18 @@ init_rtsigs (void)
__pthread_sig_cancel = SIGUSR2;
__pthread_sig_debug = 0;
#endif
+ __pthread_init_condvar(0);
}
else
{
#if __SIGRTMAX - __SIGRTMIN >= 3
current_rtmin = __SIGRTMIN + 3;
+ __pthread_restart = __pthread_restart_new;
+ __pthread_suspend = __pthread_wait_for_restart_signal;
+ __pthread_init_condvar(1);
#else
current_rtmin = __SIGRTMIN;
+ __pthread_init_condvar(0);
#endif
current_rtmax = __SIGRTMAX;
@@ -447,7 +464,7 @@ int __pthread_initialize_manager(void)
raise(__pthread_sig_debug);
/* We suspend ourself and gdb will wake us up when it is
ready to handle us. */
- suspend(thread_self());
+ __pthread_wait_for_restart_signal(thread_self());
}
/* Synchronize debugging of the thread manager */
request.req_kind = REQ_DEBUG;
@@ -770,6 +787,53 @@ int __pthread_getconcurrency(void)
}
weak_alias (__pthread_getconcurrency, pthread_getconcurrency)
+void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif)
+{
+ __pthread_lock(self->p_lock, self);
+ THREAD_SETMEM(self, p_extricate, peif);
+ __pthread_unlock(self->p_lock);
+}
+
+/* Primitives for controlling thread execution */
+
+void __pthread_wait_for_restart_signal(pthread_descr self)
+{
+ sigset_t mask;
+
+ sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
+ sigdelset(&mask, __pthread_sig_restart); /* Unblock the restart signal */
+ do {
+ self->p_signal = 0;
+ sigsuspend(&mask); /* Wait for signal */
+ } while (self->p_signal !=__pthread_sig_restart );
+}
+
+/* The _old variants are for 2.0 and early 2.1 kernels which don't have RT signals.
+ On these kernels, we use SIGUSR1 and SIGUSR2 for restart and cancellation.
+ Since the restart signal does not queue, we use an atomic counter to create
+ queuing semantics. This is needed to resolve a rare race condition in
+ pthread_cond_timedwait_relative. */
+
+void __pthread_restart_old(pthread_descr th)
+{
+ if (atomic_increment(&th->p_resume_count) == -1)
+ kill(th->p_pid, __pthread_sig_restart);
+}
+
+void __pthread_suspend_old(pthread_descr self)
+{
+ if (atomic_decrement(&self->p_resume_count) <= 0)
+ __pthread_wait_for_restart_signal(self);
+}
+
+void __pthread_restart_new(pthread_descr th)
+{
+ kill(th->p_pid, __pthread_sig_restart);
+}
+
+/* There is no __pthread_suspend_new because it would just
+ be a wasteful wrapper for __pthread_wait_for_restart_signal */
+
/* Debugging aid */
#ifdef DEBUG