summaryrefslogtreecommitdiff
path: root/pthread
diff options
context:
space:
mode:
Diffstat (limited to 'pthread')
-rw-r--r--pthread/pt-alloc.c3
-rw-r--r--pthread/pt-cancel.c27
-rw-r--r--pthread/pt-internal.h11
-rw-r--r--pthread/pt-join.c2
-rw-r--r--pthread/pt-setcancelstate.c2
-rw-r--r--pthread/pt-setcanceltype.c2
-rw-r--r--pthread/pt-testcancel.c7
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);
}