Acting on SIGTERM signal under C99

F

Francois Grieu

Hello,

in C99, the signal handler for a SIGTERM signal is subject
to restrictions, including 7.14.1.1#5

If the signal occurs other than as the result of calling
the abort or raise function, the behavior is undefined
if the signal handler refers to any object with static
storage duration other than by assigning a value to an
object declared as volatile sig_atomic_t, or the signal
handler calls any function in the standard library other
than the abort function, the _Exit function, or the
signal function with the first argument equal to the
signal number corresponding to the signal that caused the
invocation of the handler.

Clearly, it is legal that the the signal handler for a
SIGTERM signal sets a sig_atomic_t flag, and poll that
flag in the normal program flow to terminate gracefully.
Is another option possible, allowing more freedom, e.g.
calling printf to output the best result found so far,
with no test for that in the normal program flow?

I'm thinking of also defining at startup a signal handler
for SIGABRT, and calling abort in the signal handler for
SIGTERM; it seems that the signal handler for SIGABRT will
be called, and it seems less restrictions apply on this one
as it occurs as the result of calling the abort function.
Is that correct?
If yes, I get that we need a mechanism to insure consistency
of data such as "the best result found so far". Do we also
need a mechanism preventing reentrant use of <stdio.h>
functions, or is it just fine to printf to a known open file
in a/this SIGABRT signal handler?

Anyone has a known-correct illustration of directly acting
on SIGTERM rather than by polling in the normal program
flow?

Off topic: what is there in the Linux/Posix/unamit specs
justifying the common use of printf in a SIGTERM/SIGABRT
signal handler?

TIA,

Francois Grieu
 
C

Chine Bleu, Blanc, et Rouge

Francois Grieu said:
Is another option possible, allowing more freedom, e.g.
calling printf to output the best result found so far,
with no test for that in the normal program flow?

The library is not required to be reentrant, that is if the signal occurred in
some library function, calling a library function can cascade in further errors.
Some functions are still safe to be called, but you have to check that with your
system's implementation.
I'm thinking of also defining at startup a signal handler
for SIGABRT, and calling abort in the signal handler for
SIGTERM; it seems that the signal handler for SIGABRT will

You could use the same signal handler.
void handler(int s) {
if (s==SIGTERM) ...
if (s==SIGABRT) ...
comman code
}

signal(SIGTERM, handler);
signal(SIGABRT, handler);
need a mechanism preventing reentrant use of <stdio.h>
functions, or is it just fine to printf to a known open file
in a/this SIGABRT signal handler?

If you are using a unix, the kernel calls like write(2) are safe.
 
L

lawrence.jones

Francois Grieu said:
Clearly, it is legal that the the signal handler for a
SIGTERM signal sets a sig_atomic_t flag, and poll that
flag in the normal program flow to terminate gracefully.
Is another option possible, allowing more freedom, e.g.
calling printf to output the best result found so far,
with no test for that in the normal program flow?

Not portably. In reality, there's almost nothing you can do with
signals portably. Restricting yourself to POSIX systems gives you more
capabilities, but you need to use POSIX-specific APIs (like sigaction)
rather than the C Standard APIs.
I'm thinking of also defining at startup a signal handler
for SIGABRT, and calling abort in the signal handler for
SIGTERM; it seems that the signal handler for SIGABRT will
be called, and it seems less restrictions apply on this one
as it occurs as the result of calling the abort function.
Is that correct?

No, the restrictions are transative -- the SIGABRT handler in that case
is still considered to be part of the SIGTERM handler.
If yes, I get that we need a mechanism to insure consistency
of data such as "the best result found so far". Do we also
need a mechanism preventing reentrant use of <stdio.h>
functions, or is it just fine to printf to a known open file
in a/this SIGABRT signal handler?

Yes, you need a mechanism to prevent reentrant use of stdio if you want
it to work reliably.
Off topic: what is there in the Linux/Posix/unamit specs
justifying the common use of printf in a SIGTERM/SIGABRT
signal handler?

The fact that it "usually" works just fine, and that's good enough for
most people.
 
F

Francois Grieu

Not portably. In reality, there's almost nothing you can do with
signals portably. Restricting yourself to POSIX systems gives you more
capabilities, but you need to use POSIX-specific APIs (like sigaction)
rather than the C Standard APIs.


No, the restrictions are transative -- the SIGABRT handler in
that case is still considered to be part of the SIGTERM handler.

I guess Lawrence meant "transitive". I do understand why some
natural implementations of signals might work as Lawrence says,
and that it is difficult indeed for an implementation of <signal.h>
and <stdlib.h> to work according to my reading of the standard.


