
Anthony Williams wrote:
The interruption points are:
thread::join, thread::timed_join this_thread::sleep condition_variable::wait, condition_variable::timed_wait condition_variable_any::wait, condition_variable_any::timed_wait this_thread::interruption_point
Am I the only one who thinks that not having cancellation points at blocking I/O makes this fairly useless? Perhaps I am! I'm curious to hear about use-cases where Anthony or Preston's proposals are appropriate.
"Preston A. Elder" <prez@neuromancy.net> writes:
Third, interruption should also apply to locks - which would in part solve the above situation. The problem with this of course is that it is less efficient to use a mutex/condition crafted to work with interruptions at the lock level.
Yes, and that's why it doesn't happen. All the interruption points are where the thread is waiting for some event (e.g. a condition to be notified, a thread to finish, or a time to elapse). Locks should only be held for a short period, so blocking for a lock should not be a long wait. I don't agree that it should be an interruption point.
I think I agree with Preston. Yes, many locks are only held for short periods, but that's not the only way they can be used. One possibility would be to make lock acquisition a cancellation point iff that lock acquisition would block. This is straightforward with the futex code that I posted, and would not slow down the fast un-contended case. As I proposed when I presented my mutex benchmark results, it may make sense to offer different types of locks for applications with very low probability of contention and for non-trivial probabilities of contention. One application for cancellation is to cleanly kill off threads or groups of threads when something goes wrong. For example, I may observe no activity and assume that a deadlock has occurred. This would require that lock-acquisition be a cancellation point.
If you want to continue to offer interruption (which, by the way, I fully support!) - I believe it should be done one of two ways.
1) Use a signal on POSIX systems and find some equivalent mechanism on other systems. This will then actually mean what the user thinks - thread::interrupt() interrupts the thread, not just breaks out of a condition if it happens to be in one.
If a thread is waiting on a condition variable, a signal is not guaranteed to abort the wait:
"If a signal is delivered to a thread waiting for a condition variable, upon return from the signal handler the thread resumes waiting for the condition variable as if it was not interrupted, or it shall return zero due to spurious wakeup."
I think that, even if this problem could be avoided, delivering a signal to the right thread is very hard.
The only way I know to ensure that the thread being interrupted wakes from the condition variable is to broadcast the condition variable. The alternative is that boost::condition_variable does not actually use a pthread_cond_t in the implementation, and I'm not keen on that idea at all.
Interesting; I was planning to look at implementing conditions, bypassing the pthreads layer, as I did for my futex code. What have you discovered that has put you off that idea?
2) Move interruption to its own series of structures. Like I did, with an interruptable_mutex. The advantage of this is twofold: 1) It can be implemented in a non-platform specific manner, using existing data structures, so you implement the interruptable version once (utilizing boost::mutex and boost::condition) and you don't have to care about the underlying platform. This means it doesn't require any OS specific support. 2) It works exactly as the user thinks. An interruptable_mutex can be interrupted while blocked on something to do with it (meaning when trying to acquire a lock on it, OR when waiting on a condition with it).
I'm not keen on this approach, as it really does mean that the thread can only be interrupted at the defined points.
It's even further from being able to cancel at blocking I/O.... Can I suggest that we don't entirely forget about 3) use pthread_cancel It has some disadvantages, but it's the only solution that offers cancellation at blocking I/O. Regards, Phil.