0
votes

In the pthread library there is the concept of cancellation points. Most system functions that may block the execution for longer time (or wait on some ressources...) can be aborted by pthread cancellation points.

Guess there is some data protected by a condition variable that is executed in a thread like in pseudo code below. This thread has a setup cleanup procedure that is called in case a cancellation request is made to this thread.

THREAD_CLEANUP_PROC {
    UNLOCK(mutex) // Is this unlock required?
}

THREAD_PROC {
    SET THREAD_CLEANUP = THREAD_CLEANUP_PROC
    LOOP {
        LOCK(mutex)
        WHILE (condition == false) {
            condition.WAIT(mutex) // wait interrupted, cancel point is called
        }
        // ... we have the lock
        UNLOCK(mutex)
        condition.NOTIFY_ALL()

        read(descriptor); // wait for some data on a file descriptor while lock is not acquired
    }
}

If someone cancels the thread (pthread_cancel()) while waiting for the condition variable, the documentation about pthread_cond_wait says that the thread gets unblocked while acquiring the lock and start executing the cleanup handler before the thread ends.

Am I true that the cleanup handler is now responsible for unlocking that lock (mutex)? What if - like in my example - there is another blocking method like read that blocks while waiting for data but without acquiring the lock? In this case that read is also unblocked and the cleanup handler is called as if before. Only this time the cleanup handler shall not unlock the mutex. Am I correct. If so, what is the best way to handle this situation? Are there common concepts that should be followed?

2

2 Answers

2
votes

Thread cancellation is messy. Generally speaking, you should not do it.

In the pthread library there is the concept of cancellation points.

Yes.

Most system functions that may block the execution for longer time (or wait on some ressources...) can be aborted by pthread cancellation points.

Not exactly. Many functions such as you describe are cancellation points. A thread with "deferred" cancellation type will abort when it calls a function that is a cancellation point if it is currently cancellable and has a cancellation request pending. That does not imply that such a function can be interrupted by thread cancellation. Threads with "asynchronous" cancellation can be canceled at more or less any time, including when blocking on a long-running task, but cancellation points are irrelevant in that case.

If someone cancels the thread (pthread_cancel()) while waiting for the condition variable, the documentation about pthread_cond_wait says that the thread gets unblocked while acquiring the lock and start executing the cleanup handler before the thread ends.

Yes, provided that the thread has "deferred" cancellation type.

Am I true that the cleanup handler is now responsible for unlocking that lock (mutex)?

Yes. In this case, the thread holds the mutex locked when it commences its cancellation procedure. If it does not unlock the mutex before it terminates then at minimum you're in for a big hassle. Some types of mutexes (supported by pthreads) may provide for a way to recover from this situation, but you would do well to avoid it.

What if - like in my example - there is another blocking method like read that blocks while waiting for data but without acquiring the lock? In this case that read is also unblocked and the cleanup handler is called as if before. Only this time the cleanup handler shall not unlock the mutex. Am I correct.

Again, there are various types of mutex, and the situation may differ depending on which you use, but by far the best choice is to carefully avoid any thread trying to unlock a mutex that it does not hold locked.

If so, what is the best way to handle this situation?

The best way to handle the situation is to avoid it in the first place. Do not use thread cancellation, especially for threads susceptible to such issues, which are in fact common.

Instead, write multithreaded programs carefully to afford yourself alternative means to shut down threads or the whole program in a timely manner. There is a whole host of such techniques, more than I could reasonably summarize in an SO answer.

0
votes

Your code must be edited to look like:

THREAD_CLEANUP_PROC {
    UNLOCK(mutex) // Is this unlock required? YES
}

THREAD_PROC {
    LOOP {
        LOCK(mutex)
        SET THREAD_CLEANUP_PUSH = THREAD_CLEANUP_PROC // After adquire the lock
        WHILE (condition == false) {
            condition.WAIT(mutex) // wait interrupted, cancel point is called
        }
        // ... we have the lock
        THEAD_CLEANUP_POP(1) // This unlock the mutex and remove the cleanup
        // UNLOCK(mutex)
        condition.NOTIFY_ALL()

        read(descriptor); // wait for some data on a file descriptor while lock is not acquired
    }
}