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