Title
Termination in C++ is unclear
Status
new
Section
[support.start.term][exception.terminate]
Submitter
JF Bastien

Created on 2018-03-15.00:00:00 last changed 4 months ago

Messages

Date: 2024-07-15.00:00:00

[ 2024-07-26; Jonathan comments ]

In C89 and C99 the spec for `exit` in C said "If more than one call to the exit function is executed by a program, the behavior is undefined." Since C11 that was updated to also talk about `at_quick_exit`, saying "If a program calls the `exit` function more than once, or calls the `quick_exit` function in addition to the `exit` function, the behavior is undefined." The spec for `quick_exit` is similar.

That answers most of the questions here. An `atexit` or `at_quick_exit` handler cannot call `exit` or `quick_exit`, because if a handler is running then it means that `exit` or `quick_exit` has already been called, and calling either of them again would be undefined. It doesn't matter whether an `atexit` handler installs an `at_quick_exit` handler, because once `exit` handlers start running it would be undefined to call `quick_exit`, and vice versa. So you should never have a situation where both sets of handlers are running.

There is a suggestion to relax this in POSIX so that calling `exit` or `quick_exit` again from other threads would not be UB but would just block until the process exits, which should happen eventually assuming exit handlers make forward progress (calling `exit` or `quick_exit` from a handler would still be UB).

Why does C++ not make it undefined to call `exit` twice? Can we change that?

Date: 2018-04-02.00:00:00

[ 2018-04-02 Priority set to 3 after discussion on the reflector. ]

Date: 2018-04-15.00:00:00

[ 2018-04-02, Jens comments ]

Any potential wording should carefully take [basic.start] into account, and maybe should actually be integrated into the core wording, not the library wording.

Date: 2018-03-15.00:00:00

It's unclear how different termination facilities in C++ interact (and how they interact with the C termination facilities). Individually some of these functions try to handle corner cases, but hilarity ensues when combined with each other. As a simple example, can an atexit handler call exit? If not, can it call quick_exit, and can then at_quick_exit handler call exit? Is it possible to install an atexit handler from an at_quick_exit, without strongly happens before, while handling a separate atexit handler (and what happens then)?

The termination handlers and termination conditions I collected:

  • returning from main calls atexit handlers.

  • atexit / exit

  • at_quick_exit / quick_exit

  • set_terminate

  • violating noexcept and other things that call std::terminate (see [except.terminate])

  • violating exception specification

  • parallel algorithms leaving with uncaught exception

  • some std::signal such as SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, and (maybe?) SIGFPE.

  • set_unexpected (now a zombie)

  • unexpected_handler (now a zombie)

What's unclear is:

  • Is termination handling a DAG?

  • Which thread(s) are termination handlers called on?

  • Is program termination Turing complete?

I've written a sample program which exercises some of this, see here.

History
Date User Action Args
2024-07-26 09:01:45adminsetmessages: + msg14266
2018-04-03 18:56:45adminsetmessages: + msg9798
2018-04-03 18:56:45adminsetmessages: + msg9797
2018-03-15 00:00:00admincreate