Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hinnant's stack allocator and exceptions

I want to use Hinnant's stack allocator (documentation, implementation) in combination with STL containers but I want to modify it such that dynamic memory allocation NEVER takes place.

One thing that must be done to fulfil this is to replace the new/delete calls in the allocate/deallocate methods that take place if there is no room on the stack supplied buffer.

But how should I deal with exceptions? The STL containers may throw exceptions, e.g.

std::vector::at

"The function automatically checks whether n is within the bounds of valid elements in the vector, throwing an out_of_range exception if it is not [...]"

http://www.cplusplus.com/reference/vector/vector/at/

I could not find a clear answer whether exceptions are stored in dynamic or static memory. Only these lines give hints:

From [except.throw]/15.1/4:

The memory for the exception object is allocated in an unspecified way, except as noted in 3.7.4.1.

The final reference, [basic.stc.dynamic.allocation]/4, says:

[Note: In particular, a global allocation function is not called to allocate storage for [...] an exception object (15.1). — end note]

https://stackoverflow.com/a/27259902/8007684

What exactly does this mean? Is the memory reserved for exceptions placed in static memory? Or are there still any allocations happening in an "unspecified way" which would mean exceptions would be stored dynamically? The quoted description leads much space for interpretation...

So my basic question is: Is it safe to use STL containers+Hinnant's stack allocator if dynamic memory usage is forbidden? Or does this not work and I either have to use -fno-exceptions to replace exceptions by abort() calls or implement my own replacements of STL containers that do not throw exceptions...?

Thanks in advance!

inspire

like image 562
inspire Avatar asked Jun 01 '18 10:06

inspire


1 Answers

The gcc and clang implementations follow a specification called the Itanium ABI. It says, among other things:

Storage is needed for exceptions being thrown. This storage must persist while stack is being unwound, since it will be used by the handler, and must be thread-safe. Exception object storage will therefore normally be allocated in the heap, although implementations may provide an emergency buffer to support throwing bad_alloc exceptions under low memory conditions (see Section 3.3.1).

Memory will be allocated by the __cxa_allocate_exception runtime library routine. This routine is passed the size of the exception object to be thrown (not including the size of the __cxa_exception header), and returns a pointer to the temporary space for the exception object. It will allocate the exception memory on the heap if possible. If heap allocation fails, an implementation may use other backup mechanisms (see Section 3.4.1).

If __cxa_allocate_exception cannot allocate an exception object under these constraints, it calls terminate().

The libc++abi implementation of __cxa_allocate_exception is here. It will first go to the heap, and if that fails, try an emergency backup buffer that is statically allocated within the libc++abi.dylib. If both the heap, and the emergency-stash fail to allocate enough memory for the arbitrary-sized user-created exception, terminate() is called.

like image 143
Howard Hinnant Avatar answered Oct 19 '22 00:10

Howard Hinnant