This topic was briefly addressed in P0063R0, but the authors subsequently decided to address it separately. This version reflects P0270R0 discussion in Jacksonville, draft revisions in Oulu, as well as P0270R1 and draft revision discussions by SG1 and CWG in Issaquah.
The current C++ working draft restricts signal handlers to functions in the intersection of C and C++. We believe this is dubious for at least three reasons:
We propose to remedy this by replacing the notion of a "POF", "plain old function" with a "signal-safe evaluation" defined entirely in C++ terms. The definition we use largely excludes use of the library, thus avoiding any need to reimplement that in a signal-safe manner. This makes the definition rather restrictive. But we believe it remains at least theoretically possible for the user to write very sophisticated signal handlers through the use of atomics.
We expect that, although these changes are clearly substantive, there is no real practical impact. Existing implementations and user code should continue to work unchanged (or remain as broken as they always were.)
We do not exclude lambda expressions from signal handlers, on the assumption that the lambda expression itself should not require memory allocation. We do not say anything about non-function-local statics, since it should not be possible to trigger construction or destruction from within a signal handler.
As far as I can tell,
str... functions are not
guaranteed to be async-signal-safe
in Posix (or C), so it seems presumptive of us to declare them signal-safe
here. I thus decided against it.
We somewhat separably propose to make the behavior of non-signal-safe functions in signal handlers undefined, instead of implementation-defined. We are not aware of any implementations that actually attempt to define this.
The restriction on static initialization warrants discussion. We currently allow even the call of a POF that doesn't access any globals to trigger static initialization (3.6.2p4 [basic.start.init]). It's not clear how to make that work if a thread already part way through the initialization receives the signal. This appears to me to be a preexisting problem that this exposes, rather than a problem introduced by this change. It is partially addressed by P0250, which makes this implementation-defined.
It may make sense to eventually relax these restrictions invoked through an explicit synchronous raise() call. It's unclear that this is important enough to bother.
These changes are arguably not essential for the adoption of the P0063. But we believe they are highly desirable and should follow P0063 in fairly short order.
The original discussion is reflected in the CWG notes. Specific comments:
Thanks to help from the editors, these are roughly relative to the C++17 CD. Note that the underlying text changed substantially in Oulu, in ways that are orthogonal to our goals here.
Update 18.10.5 [csignal.syn] as follows.
In the synopsis, modify the signal declaration as follows:
void (*signal(int sig, void (*func)(int)))(int);
Insert a new paragraph before paragraph 3:
Modify the former paragraph 3 as follows.
The common subset of the C and C ++ languages consists of all declarations, definitions, and expressions that may appear in a well formed C++ program and also in a conforming C program. A POF (“plain old function”) is a function that uses only features from this common subset, and that does not directly or indirectly use any function that is not a POF,
that it may useplain lock-free atomic atomic operations A plain lock-free atomic operation is an invocation of a function f from Clause 29 [atomics], such that f is not a member function, and either f is the function
atomic_is_lock_free(), or for every atomic argument
Apassed to f,
atomic_is_lock_free(A)yields true. All signal handlers shall have C linkage.
The behavior of any function other than a POF used as a signal handler in a C++ program is implementation-defined.
Append a new paragraph.
The following functions need to be callable from handlers. We expect that a few other functions should also eventually be declared signal-safe, but these are among the more blatant cases. abort() and quick_exit() are treated as safe by C.
Add a new paragraph at the beginning of section 126.96.36.199 [numeric.limits.members]:
Add a sentence at the end of the remarks for _Exit() in 18.5p3 [support.start.term]:
Add a sentence at the end of the remarks for abort() in 18.5p5 [support.start.term]:
Add at new element to the end of 18.5p13 (quick_exit()) [support.start.term]. Note that it is not clear that this is actually correct for current implementations that may hold a lock while running handlers. However this requirement is already present in the C11 standard, and it doesn't seem to make much sense to weaken their specifications for their functions. An LWG issue on this has been requested.
Modify 18.9p1 [support.initlist] as follows:
<initializer_list>defines a class template and several support functions related to list-initialization (see 8.5.4).
Modify 20.2.4p1 [forward] as follows:
The library provides templated helper functions to simplify applying move semantics to an lvalue and to simplify the implementation of forwarding functions.
20.14 [meta] provides a few actual functions that return constant values. These are also clearly signal-safe. Add a new paragraph at the end of section 20.14 [meta], before 20.14.1:
Add after paragraph 2 of 21.5.3 [cstring.syn]:
Clark Nelson provided many useful suggestions, but not necessarily agreement. This version reflects contributions by many other members of CWG and LWG, including Jens Maurer, Thomas Koeppe and JF Bastien, who helped shepherd it through CWG and LWG and provided additional feedback.