Reputation: 11
Is there a way to cancel a boost::thread from another as in the following?:
boost::thread* thread1(0);
boost::thread* thread2(0);
thread2 = new boost::thread([&](){
//some expensive computation that can't be modified
if(thread1)
thread1->interrupt();
});
thread1 = new boost::thread([&]() {
//some other expensive computation that can't be modified
if(thread2)
thread2->interrupt();
});
thread1->join();
thread2->join();
delete thread1;
delete thread2;
Right now both expensive computations finish without being interrupted. I had figured the joins would be treated as an interruption point, and the main thread would continue after one of the two expensive computations completed.
Upvotes: 0
Views: 670
Reputation: 1893
A non-portable solution for POSIX-compliant systems (e.g. Linux) would be to use pthread_cancel()
and then pthread_join()
on the Boost thread's native_handle()
member, which is of type pthread_t
(again, only on POSIX-compliant systems. I can't speak for other systems, like Windows).
Also, you must use a boost::scoped_thread
instead of just a boost::thread
so that you can "override" (not in the OO-sense) the join/detach behavior that Boost will do when the thread is destroyed. This is necessary because when you call pthread_cancel
then pthread_join
on a boost::thread
, the boost::thread
object is still 'joinable' (i.e. boost::thread::joinable()
returns true), and so the destructor will exhibit undefined behavior, per the documentation.
With all that being said, if a platform-dependent solution for cancelling threads like this is necessary in your application, I'm not sure there's much to be gained from using boost::thread
s over plain-old pthread
s; still, I suppose there may be a use case for this.
Here's a code sample:
// compilation: g++ -pthread -I/path/to/boost/include -L/path/to/boost/libs -lboost_thread main.cpp
#include <cstdio>
#include <pthread.h>
#include <boost/thread/scoped_thread.hpp>
typedef struct pthreadCancelAndJoin
{
void operator()(boost::thread& t)
{
pthread_t pthreadId = t.native_handle();
int status = pthread_cancel(pthreadId);
printf("Cancelled thread %lu: got return value %d\n", pthreadId, status);
void* threadExitStatus;
status = pthread_join(pthreadId, &threadExitStatus);
printf("Joined thread %lu: got return value %d, thread exit status %ld\n",
pthreadId, status, (long)threadExitStatus);
}
} pthreadCancelAndJoin;
void foo()
{
printf("entering foo\n");
for(int i = 0; i < 2147483647; i++) printf("f"); // here's your 'expensive computation'
for(int i = 0; i < 2147483647; i++) printf("a");
printf("foo: done working\n"); // this won't execute
}
int main(int argc, char **argv)
{
boost::scoped_thread<pthreadCancelAndJoin> t1(foo);
pthread_t t1_pthread = t1.native_handle();
sleep(1); // give the thread time to get into its 'expensive computation';
// otherwise it'll likely be cancelled right away
// now, once main returns and t1's destructor is called, the pthreadCancelAndJoin
// functor object will be called, and so the underlying p_thread will be cancelled
// and joined
return 0;
}
pthread_cancel()
will cancel your thread when it reaches a "cancellation point" (assuming the cancel type and cancel state are at their default values, which is the case for boost::thread
objects); see the pthreads man page for a list of all cancellation points. You'll notice that those cancellation points include many of the more common system calls, like write
, read
, sleep
, send
, recv
, wait
, etc.
If your 'expensive computation' includes any of those calls down at its lowest level (e.g. in the code sample, printf
eventually calls write
), it will be cancelled.
Best of all, Valgrind reports no memory leaks or memory errors with this solution.
Finally, a note about your misconception in your question:
I had figured the joins would be treated as an interruption point...
join
, or any of the boost::thread
interruption functions, for that matter, is only treated as an interruption point for the thread that calls it. Since your main thread is calling join()
, the main thread is the thread that experiences the interruption point, not the thread that it is trying to join. E.g. if you call thread1.interrupt()
in some thread and then thread1
calls thread2.join()
, then thread1
is the one that gets interrupted.
Upvotes: 1
Reputation: 76381
In general, there is no portable way for one thread to terminate another, without cooperation from the thread being terminated. This question comes up once in a while, it seems (see here and here - although your question is not an exact duplicate).
Barring cooperation from the thread being interrupted (which would have to perform seppuku on notification), if you would like the main thread to continue after the first of the threads has terminated, you could make a condition
that each of the child threads fires when it ends.
At this point, you could either let the other thread continue running (possibly detach
ing it), or just terminate everything.
Upvotes: 5