• Eric Niebler's avatar
    non-throwing, non-allocating exception_wrapper · 19e3e9fe
    Eric Niebler authored
    Summary:
    The purpose of this reimplementation of `exception_wrapper` is threefold:
    
    - Make `exception_wrapper` smaller. It goes from 48 bytes to 24.
    - Give it `noexcept` ~~copy and~~ move
    - Store small exception objects in an internal buffer; i.e., with zero allocations.
    
    The ultimate goal is to change `folly::Try<T>` to a thin wrapper over `folly::Expected<T, exception_wrapper>`. (Currently, it stores the `exception_wrapper` on the heap.)
    
    As part of this redesign, I:
    
    - Remove `exception_wrapper::getCopied`. The user shouldn't care how the `exception_wrapper` stores the exception.
    - Remove `exception_wrapper::operator==`. It was only used in 2 places in test code. The existing semantics (return true IFF two `exception_wrapper`s point to the //same// exception object) prevented the small-object optimization.
    - Add new `handle()` API that behaves like cascading `catch` clauses. For instance:
    ```lang=c++
    exception_wrapper ew = ...;
    ew.handle(
        [&](const SomeException& e) { /*...*/ },
        [&](const AnotherException& e) { /*...*/ },
        [&](...) { /* catch all*/ }, // yes, lambda with ellipses works!
    ```
    - Add a `type()` member for accessing the `typeid` of the wrapped exception, if it's known or can be determined with a `catch(std::exception&)`.
    
    This table shows the percent improvement for the exception_wrapper_benchmark test:
    
    | Test  | Percent improvement (gcc-5)  | Percent improvement (gcc-4)
    | -----  | -----  | -----
    | exception_wrapper_create_and_test  | 14.33%    | -6.50%
    | exception_wrapper_create_and_test_concurrent | 11.91% | 20.15%
    | exception_wrapper_create_and_throw | -0.82% | -0.25%
    | exception_wrapper_create_and_cast | 15.02% | 14.31%
    | exception_wrapper_create_and_throw_concurrent | 18.37% | 8.03%
    | exception_wrapper_create_and_cast_concurrent | 28.18% | -10.77%
    
    The percent win for gcc-5 is 15% on average. The non-throwing tests show a greater win since the cost of actually throwing an exception drowns out the other improvements. (One of the reasons to use `exception_wrapper` is to not need to throw in the first place.) On gcc-4, there is roughly no change since the gcc-4 standard exceptions (`std::runtime_error`, std::logic_error`) are non-conforming since they have throwing copy operations.
    
    Reviewed By: yfeldblum
    
    Differential Revision: D4385822
    
    fbshipit-source-id: 63a8316c2923b29a79f8fa446126a8c37aa32989
    19e3e9fe
ExceptionWrapperTest.cpp 23.9 KB