Hi
I had built my own thread aligner before I found out about boost::barrier when watching Rob Stewart’s talk at BoostCon 2013. Then I compared my thread aligner with boost::barrier and found they did the same thing. boost::barrier had the advantage of being reusable but mine had a mechanism to deal with exceptions (to prevent threads waiting on a thread that will never arrive because it has thrown an exception). I combined the 2 versions together and this is what I came up with:
class cascade_exception {}; // used to release threads waiting in thread_barrier::wait() if an exception occurs in another thread
class thread_barrier {
// use to align threads similar to boost::barrier, it may be reused
unsigned counter_;
unsigned counter_init_;
unsigned generation_{};
bool exception_flag_{false};
std::mutex mutex_;
std::condition_variable cv_;
public:
thread_barrier(const thread_barrier&) = delete;
thread_barrier& operator=(const thread_barrier&) = delete;
explicit thread_barrier(unsigned n=0) : counter_(n), counter_init_(n) {}
void set_nthreads(unsigned n) {
counter_ = counter_init_ = n;
exception_flag_ = false;
generation_ = 0;
}
void wait() {
std::unique_lock < std::mutex > lock(mutex_);
unsigned gen = generation_;
if (--counter_ == 0) {
++generation_;
counter_ = counter_init_;
cv_.notify_all();
return;
}
while (gen == generation_) {
if ( exception_flag_ ) throw cascade_exception{};
cv_.wait(lock);
}
}
void setExceptionFlag() {
std::unique_lock < std::mutex > lock(mutex_);
exception_flag_ = true;
cv_.notify_all();
}
};
I have highlighted all the parts that deal with exception handling. This is how I use it:
void task(thread_barrier& barrier) {
try {
// ….
barrier.wait();
// ….
} catch ( cascade_exception& ) {
// do nothing
} catch (...) {
barrier.setExceptionFlag();
throw; // transfers the exception to the promise/future
}
}
If one thread throws an exception (other than cascade_exception) it should be caught before the thread is ended and setExceptionFlag() is called which causes all the other threads to throw a cascade_exception. All threads can now end gracefully and the original exception gets thrown when future::get() is called (I use std::packaged_task(task) … ).
Please feel free to add this exception handling mechanism to boost::barrier. I think it needs some sort of exception handling.
Cheers
Patrick Grace
University College Dublin
PS I am a newbie to thread programming so I will not be surprised if there are better ways to do this.