diff options
Diffstat (limited to 'libthreads')
-rw-r--r-- | libthreads/cancel-cond.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/libthreads/cancel-cond.c b/libthreads/cancel-cond.c new file mode 100644 index 00000000..8a55fba4 --- /dev/null +++ b/libthreads/cancel-cond.c @@ -0,0 +1,112 @@ +/* Modified condition_wait that checks for cancellation. +Copyright (C) 1995 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., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <hurd/signal.h> +#include <cthreads.h> +#include "cthread_internals.h" + +int +hurd_condition_wait_cancel (condition_t c, mutex_t m) +{ + /* This function will be called by hurd_thread_cancel while we are blocked + in the condition_wait. We wake up all threads blocked on C, + so our thread will wake up and notice the cancellation flag. */ + void cancel_me (void) + { + condition_broadcast (c); + } + struct hurd_sigstate *ss = _hurd_self_sigstate (); + cproc_t p = cproc_self (); + int cancel; + + p->state = CPROC_CONDWAIT | CPROC_SWITCHING; + + /* Atomically enqueue our cproc on the condition variable's queue of + waiters, and mark our sigstate to indicate that `cancel_me' must be + called to wake us up. We must hold the sigstate lock while acquiring + the condition variable's lock and tweaking it, so that + hurd_thread_cancel can never suspend us and then deadlock in + condition_broadcast waiting for the condition variable's lock. */ + + spin_lock (&ss->lock); + spin_lock (&c->lock); + cancel = ss->cancel; + if (cancel) + /* We were cancelled before doing anything. Don't block at all. */ + ss->cancel = 0; + else + { + /* Put us on the queue so that condition_broadcast will know to wake + us up. */ + cthread_queue_enq (&c->queue, p); + /* Tell hurd_thread_cancel how to unblock us. */ + ss->cancel_hook = &cancel_me; + } + spin_unlock (&c->lock); + spin_unlock (&ss->lock); + + if (cancel) + { + /* Cancelled on entry. Just leave the mutex locked. */ + m = NULL; + p->state = CPROC_RUNNING; + } + else + { + /* Now unlock the mutex and block until woken. */ + +#ifdef WAIT_DEBUG + p->waiting_for = (char *)c; +#endif WAIT_DEBUG + + mutex_unlock (m); + + spin_lock (&p->lock); + if (p->state & CPROC_SWITCHING) + cproc_block (); + else + { + /* We were woken up someplace before reacquiring P->lock. + We can just continue on. */ + p->state = CPROC_RUNNING; + spin_unlock(&p->lock); + } + +#ifdef WAIT_DEBUG + p->waiting_for = (char *)0; +#endif WAIT_DEBUG + } + + spin_lock (&ss->lock); + /* Clear the hook, now that we are done blocking. */ + ss->cancel_hook = NULL; + /* Check the cancellation flag; we might have unblocked due to + cancellation rather than a normal condition_signal or + condition_broadcast (or we might have just happened to get cancelled + right after waking up). */ + cancel |= ss->cancel; + ss->cancel = 0; + spin_unlock (&ss->lock); + + if (m) + /* Reacquire the mutex and return. */ + mutex_lock (m); + + return cancel; +} |