|
Threads-Devel : |
Subject: Re: [Threads-devel] boost::barrier - mechanism to deal exceptions
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2014-02-11 13:33:58
Le 11/02/14 14:12, Patrick Grace a écrit :
>
> 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.
>
>
Hi,
Interesting use case.
I have a question. What would you do when the task received a cascade
exception? The task is not completely finished as an exception has been
throw on the call to wait.
Vicente