C99 offers the _Exit function, which exits "immediately", although it does may close file descriptors. Unix/POSIX extends this behavior by mandating the closing of all fd's without flushing (and offers the synonym _exit).
Will these functions call destructors for static objects when called from a C++ program? Does the C++ standard make any guarantees about _Exit?
(Inspired by this question; I suddenly wondered what happens in the typical fork-exec-_exit idiom in C++.)
First, no form of program exit will automatically call destructors for heap objects (implied in ISO/IEC 14882:1998(E) 12.4.10).
Calling exit() will not call destructors for objects with automatic duration, as it does not return through their enclosing scopes (3.6.1.4). However, destructors for static objects will be called, in reverse order of construction (18.3.8).
Calling abort() does not call any destructors for any type of object, nor does it call atexit() registered functions (18.3.3). The C++ standard copy I have here is a bit dated and does not mention _exit or _Exit directly, but I'd imagine that, if present, they should behave the same - that is, not calling any destructors. In particular, in the C99 standard, _Exit() skips atexit handlers (it is implementation defined whether stream buffers are flushed, open streams are closed, or temporary files removed).
Further note that abort() can be cancelled by trapping signal SIGABRT (ISO/IEC 9899:1999 (E) 7.20.4.1.2 - I only have C99 here but I expect it would be the same in the version referenced by C++). _Exit() cannot.
On a more practical note, on most unix implementations of abort() and _exit(), abort() raises a SIGABRT while _exit() simply calls an operating system call to terminate the process immediately. This means that the main differences are:
You can specify an exit code for _exit()
abort() may be trapped by a signal handler
Depending on system configuration, OS, and ulimits, abort() may result in a core dump or similar
In a fork()/exec() pattern, _exit() would probably be preferable, to avoid the possibility of core dump.
It simply doesn't exist in standard C++, so there are no guarantees.
It is planned for inclusion in C++0x. That specifies (§18.5):
The function _Exit(int status) has
additional behavior in this
International Standard:
— The program is terminated without
executing destructors for objects of
automatic, thread, or static storage
duration and without calling functions
passed to atexit() (3.6.3).
Followup:
ISO approved C++0x on August 12, 2011.
Technically, _Exit is not defined by the C++ standard, so you can't even call it from a 100% portable C++ program. The C++03 standard incorporates by reference the C89 standard (aka C90 or ANSI C), whereas _Exit is only defined in the newer C99 standard. I'm not sure which version of C the upcoming C++0x standard incorporates, but I would guess that it's based on C99.
In any case, though, here are the relevant clauses from the relevant language standards:
_Exit is not guaranteed to close file descriptors. From C99 §7.20.4.4/2 (emphasis mine):
The _Exit function causes normal program termination to occur and control to be returned to the host environment. No functions registered by the atexit function or signal handlers registered by the signal function are called. The status returned to the host environment is determined in the same way as for the exit function (7.20.4.3). Whether open streams with unwritten buffered data are flushed, open streams are closed, or temporary files are removed is implementation-defined.
Recall that implementation-defined means that the implementation (that is, the compiler toolchain and runtime environment) can choose to do whatever it wants, but it must document what it does.
From C++03 §3.6.3/1:
Destructors (12.4) for initialized objects of static storage duration (declared at block scope or at namespace scope) are called as a result of returning from main and as a result of calling exit (18.3). These objects are destroyed in the reverse order of the completion of their constructor or of the completion of their dynamic initialization. If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized. For an object of array or class type, all subobjects of that object are destroyed before any local object with static storage duration initialized during the construction of the subobjects is destroyed.
§3.6.3/4:
Calling the function
void abort();
declared in <cstdlib> terminates the program without executing destructors for objects of automatic or static storage duration and without calling the functions passed to atexit().
Practically, in most implementations, global object destructors are implemented via atexit, so what you will see is that _Exit will not call the destructors for global objects, although this behavior is not guaranteed (since _Exit and C++ are not guaranteed to both exist in the same language).
Note that while C++ does not specify _Exit and C99 leaves it implementation-defined whether it flushes buffers, POSIX requires that it not flush buffers (since this would break the main usage of _exit/_Exit, i.e. handling failure of execve after fork). As POSIX does not align itself with C++ standards or defer to them on anything, I think it's very unlikely that a future version of the C++ standard would try to change any of this. It will probably either leave _Exit unspecified or specify that it's implementation-defined.
C++0x defines a new function called std::quick_exit that terminates a process without calling any destructors. Just checked, g++-4.4.5 already provides it.
There is an interesting analysis here in relation with concurrency and object destruction. As far as I know, destructors will not be called. There is nothing about it in the current standard.
Calling of static destructors is defined in terms of atexit. _exit (or _Exit) is defined not to run atexit handlers. So static destructors should not be called by any implementation.
Automatic destructors are not even called when calling exit().
So any sane definition of _Exit semantics for C++ would not run destructors.
I did a quick test with gcc on Mac OS and my destructors didn't get called.
struct A
{
~A()
{
puts("A::~A");
}
};
A globalA;
int main()
{
A localA;
_exit(0); // or _Exit(0)
return 0;
}
exit(0) on the other hand calls globalA's destructor.
fork(), exec(), and _exit() are all defined by POSIX and they pre-date C99's _Exit() by many years. Programs that use fork/exec/_exit are not portable to every system that supports C++.
With regard to _exit() specifically, it is an operating system call that (under POSIX) will close files and terminate the process directly (but not necessarily quickly). This would bypass any C++ mechanisms for calling destructors.
Even with _Exit() or similar being provided by C++0x, I doubt if there would be much reason to use that in conjunction with fork. It likely just provides broader portability for a "quick-exit" in other contexts. That functionality is already covered by _exit() if you are using the POSIX API.
Program termination is addressed in C++2003 section [3.6.3]. It says that static objects are destructed implicitly when main() returns and when exit() is called. It also says that such objects are NOT destructed when abort() is called. _exit() isn't addressed in the C++ 2003 standard, but the fact that it is meant to bypass language-specific cleanup is described in the POSIX documentation. That effect is further substantiated by what is stated and by what is NOT stated in the C++ standard.
Related
Consider the following code:
#include <cstdlib>
struct Foo {
~Foo() {
std::exit(0);
}
} foo;
int main() {
}
It compiles and terminates with zero successfully for me both on my Linux (GCC, Clang) and Windows (Visual Studio). However, when compiled with MSYS2's GCC on Windows (g++ (Rev2, Built by MSYS2 project) 10.3.0), it enters an infinite recursion and dies from stack overflow. This can be checked by adding some debug output right before std::exit(); I did not add it initially to avoid thinking about the destruction of std::cout.
Does any C++ standard have anything to say about such behavior? Is it well-defined/implementation-defined/undefined/etc and why?
For instance, [support.start.term]/9.1 of some recent draft says the following about std::exit's behavior:
First, objects with thread storage duration and associated with the current thread are destroyed.
Next, objects with static storage duration are destroyed and functions registered by calling atexit are called. See [basic.start.term] for the order of destructions and calls.
which refers to [basic.start.term]/1, I guess:
Constructed objects ([dcl.init]) with static storage duration are destroyed and functions registered with std::atexit are called as part of a call to std::exit ([support.start.term]).
The call to std::exit is sequenced before the destructions and the registered functions.
I don't see any immediate restriction on calling std::exit in a destructor.
Sidenote: please refrain from commenting that "this code is bad", "thou shalt not use destructors in global objects" (which you probably should not) and probing for the XY problem in the comments. Consider this an academic question from a curious student who knows a much better solution to their original problem, but have stumbled upon this quirk while exploring the vast meadows of C++.
[basic.start.main]/4:
If std::exit is called to end a program during the destruction of an object with static or thread storage duration, the program has undefined behavior.
The C++ Standard (c++14 and also the current working draft) states that
Except where explicitly specified in this document, it is
implementation-defined which functions in the C++ standard library may
be recursively reentered.
Except of std::recursive_mutex I couldn't find any type or function in the standard where it is explicitly specified that it can be recursively reentered safely. Even for std::recursive_mutex the standard states that:
It is unspecified how many levels of ownership may be acquired by a
single thread.
My questions for C++17:
Which types or functions, defined by the C++ standard, have to be implemented in a way that they can be recursively reentered?
Reentrant code may not call non-reentrant functions (see rules for reentrancy below). With this in mind, is it correct to summarize that C ++ code can only be safely reentrant if no C++ standard types or functions are used?
Please note, that reentrancy and thread-safety are orthogonal concepts:
In computing, a computer program or subroutine is called reentrant if
multiple invocations can safely run concurrently on a single processor
system, where a reentrant procedure can be interrupted in the middle
of its execution and then safely be called again ("re-entered") before
its previous invocations complete execution. The interruption could be
caused by an internal action such as a jump or call, or by an external
action such as an interrupt or signal, unlike recursion, where new
invocations can only be caused by internal call.
This definition of reentrancy differs from that of thread-safety in
multi-threaded environments. (...) Conversely, thread-safe code does
not necessarily have to be reentrant (...)
Rules for reentrancy:
Reentrant code may not hold any static or global non-constant data.
Reentrant code may not modify itself.
Reentrant code may not call non-reentrant computer programs or routines.
Which types or functions, defined by the C++ standard, have to be implemented in a way that they can be recursively reentered?
Reentrancy is a pre-requisite for asynchronous signal safety. Therefore functions which are specified as signal-safe must be implemented as reentrant.
[support.signal]
An evaluation is signal-safe unless it includes one of the following:
a call to any standard library function, except for plain lock-free atomic operations and functions explicitly identified as signal-safe;
A plain lock-free atomic operation is an invocation of a function f from [atomics], such that:
f is the function atomic_is_lock_free(), or
f is the member function is_lock_free(), or
f is a non-static member function invoked on an object A, such that A.is_lock_free() yields true, or
f is a non-member function, and for every pointer-to-atomic argument A passed to f, atomic_is_lock_free(A) yields true.
As such, those plain lock-free atomic opreations must be reentrant.
If the exception is thrown by the C++ code but is not caught, it causes SIGABRT. Some systems just print "Abort", some other systems also print the contents of e.what().
The question is: Does the C++ standard say that try/catch block is required in the main function for the program to be considered a well behaved program, or does C++ just silently rely on the system to process this?
As described in C++17 standard draft, in section 18.3.9 [except.handle]:
If no matching handler is found, the function std::terminate() is called; whether or not the stack is unwound before this call to std::terminate() is implementation-defined.
So, the behavior of such program is not considered undefined, since standard defines, that std::terminate will be called.
No, C++ standard doesn't require every exception to be caught. If the exception is not caught, the program terminates, and it is implementation-defined if stack unwinding is happening (it does with all the compilers I know). Further reading: https://en.cppreference.com/w/cpp/language/throw
We have some c++ code that can make calls to exit(3) through the users API. My assumption is that we are not properly unwinding stacks and that this considered bad in c++. Also there is a big c++ library involved that must be considered a black box.
I want to patch this, and also have an idea how, but don't know how to observe and compare the change. Can I make this somehow visible? Possibly on OS X?
exit() apparently does some cleanup. This is described in section 18.5 [support.start.term] of the standard, but the frequently correct site www.cplusplus.com summarizes it.
So it says objects with static storage or thread storage will be cleaned up, as will the entire I/O system (files will be flushed, etc).
But there are other ways to exit without running the C++ cleanup. For example, if it is a library that calls exit and it is a C language library (not C++) then it may or may not do the C++ cleanup. Or there are calls to abort or quick_exit. And, too, if you call the OS directly (e.g., ExitProcess() on Windows) then the process exits immediately and no C++ cleanup is done.
If you want to make the behavior visible: Make a object with a destructor that does something interesting - like log a message somewhere. Or maybe when it is constructed it creates a file with a certain name and when destructed it deletes it. Declare an instance this object in your main(). Declare another one (with a different message) at static scope. So now you have an effect observable in your environment.
The following is from 18.5 of N4140 (2014-10-07):
[[noreturn]] void exit(int status)
8 The function exit() has additional behavior in this International Standard:
(8.1) First, objects with thread storage duration and associated with the current
thread are destroyed. Next,objects with static storage duration are destroyed
and functions registered by calling `atexit` are called. See 3.6.3 for the
order of destructions and calls. (Automatic objects are not destroyed as a
result of calling `exit()`.) If control leaves a registered function called by
`exit` because the function does not provide a handler for a thrown exception,
`std::terminate()` shall be called (15.5.1).
(8.2) Next, all open C streams (as mediated by the function signatures declared in
`<cstdio>`) with unwritten buffered data are flushed, all open C streams are
closed, and all files created by calling `tmpfile()` are removed.
(8.3) Finally, control is returned to the host environment. If `status` is zero or
`EXIT_SUCCESS`, an implementation-defined form of the status _successful
termination_ is returned. If `status` is `EXIT_FAILURE`, an implementation-
defined form of the status _unsuccessful termination_ is returned. Otherwise
the status returned is implementation-defined.
There are various ways of exiting a process:
e.g.: ExitProcess, ExitThread (from the main thread), exit, abort, return from main, terminate.
I'd like to know the effects each method has on static/global/automatic object destruction.
For example, I have a project that crashes (probably due to some deallocation error) when ExitProcess is called, but not when exit() is called. (related to this question, incidentally).
So basically I'd like to know under which circumstances deallocation of the above objects occurs, and in what order (For VC++).
In short: The only totally safe thing to do is to allow main(), or your thread function, to return.
The C++ standard guarantees (3.6.3/1, 18.3) that destructors for global objects (including static objects) will be called if exit() is called, however it explicitly states that destructors for local variables will not be called in this case. exit() will call any functions registered with atexit(), and will also flush and then close any open stdio streams (including at least stdin, stdout, stderr).
Calling abort() is guaranteed not to call local or global destructors. Nor will it call functions registered with atexit() or flush stdio streams.
Calling any Win32 primitive such as ExitProcess() or ExitThread() will certainly not call destructors for local variables, and will almost certainly not call any destructors for global objects, or any functions registered with atexit(). Calling these functions directly in a C++ program is not advised -- basically, these Win32 functions and the C++ runtime library know nothing about each other. In fact, even the MSDN documentation for ExitThread() advises that C++ programs should return from the thread function instead of calling ExitThread().
(It is theoretically possible that the runtime library has specially arranged for ExitProcess() to call global object destructors -- this could be done by always loading a specific DLL whose entry point function will perform these calls, since ExitProcess() will call the entry point function for each loaded DLL with DLL_PROCESS_DETACH -- however to my knowledge, no implementation does this.)
See the source code of ExitProcess() (posted on compuserve, usenet)