This is the mail archive of the mailing list for the libstdc++ project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Implementing Exception Propagation (N2179)

Some more thoughts on this.

Sebastian Redl wrote:
To implement the proposal, I believe that these changes are necessary:

To libsupc++:

1) Extend struct __cxa_exception with two fields:
Another field is necessary, storing the size of the exception object, as passed to __cxa_allocate_exception originally. This information is not stored anywhere else, so no copy can be made without it.
3) Implement the three functions of N2179:
3b) rethrow_exception() calls __cxa_allocate_exception() to create space for a new exception object. It copies the relevant state and copies the exception object using the stored copy constructor. It then calls _Unwind_RaiseException.
Here's an interesting tidbit. The standard requires that exceptions use the unexpected_handler and terminate_handler set when the exception was thrown. In other words, if a destructor called during cleanup calls set_unexpected_handler() or set_terminate_handler(), this should have no effect on the exception. For this reason, these handlers are stored by __cxa_throw in the exception object.
With the current __cxa_rethrow implementation, an exception rethrown by a simple throw does not change these handlers. In other words, this does not affect the exception:

catch(...) {

However, from my understanding of the description of std::terminate(), this is actually a bug in libstdc++, not the intent of the standard:

"Calls the terminate_handler function in effect immediately after evaluating the throw-expression, if called by the implementation, [...]"

What about rethrow_exception(), then? Should it copy the terminate and unexpected handlers from the old exception, and thus act like the current implementation of rethrow? Or should it acquire the current handlers anew and thus pretend it contains a throw-expression?
If, and only if, the passed exception_ptr is the only reference to the exception (handlerCount == 0 and the reference count == 1), rethrow_exception() may simply throw the existing exception object.
I've realized that this is problematic. rethrow_exception() takes its argument by value. Thus, during the call, there will be at least two exception_ptrs to this exception: the argument, and the pointer passed as the argument. That is, unless exception_ptr has a move constructor, in which case the outer pointer could be moved to the argument, making it just one pointer.
This means that the optimization is not possible, unless one of two things are done:
1) Make the argument a const reference. I'm not sure if this change in signature is allowed by the standard.
2) Don't ever give exception_ptr move semantics. Given that copying it should be quite fast, this ought to be fine.
3c) While the simple implementation of copy_exception works, it's inefficient. It would be more efficient to call __cxa_allocate_exception() and initialize the resulting object, then return an exeption_ptr referring to it. However, this requires compiler support, as there is no library way of obtaining the destructor and copy constructor of a type.
Is there any precedent for the compiler generating low-level instantiations of a template?
To the C++ frontend itself:

4) Pass the copy constructor to __cxa_throw.

Therefore, it may be necessary to construct a stub that fits the signature and calls the copy constructor with default arguments as required, then pass that stub to __cxa_throw.
For example, a valid stub implementation would be

template <typename E>
void copy_constructor(E *dest, const E *src)
 new (dest) E(*src);

I have no idea how I would inject this definition into GCC's tree, though - especially how to avoid dupes.
As such, the exception class would have to be changed. This would make interoperation with old code at least partially possible - the exceptions would be relegated to the status of foreign exceptions, could be caught with catch(...) only, and could not be nested. Possible classes are "GNU2C++\0" or "2NUCC++\0".
Actually, since we're breaking the common C++ ABI (extending the __cxa_throw prototype and changing __cxa_exception), we should change no the vendor part ("GNUC") but the language part "C++\0", perhaps to "C+1\0" or "C+09".

By the way, I got impatient and implemented this, including the changes to G++. All I need now is the whole copy constructor thing, then I can compile and test.
(I'd need test cases, too. So I've still got things to write. Lots of things - it's a simple proposal, but with very complex applications.)

Still hoping for answers,

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]