Thread Interruption in the Boost Thread Library
Tuesday, 11 March 2008
One of the new features introduced in the upcoming 1.35.0 release of the boost thread library is support for interruption of a running thread. Similar to the Java and .NET interruption support, this allows for one thread to request another thread to stop at the next interruption point. This is the only way to explicitly request a thread to terminate that is directly supported by the Boost Thread library, though users can manually implement cooperative interruption if required.
Interrupting a thread in this way is much less dangerous than brute-force tactics such as
TerminateThread()
, as such tactics can leave broken invariants and leak resources. If a thread is killed using a
brute-force method and it was holding any locks, this can also potentially lead to deadlock when another thread tries to acquire
those locks at some future point. Interruption is also easier and more reliable than rolling your own cooperative termination scheme
using mutexes, flags, condition variables, or some other synchronization mechanism, since it is part of the library.
Interrupting a Thread
A running thread can be interrupted by calling the interrupt()
member function on the corresponding
boost::thread
object. If the thread doesn't have a boost::thread
object (e.g the initial thread of the
application), then it cannot be interrupted.
Calling interrupt()
just sets a flag in the thread management structure for that thread and returns: it doesn't wait
for the thread to actually be interrupted. This is important, because a thread can only be interrupted at one of the predefined
interruption points, and it might be that a thread never executes an interruption point, so never sees the
request. Currently, the interruption points are:
boost::thread::join()
boost::thread::timed_join()
boost::condition_variable::wait()
boost::condition_variable::timed_wait()
boost::condition_variable_any::wait()
boost::condition_variable_any::timed_wait()
boost::this_thread::sleep()
boost::this_thread::interruption_point()
When a thread reaches one of these interruption points, if interruption is enabled for that thread then it checks its
interruption flag. If the flag is set, then it is cleared, and a boost::thread_interrupted
exception is thrown. If the
thread is already blocked on a call to one of the interruption points with interruption enabled when interrupt()
is
called, then the thread will wake in order to throw the boost::thread_interrupted
exception.
Catching an Interruption
boost::thread_interrupted
is just a normal exception, so it can be caught, just like any other exception. This is
why the "interrupted" flag is cleared when the exception is thrown — if a thread catches and handles the interruption, it is
perfectly acceptable to interrupt it again. This can be used, for example, when a worker thread that is processing a series of
independent tasks — if the current task is interrupted, the worker can handle the interruption and discard the task, and move
onto the next task, which can then in turn be interrupted. It also allows the thread to catch the exception and terminate itself by
other means, such as returning error codes, or translating the exception to pass through module boundaries.
Disabling Interruptions
Sometimes it is necessary to avoid being interrupted for a particular section of code, such as in a destructor where an exception
has the potential to cause immediate process termination. This is done by constructing an instance of
boost::this_thread::disable_interruption
. Objects of this class disable interruption for the thread that created them on
construction, and restore the interruption state to whatever it was before on destruction:
void f() { // interruption enabled here { boost::this_thread::disable_interruption di; // interruption disabled { boost::this_thread::disable_interruption di2; // interruption still disabled } // di2 destroyed, interruption state restored // interruption still disabled } // di destroyed, interruption state restored // interruption now enabled }
The effects of an instance of boost::this_thread::disable_interruption
can be temporarily reversed by constructing
an instance of boost::this_thread::restore_interruption
, passing in the
boost::this_thread::disable_interruption
object in question. This will restore the interruption state to what it was
when the boost::this_thread::disable_interruption
object was constructed, and then disable interruption again when the
boost::this_thread::restore_interruption
object is destroyed:
void g() { // interruption enabled here { boost::this_thread::disable_interruption di; // interruption disabled { boost::this_thread::restore_interruption ri(di); // interruption now enabled } // ri destroyed, interruption disabled again { boost::this_thread::disable_interruption di2; // interruption disabled { boost::this_thread::restore_interruption ri2(di2); // interruption still disabled // as it was disabled when di2 constructed } // ri2 destroyed, interruption still disabled } //di2 destroyed, interruption still disabled } // di destroyed, interruption state restored // interruption now enabled }
boost::this_thread::disable_interruption
and boost::this_thread::restore_interruption
cannot be moved
or copied, and they are the only way of enabling and disabling interruption. This ensures that the interruption state is correctly
restored when the scope is exited (whether normally, or by an exception), and that you cannot enable interruptions in the middle of
an interruption-disabled block unless you're in full control of the code, and have access to the
boost::this_thread::disable_interruption
instance.
At any point, the interruption state for the current thread can be queried by calling
boost::this_thread::interruption_enabled()
.
Cooperative Interruption
As well as the interruption points on blocking operations such as sleep()
and join()
, there is one
interruption point explicitly designed to allow interruption at a user-designated point in the
code. boost::this_thread::interruption_point()
does nothing except check for an interruption, and can therefore be used
in long-running code that doesn't execute any other interruption points, in order to allow for cooperative interruption. Just like
the other interruption points, interruption_point()
respects the interruption enabled state, and does nothing if
interruption is disabled for the current thread.
Interruption is Not Cancellation
On POSIX platforms, threads can be cancelled rather than killed, by calling pthread_cancel()
. This is similar to interruption, but is a separate mechanism, with different behaviour. In particular,
cancellation cannot be stopped once it is started: whereas interruption just throws an exception, once a cancellation request has
been acknowledged the thread is effectively dead. pthread_cancel()
does not always execute destructors either (though
it does on some platforms), as it is primarily a C interface — if you want to clean up your resources when a thread is
cancelled, you need to use pthread_cleanup_push()
to register a cleanup handler. The advantage here is that
pthread_cleanup_push()
works in C stack frames, whereas exceptions don't play nicely in C: on some platforms it will
crash your program for an exception to propagate into a C stack frame.
For portable code, I recommend interruption over cancellation. It's supported on all platforms that can use the Boost Thread library, and it works well with C++ code — it's just another exception, so all your destructors and catch blocks work just fine.
Posted by Anthony Williams
[/ threading /] permanent link
Tags: thread, boost, interruption, concurrency, cancellation, multi-threading
Stumble It! | Submit to Reddit | Submit to DZone
If you liked this post, why not subscribe to the RSS feed or Follow me on Twitter? You can also subscribe to this blog by email using the form on the left.
Design and Content Copyright © 2005-2024 Just Software Solutions Ltd. All rights reserved. | Privacy Policy
6 Comments
please give a code using c programming language that is running that is using interruption and if you want to continue then it will continue
Interruption is a boost.thread feature, and as such is C++ only. Anyway, here's an example that allows continuation:
void do_stuff()
{
bool done=false;
while(!done)
{
try {
process_data();
done=processing_complete();
}
catch(boost::thread_interrupted const&)
{} // catch and ignore interruption
}
}
If you want to make the continuation conditional then you could re-throw the exception in the catch block if you *do* want to be interrupted.
In the adopted standard C++11 in the class std::thread no mechanism for safe termination of flow of the same interrupt boost. What is proposed to solve this problem in the new standard C++11, especially interrupt the flow is in sleep?
Nice post, thanks a lot, that was very functional for me.
thanks for the tips...very useful, keep them coming
Is .interrupt() gonna be part of the TR2 library ?