|
Threads-Devel : |
Subject: Re: [Threads-devel] threads-devel Digest, Vol 69, Issue 3
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2014-02-19 20:04:40
Le 19/02/14 18:35, Patrick Grace a écrit :
> threads-devel_at_[hidden]
>
>> -----Original Message-----
>> From: threads-devel [mailto:threads-devel-bounces_at_[hidden]] On
> Behalf
>> Of threads-devel-request_at_[hidden]
>> Sent: 12 February 2014 17:00
>> To: threads-devel_at_[hidden]
>> Subject: threads-devel Digest, Vol 69, Issue 3
>>
>> Send threads-devel mailing list submissions to
>> threads-devel_at_[hidden]
>>
>> To subscribe or unsubscribe via the World Wide Web, visit
>> http://lists.boost.org/mailman/listinfo.cgi/threads-devel
>> or, via email, send a message with subject or body 'help' to
>> threads-devel-request_at_[hidden]
>>
>> You can reach the person managing the list at
>> threads-devel-owner_at_[hidden]
>>
>> When replying, please edit your Subject line so it is more specific than
> "Re:
>> Contents of threads-devel digest..."
>>
>>
>> Today's Topics:
>>
>> 1. Re: boost::barrier - mechanism to deal exceptions
>> (Vicente J. Botet Escriba)
>>
>>
>> ----------------------------------------------------------------------
>>
>> Message: 1
>> Date: Tue, 11 Feb 2014 19:33:58 +0100
>> From: "Vicente J. Botet Escriba" <vicente.botet_at_[hidden]>
>> To: "Discussions about the boost.thread library"
>> <threads-devel_at_[hidden]>
>> Subject: Re: [Threads-devel] boost::barrier - mechanism to deal
>> exceptions
>> Message-ID: <52FA6D16.2080209_at_[hidden]>
>> Content-Type: text/plain; charset="iso-8859-1"; Format="flowed"
>>
>> 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
>
> In my case I am happy to let all threads exit their respective task if one
> exception is thrown. The purpose of the cascade exception is just to free
> up any waiting threads and get them to jump over the now-meaningless
> data-merging operation that I do after the barrier. Once the cascade
> exception is caught I allow the task to exit without doing anything else as
> the originating exception will be dealt with outside the task. (In my case
> I have all threads running the same task with different data.) Does that
> answer your question?
>
>
Yes.
An alternative is to use an vector of future<void> and use when_all() [1]
Another would be to have a task_group [2] or a task_region [3]
Would some of these alternatives cover your use case? What would you prefer?
Best,
Vicente
[1] http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3857.pdf
[2] http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3711.pdf
[3] http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3832.pdf