Boost logo

Threads-Devel :

Subject: [Threads-devel] boost::barrier - mechanism to deal exceptions
From: Patrick Grace (patrick.grace_at_[hidden])
Date: 2014-02-11 08:12:20


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.

 



Threads-Devel list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk