summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2018-02-26 05:10:13 -0800
committerVito Caputo <vcaputo@pengaru.com>2018-02-26 05:18:59 -0800
commit29a6c98f522236a9abb9a8f2e83fdf76bc39c614 (patch)
tree887694954d3f60a33e35196f62e8436a4173fa2b /src
parent6fabe39ba8841ffe392c9b659505e12edaff43d6 (diff)
threads: fix cancellation on destroy
Depending on where cancellation was happening, locks were potentially left held which could result in the next thread being cancelled deadlocking. In deferred cancellation, only cancellation points realize the cancellation. So one worker thread could realize the cancellation entering say, pthread_cond_wait, and exit with the associated mutex still held. The other thread could be in the process of returning from pthread_cond_wait - past the cancellation point already, and get stuck in trying to acquire the mutex as pthread_cond_wait does before returning, because the lock was left held by the other thread. Instead, use the cleanup handlers to unlock the mutexes, and enable asynchronous cancellation. This seems to eliminate the observed occasional deadlocks on destroy.
Diffstat (limited to 'src')
-rw-r--r--src/threads.c12
1 files changed, 8 insertions, 4 deletions
diff --git a/src/threads.c b/src/threads.c
index 0ffbfb5..77ff7d1 100644
--- a/src/threads.c
+++ b/src/threads.c
@@ -31,17 +31,20 @@ typedef struct threads_t {
/* render fragments using the supplied render function */
static void * thread_func(void *_threads)
{
- threads_t *threads = _threads;
- unsigned prev_frame_num = 0;
+ threads_t *threads = _threads;
+ unsigned prev_frame_num = 0;
+
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
for (;;) {
/* wait for a new frame */
pthread_mutex_lock(&threads->frame_mutex);
+ pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, &threads->frame_mutex);
while (threads->frame_num == prev_frame_num)
pthread_cond_wait(&threads->frame_cond, &threads->frame_mutex);
prev_frame_num = threads->frame_num;
- pthread_mutex_unlock(&threads->frame_mutex);
+ pthread_cleanup_pop(1);
/* render fragments */
for (;;) {
@@ -58,10 +61,11 @@ static void * thread_func(void *_threads)
/* report as idle */
pthread_mutex_lock(&threads->idle_mutex);
+ pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, &threads->idle_mutex);
threads->n_idle++;
if (threads->n_idle == threads->n_threads) /* Frame finished! Notify potential waiter. */
pthread_cond_signal(&threads->idle_cond);
- pthread_mutex_unlock(&threads->idle_mutex);
+ pthread_cleanup_pop(1);
}
return NULL;
© All Rights Reserved