However, if I stick to chapter and verse, as topical on CLC:

a) 7.14.1.1#2 defines a signal handler:
" An invocation of [a function passed as the second argument
of the signal function()] because of a signal, or
(recursively) of any further functions called by that
invocation (other than functions in the standard library),
is called a signal handler. "

b) if the signal handler for SIGTERM occurs not as the result
of calling the abort or raise function (on some systems that
occurs on Ctrl-C], the "If.." conditions at the beginning of
7.14.1.1#5 is fulfilled:
" If the signal occurs other than as the result of calling
the abort or raise function, the behavior is undefined
if the signal handler refers to any object with static
storage duration other than by assigning a value to an
object declared as volatile sig_atomic_t, or the signal
handler calls any function in the standard library other
than the abort function, the _Exit function, or the
signal function with the first argument equal to the
signal number corresponding to the signal that caused the
invocation of the handler. "

c) therefore the explicit exception "or the signal calls any function
in the standard library other than the abort function" worded in
7.14.1.1#5 applies, and this first signal handler can invoke the abort
function.

d) abort is a function in the standard library, thus invocation
of abort(), including in a signal handler for SIGTERM, does not
constitute a signal handler, by virtue of the "other than
functions in the standard library" exception in the definition
of a signal handler in7.14.1.1#2.

e) thus when calling the abort function in the signal handler
for SIGTERM cause invocation of the function that was passed
as the second argument to the signal function() with SIGABRT
as the first argument, this invocation is not part of the
the first signal handler, which would make it fall under the
first "if" clause of 7.14.1.1#5.

f) this invocation in e) is a second signal handler, one that
occurs "as the result of calling the abort (..) function", and
thus exempt from the first "if" clause in 7.14.1.1#5.

g) this leaves us with no reason that this invocation in e) would
fall under the "behavior is undefined if.." curse of 7.14.1.1#5.


Am I missing something?

And beside, as a meta argument: beyond making what I describe
correct at d) and e), what would be the rationale of the exception
"other than functions in the standard library" in 7.14.1.1#2?


Francois Grieu
 
L

lawrence.jones

Francois Grieu said:
g) this leaves us with no reason that this invocation in e) would
fall under the "behavior is undefined if.." curse of 7.14.1.1#5.

Am I missing something?

That the SIGABRT handler *is* being called from the SIGTERM handler,
albeit indirectly and via standard library functions. The standard
library functions themselves are not considered part of the handler,
but other user functions that they invoke are.
 
F

Francois Grieu

That the SIGABRT handler *is* being called from the SIGTERM handler,
albeit indirectly and via standard library functions. The standard
library functions themselves are not considered part of the handler,
but other user functions that they invoke are.

I agree with the above if you change "called from" to "invoked in
response to"; and "functions that they invoke" to "functions that
they call". I think that the standard is making a distinction between
function call, which is the usual mean of invocation of a function,
and the wider notion of invocation (which can include things such as
interrupts and other means).

In my reading, when the call to abort() causes the invocation
of the head function of the SIGABRT handler, that would not be a
"function call" as worded in 7.14.1.1#2.
" An *invocation* of [a function passed as the second argument
of the signal() function] because of a signal, or
(recursively) of any further functions *called* by that
invocation (other than functions in the standard library),
is [by definition] a signal handler. "

In practical terms on a traditional microprocessor of the 1980's
(or today's low-end embedded systems) without processes and threads,
an implementation could be that the SIGTERM handler executes as an
interrupt, thus under very restricted rules, though it can call
abort(). The code for abort() senses that it executes in this context
by testing the interrupt level. In that case it restores the stack
pointer to what it was on entry of the interrupt, then in the stack
substitutes the interrupt return address with that of the head
function of the SIGABRT handler, then execute a return-from-interrupt
instruction which atomically unstacks the program counter and re-enable
interrupts. That has the effect that the head function of the SIGABRT
handler is invoked on return of the interrupt, without being called.
The context of this invocation is less restrictive than that of
the SIGTERM handler (interrupts have been re-enabled and typically
stdio functions would work if not called reentrantly).

At the very least, 7.14.1.1 (and in particular the special provision
for abort in 7.14.1.1#5) is worded in a way that makes the above
implementation natural.
And I even read 7.14.1.1 as *requiring* something with the effect that
a SIGABRT handler invoked thru a call to abort() in a SIGTERM handler
is free from the "behavior is undefined if.." part of 7.14.1.1#5.


Is the intend of 7.14.1.1 known on that point, or at least is it
known that "call" is meant to be a distinct specialization of
"invoke"? I looked in the C99 rationale
<http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf>
and a few books, but did not find something relevant.


Francois Grieu
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top