For some multithreaded code, I would like to capture all exceptions and pass a them to a single exception handling thread. Here's the message passing framework:
#include <exception>
struct message
{
virtual ~message() = default;
virtual void act() = 0;
};
struct exception_message : message
{
std::exception_ptr ep;
virtual void act()
{
std::rethrow_exception(ep);
}
// ...
};
Here's the use case:
try
{
// ...
}
catch (...)
{
exception_message em { std::current_exception(); }
handler_thread.post_message(em);
}
The handler thread goes through all its messages and calls act()
, and it can install its own try/catch block to handle all the posted exceptions.
Now I was wondering what happens if I send copies this message to multiple receivers. In general, messages may have any number of recipients, and so I don't want to put arbitrary restrictions on exception propagation messages. The exception_ptr
is documented as a "shared-ownership" smart pointer, and rethrow_exception
"does not introduce a data race".
So my question: Is it legitimate to duplicate an active exception by storing it in an exception_ptr
, copying the pointer, and calling rethrow_exception
multiple times?
C++ Standard Exceptions An exception and parent class of all the standard C++ exceptions. This can be thrown by new. This can be thrown by dynamic_cast. This is useful device to handle unexpected exceptions in a C++ program.
Exception handling is a mechanism that separates code that detects and handles exceptional circumstances from the rest of your program. Note that an exceptional circumstance is not necessarily an error.
In the main() function, a try-catch block is created to throw and handle the exception. Within the try block, an object of MyCustomException is created and thrown using the throw keyword. The exception is then caught in the catch block, where the message is printed by accessing the what() function.
From my understanding of the Standard, it is legitimate. However I would note that the rethrow does not duplicate the exception, and therefore the shared exception object itself is submitted to data races should you modify it and access it from other threads meantime. If the exception is read-only (once thrown), then you should not have any issue.
Regarding storage duration:
15.1 Throwing an exception [except.throw]
4 The memory for the exception object is allocated in an unspecified way, except as noted in 3.7.4.1. If a handler exits by rethrowing, control is passed to another handler for the same exception. The exception object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type
std::exception_ptr
(18.8.5) that refers to the exception object is destroyed, whichever is later. In the former case, the destruction occurs when the handler exits, immediately after the destruction of the object declared in the exception-declaration in the handler, if any. In the latter case, the destruction occurs before the destructor ofstd::exception_ptr
returns.
Regarding data races:
18.8.5 Exception propagation [propagation]
7 For purposes of determining the presence of a data race, operations on
exception_ptr
objects shall access and modify only theexception_ptr
objects themselves and not the exceptions they refer to. Use ofrethrow_exception
onexception_ptr
objects that refer to the same exception object shall not introduce a data race. [ Note: ifrethrow_exception
rethrows the same exception object (rather than a copy), concurrent access to that rethrown exception object may introduce a data race. Changes in the number ofexception_ptr
objects that refer to a particular exception do not introduce a data race. —end note ]
Regarding rethrow
:
[[noreturn]] void rethrow_exception(exception_ptr p);
9 Requires:
p
shall not be a null pointer.10 Throws: the exception object to which p refers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With