1

I've been strong exception guarantee testing a class, especially on what happens on an out of memory condition, by randomly making malloc() return nullptr. It uses nested exceptions.

Let's say I have the following code:

static std::unordered_map<size_t, size_t> map;
try {
    map.at(0); // Throws std::out_of_range
} catch (...) {
    std::throw_with_nested(std::runtime_error("Input not in map")); // Out of memory here
}

std::throw_with_nested() ended up calling std::terminate():

terminate called after throwing an instance of 'std::out_of_range'
  what():  _Map_base::at

Program received signal SIGABRT, Aborted.
0x00007ffff6d96418 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54  ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff6d96418 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff6d9801a in __GI_abort () at abort.c:89
#2  0x00007ffff76d884d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff76d66b6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff76d6701 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff76d5472 in __cxa_allocate_exception () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x0000000000425d4c in std::_Throw_with_nested_impl<std::runtime_error, true>::_S_throw<std::runtime_error>(std::runtime_error&&) (
    __t=<unknown type in /path/to/<redacted>, CU 0x2a2a, DIE 0xae780>) at /usr/include/c++/5/bits/nested_exception.h:100
#7  0x000000000041d09f in std::throw_with_nested<std::runtime_error>(std::runtime_error&&) (__t=<unknown type in /path/to/<redacted>, CU 0x2a2a, DIE 0xa3e18>)
    at /usr/include/c++/5/bits/nested_exception.h:137

Is this the intended behaviour according to the standard? Personally, it feels like that it should just overwrite the old exception or throw std::bad_alloc if it fails to allocate a nested exception.

小太郎
  • 5,510
  • 6
  • 37
  • 48

1 Answers1

0

As far as I understand it, both the constructor of std::nested_exception and the global function std::current_exception() are noexcept, so if an exception occurs in either one, the only allowable course of action is std::terminate.

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • according to cppreference.com std::current_exception() will just return the exception caused during the call itself. – josefx Sep 14 '16 at 17:58
  • @josefx you're right. So that leaves the constructor of the outer exception. For example, std::runtime_error contains a std::string. If that throws during the throw, this will be terminate, I think. – Richard Hodges Sep 14 '16 at 18:07
  • The stack trace points to inside std::throw_with_nested though, so the std::runtime_error seems to have constructed without issue (otherwise it would never had called std::throw_with_nested) – 小太郎 Sep 15 '16 at 09:22