diff --git a/Makefile b/Makefile index e8c77e3..9ec7863 100644 --- a/Makefile +++ b/Makefile @@ -137,6 +137,8 @@ libpthread-routines := pt-attr pt-attr-destroy pt-attr-getdetachstate \ pt-kill \ pt-getcpuclockid \ \ + pt-hurd-np \ + \ pt-getschedparam pt-setschedparam pt-setschedprio \ pt-yield \ \ diff --git a/include/pthread/pthread.h b/include/pthread/pthread.h index cd32fb2..dd41ba4 100644 --- a/include/pthread/pthread.h +++ b/include/pthread/pthread.h @@ -581,6 +581,7 @@ extern void pthread_cleanup_pop (int execute); #define PTHREAD_CANCEL_DISABLE 0 #define PTHREAD_CANCEL_ENABLE 1 +#define PTHREAD_CANCEL_HURD 2 /* Return the calling thread's cancelation state in *OLDSTATE and set its state to STATE. */ @@ -602,6 +603,9 @@ extern int pthread_cancel (pthread_t thread); /* Add an explicit cancelation point. */ extern void pthread_testcancel (void); + +/* Test for cancelation. */ +extern int pthread_check_and_clear_cancel_np (void); /* Barriers attributes. */ @@ -742,6 +746,10 @@ extern int pthread_setschedprio (pthread_t thread, int prio); might be differently implemented in the case of a m-on-n thread implementation. */ extern int pthread_yield (void); + +/* Set the current thread's cancelation state to PTHREAD_CANCEL_HURD + and force that to be the default cancelation state. */ +extern void pthread_hurd_server_np (void); #endif diff --git a/pthread/pt-alloc.c b/pthread/pt-alloc.c index 6af2da9..1f612e7 100644 --- a/pthread/pt-alloc.c +++ b/pthread/pt-alloc.c @@ -46,6 +46,11 @@ pthread_rwlock_t __pthread_threads_lock; struct __pthread *__pthread_free_threads; pthread_mutex_t __pthread_free_threads_lock; +/* POSIX specifies that the default cancel_state is always + PTHREAD_CANCEL_ENABLE, but for Hurd servers, we want to + change this to PTHREAD_CANCEL_HURD. */ +int __pthread_hurd_server; + static inline error_t initialize_pthread (struct __pthread *new, int recycling) { @@ -55,7 +60,8 @@ initialize_pthread (struct __pthread *new, int recycling) if (err) return err; - new->cancel_state = PTHREAD_CANCEL_ENABLE; + new->cancel_state + = __pthread_hurd_server ? PTHREAD_CANCEL_HURD : PTHREAD_CANCEL_ENABLE; new->cancel_type = PTHREAD_CANCEL_DEFERRED; new->cancel_pending = 0; diff --git a/pthread/pt-cancel.c b/pthread/pt-cancel.c index d19c557..4b589b7 100644 --- a/pthread/pt-cancel.c +++ b/pthread/pt-cancel.c @@ -31,10 +31,16 @@ pthread_cancel (pthread_t t) if (! p) return ESRCH; + __pthread_mutex_lock(&p->state_lock); p->cancel_pending = 1; + __pthread_mutex_unlock(&p->state_lock); if (p->cancel_state == PTHREAD_CANCEL_ENABLE && p->cancel_type == PTHREAD_CANCEL_ASYNCHRONOUS) err = __pthread_do_cancel (p); + if (p->cancel_state == PTHREAD_CANCEL_HURD + && p->cancelation_handlers) + p->cancelation_handlers->handler (p->cancelation_handlers->arg); + return err; } diff --git a/pthread/pt-internal.h b/pthread/pt-internal.h index 067fb73..02e0ee7 100644 --- a/pthread/pt-internal.h +++ b/pthread/pt-internal.h @@ -163,6 +163,11 @@ extern int __pthread_num_threads; /* Concurrency hint. */ extern int __pthread_concurrency; +/* POSIX specifies that the default cancel_state is always + PTHREAD_CANCEL_ENABLE, but for Hurd servers, we want to + change this to PTHREAD_CANCEL_HURD. */ +extern int __pthread_hurd_server; + /* Array of __pthread structures and its lock. Indexed by the pthread id minus one. (Why not just use the pthread id? Because some brain-dead users of the pthread interface incorrectly assume that 0 diff --git a/sysdeps/generic/pt-cond-timedwait.c b/sysdeps/generic/pt-cond-timedwait.c index 56eb1ec..baba5fe 100644 --- a/sysdeps/generic/pt-cond-timedwait.c +++ b/sysdeps/generic/pt-cond-timedwait.c @@ -45,8 +45,9 @@ __pthread_cond_timedwait_internal (pthread_cond_t *cond, const struct timespec *abstime) { error_t err; - int canceltype; + int canceltype, canceled; clockid_t clock_id = __pthread_default_condattr.clock; + void (*cleanup_handler)(void *); void cleanup (void *arg) { @@ -61,6 +62,31 @@ __pthread_cond_timedwait_internal (pthread_cond_t *cond, __pthread_mutex_lock (mutex); } + void cleanup_hurd (void *arg) + { + struct __pthread *self = arg; + + /* Either we called this function ourself at the end of the wait, + or someone has called it to wake us up. */ + if (self == _pthread_self()) + { + __pthread_spin_lock (&cond->__lock); + if (self->prevp) + __pthread_dequeue (self); + __pthread_spin_unlock (&cond->__lock); + + pthread_setcanceltype (canceltype, &canceltype); + __pthread_mutex_lock (mutex); + } + else + { + /* Don't bother waking the thread if it has been dequeued. + It means that someone else is waking it up. */ + if (self->prevp) + __pthread_wakeup (self); + } + } + if (abstime && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) return EINVAL; @@ -75,9 +101,22 @@ __pthread_cond_timedwait_internal (pthread_cond_t *cond, __pthread_mutex_unlock (mutex); + if (self->cancel_state != PTHREAD_CANCEL_HURD) + { + cleanup_handler = cleanup; + canceled = 0; + } + else + { + cleanup_handler = cleanup_hurd; + /* Don't lock, we're only polling. */ + canceled = self->cancel_pending; + } + + /* cleanup doesn't use arg, so this should be safe */ + pthread_cleanup_push (cleanup_handler, self); /* Enter async cancelation mode. If cancelation is disabled, then this does not change anything which is exactly what we want. */ - pthread_cleanup_push (cleanup, 0); pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &canceltype); if (abstime) @@ -102,10 +141,22 @@ __pthread_cond_timedwait_internal (pthread_cond_t *cond, else { err = 0; - __pthread_block (self); + /* If we're already canceled, don't block. */ + if (! canceled) + __pthread_block (self); } pthread_cleanup_pop (1); - return err; + if (self->cancel_state == PTHREAD_CANCEL_HURD) + { + __pthread_mutex_lock (&self->state_lock); + canceled |= self->cancel_pending; + self->cancel_pending = 0; + __pthread_mutex_unlock (&self->state_lock); + + return canceled; + } + else + return err; } diff --git a/sysdeps/hurd/pt-hurd-np.c b/sysdeps/hurd/pt-hurd-np.c new file mode 100644 index 0000000..7b1b188 --- /dev/null +++ b/sysdeps/hurd/pt-hurd-np.c @@ -0,0 +1,43 @@ +/* Hurd non-portable functions to establish cthread cancelation. + Copyright (C) 2012 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include + +int +pthread_check_and_clear_cancel_np (void) +{ + struct __pthread *p = _pthread_self(); + int cancel_pending; + + __pthread_mutex_lock(&p->state_lock); + cancel_pending = p->cancel_pending; + p->cancel_pending = 0; + __pthread_mutex_unlock(&p->state_lock); + + return cancel_pending; +} + +void +pthread_hurd_server_np (void) +{ + struct __pthread *p = _pthread_self(); + + p->cancel_state = PTHREAD_CANCEL_HURD; + __pthread_hurd_server = 1; +}