ISO/IEC JTC1 SC22 WG14 N1327, submitted by P.J. Plauger
Adapted from the paper accepted for the revised C++ Standard:
ISO/IEC JTC1 SC22 WG21 N2440 = 07-0310 - 2007-10-05
by Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org
The primary problem with destruction of static-duration objects
is access to static-duration objects after their destructors have executed,
thus resulting in undefined behavior.
To prevent this problem,
WG21 N2382
Dynamic Initialization and Destruction with Concurrency
requires that all user threads finish before destruction begins
(via a call to exit
or return from main
).
In many applications, finishing a thread requires canceling the thread from the outside. See WG21 N2320 Multi-threading Library for Standard C++ and its successors for proposed mechanisms to canceling threads. For various reasons, this cancellation needs to be cooperative.
In essence, the problem is that applications may not be able to cooperatively cancel. There are a number of reasons for this problem.
In summary, relying on only cooperative cancellation to terminate processes will not meet the needs of many applications.
If cooperative cancellation is not possible, it would be better to never execute static destructors. The C++ Committee has clearly stated that it wishes to preserve execution of static destructors in normal applications. So, we need a mechanism to abandon an application process without cooperatively canceling all threads and without executing the static destructors.
Such a mechanism already exists in the _Exit
function.
Unfortunately, that function has no mechanism
to enable applications to flush critical information to stable storage.
In short, _Exit
is too drastic.
The existing standard has a facility (atexit
)
to register functions
that must execute when the program exits
(via a return from main
or an explicit call to exit
).
That function, though, implies execution of the static destructors,
and thus does not solve the problem.
The solution lies between _Exit
and exit
,
a function that executes registered functions,
but does not execute static destructors.
Threads may continue to run while registered functions execute,
and static-duration variables are still live for both the threads
and the registered functions.
We considered using
the existing atexit
function registration facility,
but ultimately decided against it because
programs often use atexit
for destructor-like actions,
and because
existing compilers often use atexit
to register static destructors.
Thus a new registration facility is needed.
The burden of converting to the new registration facility
is not large,
programmers need simply find all calls to atexit
and, after verifying that the register function is appropriate,
also call the new registration facility.
The number of calls to atexit
is not large.
For the new facility,
we have chosen quick_exit
and at_quick_exit
.
Other logical choices, like _Exit
already seem to be in use,
while there are no occurances of quick_exit
in a Google code search.
To the header <stdlib.h>
add the function:
int at_quick_exit(void (*f)(void));
The at_quick_exit
function
registers the function pointed to by f
,
to be called without arguments should quick_exit
be called.
[Note:
The at_quick_exit
registrations are distinct from
the atexit
registrations,
and applications may need to call both registration functions
with the same argument. -- end note]
The at_quick_exit()
functions are thread-safe.
The at_quick_exit()
function returns
zero if the registration succeeds, nozero if it fails.
Implementation limit: The implementation shall support the registration of at least 32 functions.
To the header <stdlib.h>
add the function:
quick_exit(int status);
The function quick_exit
calls functions registered by calling
at_quick_exit
, in the reverse order of
their registration.
A function registered during the calling of functions registered
with at_quick_exit
is called after any previously registered functions
that had already been called at the time it was registered.
After calling registered functions,
quick_exit
will call _Exit(status)
.
[Note:
The standard file buffers are not flushed. -- end note]
The function quick_exit
never returns to its caller.