I was thinking about what Richard Braun said about removing hurd_thread_cancel. Attached is an exploration into implementing PTHREAD_CANCEL_HURD as a cancellation state extension. It compiles, but I haven't tested if the resulting library still works.
A server would call pthread_hurd_server_np upon start-up, which irreversibly puts pthreads into hurd_server mode. All this really does is set the default cancellation state for new threads to be the new cancellation state. The function pthread_check_and_clear_cancel_np replaces hurd_check_cancel. The name is more verbose, but describes what the function does. pthread_testcancel doesn't return a value and I didn't want to change that. Disgustingly, I've reused state_lock to act as the lock for cancel_pending, so we don't accidentally lose any signals to cancel. There are interesting implications for this, however. pthread_join becomes a function that may eat a signal to cancel. The main implication is, essentially, that every occurrence of condition_wait becomes a call to hurd_condition_wait. I don't know if any of the calls to condition_wait weren't cancellation points for good reasons. Secondly, would this conflict with the proper functioning of the libraries? Does a utility that links to a hurd library have to declare itself a server to pthreads for the library to work properly (if the library expects these cancellation semantics)? This is just meant to look at and converse on. Thomas D PS. In timedwait, we dequeue the thread, but the cleanup handler (which is always run) ensures that the thread is dequeued also. Is this necessary, or just an artifact that is due to how all of the timed functions were implemented?
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 <pt-internal.h> + +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; +}