[Boost.thread] Exception handling strategy ?
Hi there, is there a recommended strategy for exception (or more generally error-)handling with Boost.thread ? From what I can see it is not safe to throw an exception from within a thread, as it is not sure who catches it (or whether it gets caught at all). Best, Ruediger
on Wed Jul 16 2008, Ruediger Berlich
Hi there,
is there a recommended strategy for exception (or more generally error-)handling with Boost.thread ? From what I can see it is not safe to throw an exception from within a thread, as it is not sure who catches it (or whether it gets caught at all).
You can throw as long as the exception will be caught before it propagates out of the thread function. You can always put a try/catch(...) block in all your thread functions. -- Dave Abrahams BoostPro Computing http://www.boostpro.com
Yet to be released Boost 1.36 includes the new Exception library which
among other things supports transporting of exceptions between
threads.
Emil Dotchevski
Reverge Studios, Inc.
http://www.revergestudios.com/reblog/index.php?n=ReCode
On Wed, Jul 16, 2008 at 10:41 AM, Ruediger Berlich
Hi there,
is there a recommended strategy for exception (or more generally error-)handling with Boost.thread ? From what I can see it is not safe to throw an exception from within a thread, as it is not sure who catches it (or whether it gets caught at all).
Best, Ruediger
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
"Emil Dotchevski"
Yet to be released Boost 1.36 includes the new Exception library which among other things supports transporting of exceptions between threads.
This is true. However, users need to be aware that there are currently no mechanisms in place with the thread library to do this automatically: if you want to transport exceptions you need to catch them with boost.Exception yourself, and then transport them. My futures library (http://www.justsoftwaresolutions.co.uk/threading/updated-implementation-of-c...) which is on the Boost review queue provides a packaged_task which can be used with boost.thread to transport exceptions to the thread that is waiting for the result. Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
On Wed, Jul 16, 2008 at 1:20 PM, Anthony Williams
"Emil Dotchevski"
writes: Yet to be released Boost 1.36 includes the new Exception library which among other things supports transporting of exceptions between threads.
This is true. However, users need to be aware that there are currently no mechanisms in place with the thread library to do this automatically: if you want to transport exceptions you need to catch them with boost.Exception yourself, and then transport them.
My futures library (http://www.justsoftwaresolutions.co.uk/threading/updated-implementation-of-c...) which is on the Boost review queue provides a packaged_task which can be used with boost.thread to transport exceptions to the thread that is waiting for the result.
Yes, futures is a higher level library. Boost Exception is very basic, perhaps at the same level as boost::thread. Just to clarify, this is how boost::thread and boost::exception can be used together to transport exceptions between threads: http://svn.boost.org/svn/boost/trunk/libs/exception/doc/tutorial_exception_p... Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
Exceptions should be caught somewhere. The software engineer has to make certain of that by adding a try-catch-block on every interface boundary -- such boundaries are usually points where C++ borders on C or the operating system -- e.g. * the main-function, * window message handlers, * thread-functions, * callback handlers provided by certain frameworks e.g. to read from a file descriptor. I don't think a thread package should provide features for storing exceptions, since this would require a new base class other than std::exception. You may want to make certain that the exceptions thrown inside a thread function are not simply ignored -- e.g. the string returned from what() can be stored and displayed by the main thread. -----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Ruediger Berlich Sent: Wednesday, July 16, 2008 10:41 To: boost-users@lists.boost.org Subject: [Boost-users] [Boost.thread] Exception handling strategy ? Hi there, is there a recommended strategy for exception (or more generally error-)handling with Boost.thread ? From what I can see it is not safe to throw an exception from within a thread, as it is not sure who catches it (or whether it gets caught at all). Best, Ruediger _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On Wed, Jul 16, 2008 at 4:38 PM,
Exceptions should be caught somewhere. The software engineer has to make certain of that by adding a try-catch-block on every interface boundary -- such boundaries are usually points where C++ borders on C or the operating system -- e.g. * the main-function, * window message handlers, * thread-functions, * callback handlers provided by certain frameworks e.g. to read from a file descriptor.
I don't think a thread package should provide features for storing exceptions, since this would require a new base class other than std::exception.
I assume your remark is regarding Boost Exception's ability to copy exceptions so that they can be re-thrown at a later time (this is independent of boost::thread.) This functionality is essentially identical to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html, except that without language support, for current_exception() to work, it is required that the original exception is thrown using boost::enable_current_exception(). There are no requirements that the exception type derives any particular base class. There is no problem to use enable_current_exception()/current_exception() with a type that derives from std::exception: struct foo: std::exception { }; //in a worker thread: ... throw boost::enable_error_info(foo()); //in the worker thread's main thread function: ... catch(...) { boost::exception_ptr ep=boost::current_exception(); //copy ep outside of the thread } //in the main thread, working with the exception_ptr returned from a worker thread: ... if( ep ) boost::rethrow_exception(ep); //throws foo
You may want to make certain that the exceptions thrown inside a thread function are not simply ignored -- e.g. the string returned from what() can be stored and displayed by the main thread.
It's even better to copy the entire exception object, not only the string returned from what(). Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
"Emil Dotchevski"
This functionality is essentially identical to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html, except that without language support, for current_exception() to work, it is required that the original exception is thrown using boost::enable_current_exception().
Have you any plans to incorporate the MSVC-specific code I posted a few months ago to provide this support? Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
Yes this should be easy, I'll do it after 1.36 ships.
Emil Dotchevski
Reverge Studios, Inc.
http://www.revergestudios.com/reblog/index.php?n=ReCode
On Thu, Jul 17, 2008 at 12:04 AM, Anthony Williams
"Emil Dotchevski"
writes: This functionality is essentially identical to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html, except that without language support, for current_exception() to work, it is required that the original exception is thrown using boost::enable_current_exception().
Have you any plans to incorporate the MSVC-specific code I posted a few months ago to provide this support?
Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
"Emil Dotchevski"
Yes this should be easy, I'll do it after 1.36 ships.
Excellent! Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL
----- Original Message -----
From: "Anthony Williams"
"Emil Dotchevski"
writes: Yes this should be easy, I'll do it after 1.36 ships.
Excellent!
If I understood, the MSVC-specific code allows the user to get the current exception without throwing the exception with boost::enable_current_exception(). What is the advantage if we can not profit of this mechanism with other compilers? A few months ago I proposed an intrusive way allowing the user to define which exceptions where wrapped by boost::exception_ptr. IMHO, the exception_ptr emulation (without language support) to transport exceptions between threads is not of real use if the user can not configurate the exceptions that can be thrown by 3pp as the Exception library do already for the STL exceptions. If the Exception library do not allow this, how can the Future library transport every exception between two threads? For recall, my proposal was the following: The exception library defines a macro such as: #define BOOST_EXCEPTION_CATCH_RETURN(EXCP) \ catch( EXCP & e ) { \ return exception_detail::current_exception_unknown_boost_exception(e); \ } The current_exception function has a USER_SLOT used us follows inline exception_ptr current_exception() { try { throw; } catch( exception_detail::cloning_base & e ) { exception_detail::clone_base const * c = e.clone(); BOOST_ASSERT(c!=0); return exception_ptr(c); } catch( std::invalid_argument & e ) { return exception_detail::current_exception_std_exception(e); } // ... as before #ifdef BOOST_EXCEPTION_USER_SLOT #include BOOST_EXCEPTION_USER_SLOT #endif catch( ... ) { return exception_detail::current_exception_unknown_exception(); } } The user can define BOOST_EXCEPTION_USER_SLOT to a file containing one line for each exception that must be transported: BOOST_EXCEPTION_CATCH_RETURN(3pp::exception1) BOOST_EXCEPTION_CATCH_RETURN(boost:lock_error) Of course this has a limitation: it works only if the function using the current_exception is also inlined. Best, Vicente
On Mon, Jul 21, 2008 at 9:55 AM, vicente.botet
If I understood, the MSVC-specific code allows the user to get the current exception without throwing the exception with boost::enable_current_exception(). What is the advantage if we can not profit of this mechanism with other compilers?
Adding support for MSVC doesn't prevent or make it more difficult to add similar support for GCC and other compilers. Antony's implementation is a good example to follow, IMO.
A few months ago I proposed an intrusive way allowing the user to define which exceptions where wrapped by boost::exception_ptr. IMHO, the exception_ptr emulation (without language support) to transport exceptions between threads is not of real use if the user can not configurate the exceptions that can be thrown by 3pp as the Exception library do already for the STL exceptions.
I don't consider what the exception library does for STL exceptions very valuable. It can even be argued that it's fundamentally flawed in that it erases the actual exception type in case someone throws (for example) an exception that derives std::logic_error. Still, it is a reasonable workaround for the lack of language support. As last resort, you get an object of type boost::unknown_exception. This at least guarantees that no exception gets ignored.
For recall, my proposal was the following: The exception library defines a macro such as:
#define BOOST_EXCEPTION_CATCH_RETURN(EXCP) \ catch( EXCP & e ) { \ return exception_detail::current_exception_unknown_boost_exception(e); \ }
The current_exception function has a USER_SLOT used us follows
inline exception_ptr current_exception() { try { throw; } catch( exception_detail::cloning_base & e ) { exception_detail::clone_base const * c = e.clone(); BOOST_ASSERT(c!=0); return exception_ptr(c); } catch( std::invalid_argument & e ) { return exception_detail::current_exception_std_exception(e); } // ... as before #ifdef BOOST_EXCEPTION_USER_SLOT #include BOOST_EXCEPTION_USER_SLOT #endif catch( ... ) { return exception_detail::current_exception_unknown_exception(); }
I don't see the benefit of using a macro to inject this functionality into Boost. Instead, you could do something along the lines of (note, this won't work for abstract foo or bar): try { try { //call functions that throw without using boost::enable_current_exception } catch( foo & e ) { throw boost::enable_current_exception(e); } catch( bar & e ) { throw boost::enable_current_exception(e); } } catch( ... ) { boost::exception_ptr ep=boost::current_exception(); ... } Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
----- Original Message -----
From: "Emil Dotchevski"
On Mon, Jul 21, 2008 at 9:55 AM, vicente.botet
wrote: If I understood, the MSVC-specific code allows the user to get the current exception without throwing the exception with boost::enable_current_exception(). What is the advantage if we can not profit of this mechanism with other compilers?
Adding support for MSVC doesn't prevent or make it more difficult to add similar support for GCC and other compilers. Antony's implementation is a good example to follow, IMO.
It is only a portability issu. If the user compiles with MSVC, it will be able to recover every exception thrown using current_exception, which is not the case for other compilers.
A few months ago I proposed an intrusive way allowing the user to define which exceptions where wrapped by boost::exception_ptr. IMHO, the exception_ptr emulation (without language support) to transport exceptions between threads is not of real use if the user can not configurate the exceptions that can be thrown by 3pp as the Exception library do already for the STL exceptions.
I don't consider what the exception library does for STL exceptions very valuable. It can even be argued that it's fundamentally flawed in that it erases the actual exception type in case someone throws (for example) an exception that derives std::logic_error.
Why not complete the list?
Still, it is a reasonable workaround for the lack of language support. As last resort, you get an object of type boost::unknown_exception. This at least guarantees that no exception gets ignored.
Why to manage with a boost::unknown_exception. whenwe can have the thown exception using my proposal?
For recall, my proposal was the following: The exception library defines a macro such as:
#define BOOST_EXCEPTION_CATCH_RETURN(EXCP) \ catch( EXCP & e ) { \ return exception_detail::current_exception_unknown_boost_exception(e); \ }
The current_exception function has a USER_SLOT used us follows
inline exception_ptr current_exception() { try { throw; } catch( exception_detail::cloning_base & e ) { exception_detail::clone_base const * c = e.clone(); BOOST_ASSERT(c!=0); return exception_ptr(c); } catch( std::invalid_argument & e ) { return exception_detail::current_exception_std_exception(e); } // ... as before #ifdef BOOST_EXCEPTION_USER_SLOT #include BOOST_EXCEPTION_USER_SLOT #endif catch( ... ) { return exception_detail::current_exception_unknown_exception(); }
I don't see the benefit of using a macro to inject this functionality into Boost. Instead, you could do something along the lines of (note, this won't work for abstract foo or bar):
try { try { //call functions that throw without using boost::enable_current_exception } catch( foo & e ) { throw boost::enable_current_exception(e); } catch( bar & e ) { throw boost::enable_current_exception(e); } } catch( ... ) { boost::exception_ptr ep=boost::current_exception(); ... }
Yes you are right, I can do it, but what can I do if I use the Future library which manage internally the current_exception? I think that I can't, or maybe I'm missing something. Vicente
Thread Exception callbacks using boost::function boost::bind is what I have seen implemented with boost 1.34 and 1.35 due to the lack of this architecture and also a thread pool manager who received all exception callbacks. David wrote:
You can throw as long as the exception will be caught before it propagates out of the thread function. You can always put a try/catch(...) block in all your thread functions.
However an automated easily specified architecture's is desired. Thread pool manager allowing any thread to register for thread exceptions from any thread. This is also needed in dataflow as Stjepan and I discussed at BoostCon. To allow dataflow components to register for exceptions in other components which can be running in other threads, but when an exception happens in the other component in the other thread the remaining components need to know about it. Emil wrote:
Yet to be released Boost 1.36 includes the new Exception library which among other things supports transporting of exceptions between threads.
Which is what I have seen programmers resort to in the past. Only manually. If this happens in 1.36 it will remove some of our code and we can take advantage of this feature. Anthony worte:
My futures library ( http://www.justsoftwaresolutions.co.uk/threading/updated-implementation-of-c... ) which is on the Boost review queue provides a packaged_task which can be used with boost.thread to transport exceptions to the thread that is waiting for the result.
Exceptions should be caught somewhere. The software engineer has to make certain of that by adding a
Sweet Anthony this may be just what I needed. I'll check it out. Peter wrote: try-catch-block on every interface boundary -- such boundaries
are usually points where C++ borders on C or the operating system -- e.g. * the main-function, * window message handlers, * thread-functions, * callback handlers provided by certain frameworks e.g. to read from a file descriptor.
I don't think a thread package should provide features for storing exceptions, since this would require a new base class other than std::exception. You may want to make certain that the exceptions thrown inside a thread function are not simply ignored -- e.g. the string returned from what() can be stored and displayed by the main thread.
Keeping the exception model to one root element is ideal, but may be idealistic. There is other info than what std::exception can provide such as the line and file info of where the exception occurred,and the stack trace, and what thread the exception occurred in preferably by thread name (boost threads currently to my knowledge have now way to specify a name).. So while I too to not want exception hierarchies such as the JAVA, I do not see how std::exception will provide my desirements (which will hopefully one day become requirements). The key there in certain program is to keep the system running uncaught exceptions are a sure fire way to bring down a system. Thread death with out notification of what occurred and where is another way to crash a system. Peter wrote:
You may want to make certain that the exceptions thrown inside a thread function are not simply ignored -- e.g. the string returned from what() can be stored and displayed by the main thread.
Then Emil replied:
It's even better to copy the entire exception object, not only the string returned from what().
I would add to this the exceptions derived children... Even though we may not want exception hierarchies, other programmers might. So the transport mechanism should allow the full exception info supporting exception hierarchies. Emil wrote
I don't consider what the exception library does for STL exceptions very valuable. It can even be argued that it's fundamentally flawed in that it erases the actual exception type in case someone throws (for example) an exception that derives std::logic_error.
Yes a true issue. Derived type exceptions should not be erased.
Still, it is a reasonable workaround for the lack of language support. As last resort, you get an object of type boost::unknown_exception. This at least guarantees that no exception gets ignored.
I think Emil and I are in agreement with exception hierarchies. Brian
participants (7)
-
Anthony Williams
-
Brian Davis
-
David Abrahams
-
Emil Dotchevski
-
peter_foelsche@agilent.com
-
Ruediger Berlich
-
vicente.botet