diff options
Diffstat (limited to 'pthread')
-rw-r--r-- | pthread/pt-alloc.c | 3 | ||||
-rw-r--r-- | pthread/pt-cancel.c | 27 | ||||
-rw-r--r-- | pthread/pt-internal.h | 11 | ||||
-rw-r--r-- | pthread/pt-join.c | 2 | ||||
-rw-r--r-- | pthread/pt-setcancelstate.c | 2 | ||||
-rw-r--r-- | pthread/pt-setcanceltype.c | 2 | ||||
-rw-r--r-- | pthread/pt-testcancel.c | 7 |
7 files changed, 50 insertions, 4 deletions
diff --git a/pthread/pt-alloc.c b/pthread/pt-alloc.c index 6af2da9..89fca8a 100644 --- a/pthread/pt-alloc.c +++ b/pthread/pt-alloc.c @@ -55,6 +55,9 @@ initialize_pthread (struct __pthread *new, int recycling) if (err) return err; + new->cancel_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; + new->cancel_hook = NULL; + new->cancel_hook_arg = NULL; new->cancel_state = 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..96c77f7 100644 --- a/pthread/pt-cancel.c +++ b/pthread/pt-cancel.c @@ -31,10 +31,33 @@ pthread_cancel (pthread_t t) if (! p) return ESRCH; + __pthread_mutex_lock (&p->cancel_lock); + if (p->cancel_pending) + { + __pthread_mutex_unlock (&p->cancel_lock); + return 0; + } + p->cancel_pending = 1; - if (p->cancel_state == PTHREAD_CANCEL_ENABLE - && p->cancel_type == PTHREAD_CANCEL_ASYNCHRONOUS) + + if (p->cancel_state != PTHREAD_CANCEL_ENABLE) + { + __pthread_mutex_unlock (&p->cancel_lock); + return 0; + } + + if (p->cancel_type == PTHREAD_CANCEL_ASYNCHRONOUS) + /* CANCEL_LOCK is unlocked by this call. */ err = __pthread_do_cancel (p); + else + { + if (p->cancel_hook != NULL) + /* Thread blocking on a cancellation point. Invoke hook to unblock. + See __pthread_cond_timedwait_internal. */ + p->cancel_hook (p->cancel_hook_arg); + + __pthread_mutex_unlock (&p->cancel_lock); + } return err; } diff --git a/pthread/pt-internal.h b/pthread/pt-internal.h index 291baf5..aeac009 100644 --- a/pthread/pt-internal.h +++ b/pthread/pt-internal.h @@ -73,6 +73,11 @@ struct __pthread pthread_t thread; /* Cancellation. */ + pthread_mutex_t cancel_lock; /* Protect cancel_xxx members. */ + void (*cancel_hook)(void *); /* Called to unblock a thread blocking + in a cancellation point (namely, + __pthread_cond_timedwait_internal). */ + void *cancel_hook_arg; int cancel_state; int cancel_type; int cancel_pending; @@ -107,6 +112,8 @@ struct __pthread tcbhead_t *tcb; #endif /* ENABLE_TLS */ + /* Queue links. Since PREVP is used to determine if a thread has been + awaken, it must be protected by the queue lock. */ struct __pthread *next, **prevp; }; @@ -128,6 +135,7 @@ static inline void __pthread_dequeue (struct __pthread *thread) { assert (thread); + assert (thread->prevp); if (thread->next) thread->next->prevp = thread->prevp; @@ -264,7 +272,8 @@ extern error_t __pthread_timedblock (struct __pthread *__restrict thread, extern void __pthread_wakeup (struct __pthread *thread); -/* Perform a cancelation. */ +/* Perform a cancelation. The CANCEL_LOCK member of the given thread must + be locked before calling this function, which must unlock it. */ extern int __pthread_do_cancel (struct __pthread *thread); diff --git a/pthread/pt-join.c b/pthread/pt-join.c index 153058b..8bd2c6c 100644 --- a/pthread/pt-join.c +++ b/pthread/pt-join.c @@ -40,6 +40,8 @@ pthread_join (pthread_t thread, void **status) pthread_cleanup_push ((void (*)(void *)) __pthread_mutex_unlock, &pthread->state_lock); + /* Rely on pthread_cond_wait being a cancellation point to make + pthread_join one too. */ while (pthread->state == PTHREAD_JOINABLE) pthread_cond_wait (&pthread->state_cond, &pthread->state_lock); diff --git a/pthread/pt-setcancelstate.c b/pthread/pt-setcancelstate.c index 38550ee..7b60015 100644 --- a/pthread/pt-setcancelstate.c +++ b/pthread/pt-setcancelstate.c @@ -35,9 +35,11 @@ __pthread_setcancelstate (int state, int *oldstate) break; } + __pthread_mutex_lock (&p->cancel_lock); if (oldstate) *oldstate = p->cancel_state; p->cancel_state = state; + __pthread_mutex_unlock (&p->cancel_lock); return 0; } diff --git a/pthread/pt-setcanceltype.c b/pthread/pt-setcanceltype.c index 7226a3a..3cfbe9c 100644 --- a/pthread/pt-setcanceltype.c +++ b/pthread/pt-setcanceltype.c @@ -35,9 +35,11 @@ __pthread_setcanceltype (int type, int *oldtype) break; } + __pthread_mutex_lock (&p->cancel_lock); if (oldtype) *oldtype = p->cancel_type; p->cancel_type = type; + __pthread_mutex_unlock (&p->cancel_lock); return 0; } diff --git a/pthread/pt-testcancel.c b/pthread/pt-testcancel.c index 01f1ac9..3ba07b6 100644 --- a/pthread/pt-testcancel.c +++ b/pthread/pt-testcancel.c @@ -25,7 +25,12 @@ void pthread_testcancel (void) { struct __pthread *p = _pthread_self (); + int cancelled; - if (p->cancel_state == PTHREAD_CANCEL_ENABLE && p->cancel_pending) + __pthread_mutex_lock (&p->cancel_lock); + cancelled = (p->cancel_state == PTHREAD_CANCEL_ENABLE) && p->cancel_pending; + __pthread_mutex_unlock (&p->cancel_lock); + + if (cancelled) pthread_exit (PTHREAD_CANCELED); } |