From the down attached source code (Linux kernel 2.6.18.6)
Code:
static inline void suspend_with_cancellation(pthread_descr self)
{
sigset_t mask;
sigjmp_buf jmpbuf;
sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
sigdelset(&mask, __pthread_sig_restart); /* Unblock the restart signal */
/* No need to save the signal mask, we'll restore it ourselves */
if (sigsetjmp(jmpbuf, 0) == 0) {
self->p_cancel_jmp = &jmpbuf;
if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
do {
self->p_signal = 0;
sigsuspend(&mask); /* Wait for a signal */
} while (self->p_signal != __pthread_sig_restart);//sig_restart is sent by pthread_cond_broadcast() or pthread_cond_signal() //it is not clear if a cancel unblocks or not //it seems no?!
}
self->p_cancel_jmp = NULL;
} else {
sigaddset(&mask, __pthread_sig_restart); /* Reblock the restart signal */
sigprocmask(SIG_SETMASK, &mask, NULL);
}
}
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
volatile pthread_descr self = thread_self();
__pthread_lock(&cond->__c_lock, self);
enqueue(&cond->__c_waiting, self);
__pthread_unlock(&cond->__c_lock);
pthread_mutex_unlock(mutex);
suspend_with_cancellation(self);
/* This is a cancellation point */
if (THREAD_GETMEM(self, p_canceled)
&& THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
/* Remove ourselves from the waiting queue if we're still on it */
__pthread_lock(&cond->__c_lock, self);
remove_from_queue(&cond->__c_waiting, self);
__pthread_unlock(&cond->__c_lock);
pthread_exit(PTHREAD_CANCELED);
}
return 0;
}
it seems that pthread_cond_wait() has a little bit strange behaviour:
(1) if a pthread_cancel is received before executing pthread_cond_wait -> the mutex is locked and immediately cancelation is executed (without waiting for a pthread_cond_broadcast/signal() )
(2) if a pthread_cancel is received during execution of pthread_cond_wait it depends:
(2.1) if cancel is recieved inside suspend_with_cancellation before do{}while
same as (1)
(2.2) if cancel is received during do{}while , the thread keeps blocked-waiting for a pthread_cond_broadcast/signal(). Only after another thread has sent pthread_cond_broadcast/signal() this thread will go on and execute cancelation!
Is it right?
This means that in the following 3 examples only the first is safe: (please tell me if you agree)
Code:
(1)
Thread 1
pthread_mutex_lock(mutex);
pthread_cleanup_push(pthread_mutex_unlock,mutex);
while (...) pthread_cond_wait(cond,mutex);
......
pthread_cleanup_pop(0);
pthread_mutex_unlock(mutex);
Thread 2
pthread_mutex_lock(mutex);
....
pthread_cond_broadcast(cond);
pthread_mutex_unlock(mutex);
(2)
Thread 1
pthread_mutex_lock(mutex);
pthread_cleanup_push(pthread_mutex_unlock,mutex);
while (boolean condition) pthread_cond_wait(cond,mutex);
......
pthread_cleanup_pop(0);
pthread_mutex_unlock(mutex);
Thread 2
pthread_mutex_lock(mutex);
....
if ( !boolean condition) pthread_cond_broadcast(cond);
pthread_mutex_unlock(mutex);
(3)
(1)
Thread 1
pthread_mutex_lock(mutex);
pthread_cleanup_push(pthread_mutex_unlock,mutex);
while (...) pthread_cond_wait(cond,mutex);
......
pthread_cleanup_pop(0);
pthread_mutex_unlock(mutex);
Thread 2
pthread_mutex_lock(mutex);
....
some function which is a cancelation point
....
pthread_cond_broadcast(cond);
pthread_mutex_unlock(mutex);