Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

pending SIGALRM when resetting an alarm

101 views
Skip to first unread message

Rainer Weikusat

unread,
Mar 18, 2021, 10:36:07 AM3/18/21
to
Assuming SIGALRM is blocked, a SIGALRM is pending and the alarm is reset
with a new alarm call, what (if anything) happens to the pending signal?

Due "the precautionary principle", all my alarm calls in a certain piece
of code are presently wearing facemas^w^w followed by switching the
signal action to SIG_IGN and back in order to get rid of one which might
have been pending by the time the alarm call was made. At least
theoretically, that's unsafe because the alarm which was just set up
could have been expired before the second syscall.

Richard L. Hamilton

unread,
Mar 18, 2021, 9:04:41 PM3/18/21
to
In article <87a6r0m...@doppelsaurus.mobileactivedefense.com>,
Except for cases of SIGCHLD generated by the OS (which is an exception
because one could have multiple child processes exit, and if depending
on SIGCHLD rather than wait() to find out, still need to know about
each), signal delivery is generally NOT reliable when multiple
instances of the same signal are pending; probably no more than one
will be received, there aren't counters, lists, or stacks of them to
support multiple pending instances of the same signal.

But more specifically than that, for SIGALRM,

Solaris 11.3 alarm(2) man page:
Alarm requests are not stacked; only one SIGALRM generation can be
scheduled in this manner; if the SIGALRM signal has not yet been gener-
ated, the call will result in rescheduling the time at which the
SIGALRM signal will be generated.

macOS 10.15 alarm(3) man page (underlying implementation uses setitimer(2)):
The alarm() function sets a timer to deliver the signal SIGALRM to the
calling process after the specified number of seconds. If an alarm has
already been set with alarm() but has not been delivered, another call to
alarm() will supersede the prior call. The request alarm(0) voids the
current alarm and the signal SIGALRM will not be delivered.


Where applicable, setitimer() rather than alarm() gives more control,
although its man page (let alone different versions of it) seems a bit unclear
to me.

Even alarm() returns the old timer value - how many seconds before the
previously set alarm would have expired, or zero if no previous alarm.
So the sending code COULD do something sensible if that was nonzero,
and if it still mattered. Although I can see ways that could also have
unexpected results (for example, sleep() uses alarm() and pause() internally).
But if one didn't use any library routines that called alarm() or sigitimer(),
one could probably write something that would keep track of and reissue alarms
so they were all sent (although no more than one to arrive at any particular
second); but it'd still take care to catch them in such a way as to not
miss any (see below). A further complication, what happens if the system
clock is set forward with an alarm pending, is it delivered based on the
new wall clock time, or unaffected and still the same amount of time after it
was issued? There should be a standard answer for that, but I don't know off
the top of my head; and depending on what happens there, it might not be
strictly possible to have a user space implementation of multiple pending
alarms.

sigaction() and sigprocmask() and related calls give the additional
option of blocking rather than ignoring signals, allowing one pending
instance of each signal (more for SIGCHLD) while they're blocked,
rather than throwing them away as during SIG_IGN. Depending on how
they're used, that can also block automatically the same signal while
in a handler for that signal, etc. Those newer interfaces give a lot
more control, but good examples of how to use them (and setitimer() I
suppose) for more reliable outcomes might be rare.

I never did anything that really needed setitimer() or the more advanced
signal handling facilities, so I don't have such examples handy.

Rainer Weikusat

unread,
Mar 19, 2021, 10:36:59 AM3/19/21
to
rlh...@smart.net (Richard L. Hamilton) writes:

> In article <87a6r0m...@doppelsaurus.mobileactivedefense.com>,
> Rainer Weikusat <rwei...@talktalk.net> writes:
>> Assuming SIGALRM is blocked, a SIGALRM is pending and the alarm is reset
>> with a new alarm call, what (if anything) happens to the pending signal?
>>
>> Due "the precautionary principle", all my alarm calls in a certain piece
>> of code are presently wearing facemas^w^w followed by switching the
>> signal action to SIG_IGN and back in order to get rid of one which might
>> have been pending by the time the alarm call was made. At least
>> theoretically, that's unsafe because the alarm which was just set up
>> could have been expired before the second syscall.

There's nothing in this reply which addresses the question I asked ;-).
I'll reply to a few bits of it for technical correctness.

> Except for cases of SIGCHLD generated by the OS (which is an exception
> because one could have multiple child processes exit, and if depending
> on SIGCHLD rather than wait() to find out, still need to know about
> each), signal delivery is generally NOT reliable when multiple
> instances of the same signal are pending;

[...]

Realtime signals are queued. An implementation supporting queued
realtime signals may also queue other signals (IIRC, Linux does) but it
may as well not: SIGCHLD means "some number of child processes exited"
(since the last SIGCHLD).

[...]

> A further complication, what happens if the system clock is set
> forward with an alarm pending, is it delivered based on the
> new wall clock time, or unaffected and still the same amount of time after it
> was issued?

In this case, "an alarm" should go off somewhere in middle-management so
that they guy randomly breaking stuff by making random changes to the
wallclock can be sent to a "The 19th century is long over, dude, and
'universal time' is not a evil conspiracy of the railway companies no
sane person would ever want to have"[*] training course. :->.

[*] I've actually read an editorial by a "scientist" complaining about
this very fact ("Without the evil railway, no one would need to know
what the clock time in the next village is and they could again be all
different! Paradise regained!") within the last 10 years.

Rainer Weikusat

unread,
Mar 19, 2021, 11:37:30 AM3/19/21
to
Paragraph above is not entirely correct: Prior to reprogramming the
alarm time, I'm doing an alarm(0) to kill a possibly still running alarm
followed by changing the signal disposition to SIG_IGN and back to get
rid of a possibly pending SIGALRM, followed by setting the new alarm.

AFAICT, this procedure is correct and SUS doesn't say anything re: What
happens to a pending SIGALRM when the alarm time is changed. Judging
from a cursory look at the Linux code, it doesn't seem to do anything
about this despite receiving an "old" SIGALRM after a new alarm was set
seems singularly useless (to me).

Philip Guenther

unread,
Mar 19, 2021, 12:23:52 PM3/19/21
to
On Friday, March 19, 2021 at 6:37:30 AM UTC-9, Rainer Weikusat wrote:
> Rainer Weikusat <rwei...@talktalk.net> writes:
> > Assuming SIGALRM is blocked, a SIGALRM is pending and the alarm is reset
> > with a new alarm call, what (if anything) happens to the pending signal?
> >
> > Due "the precautionary principle", all my alarm calls in a certain piece
> > of code are presently wearing facemas^w^w followed by switching the
> > signal action to SIG_IGN and back in order to get rid of one which might
> > have been pending by the time the alarm call was made. At least
> > theoretically, that's unsafe because the alarm which was just set up
> > could have been expired before the second syscall.
> Paragraph above is not entirely correct: Prior to reprogramming the
> alarm time, I'm doing an alarm(0) to kill a possibly still running alarm
> followed by changing the signal disposition to SIG_IGN and back to get
> rid of a possibly pending SIGALRM, followed by setting the new alarm.

Given the context of "already blocked SIGALRM", this seems like the correct
procedure to me.

(For some simpler cases, instead of blocking it before the critical section it may
be simpler to call alarm(0) and then see whether the alarm actually triggered
and skip the processing if it did, but if there are prerequisites that have to be
handled before the alarm can be safely canceled then that won't be reliable and
I agree your block/cancel/ignore/catch/set is the right sequence.)


> AFAICT, this procedure is correct and SUS doesn't say anything re: What
> happens to a pending SIGALRM when the alarm time is changed.

It doesn't say anything because changing the alarm time has no effect on
pending SIGALRM signals.

AFAIR, there's only one case in SUS where a blocked-not-yet-delivered signal
can be vanish: when a wait-family call processes the death of a child process
the SIGCHLD for that child can vanish.


> Judging
> from a cursory look at the Linux code, it doesn't seem to do anything
> about this despite receiving an "old" SIGALRM after a new alarm was set
> seems singularly useless (to me).

That was the behavior of all the existing UNIX systems when POSIX was written, no?


Philip Guenther

Rainer Weikusat

unread,
Mar 19, 2021, 4:02:37 PM3/19/21
to
Philip Guenther <guen...@gmail.com> writes:
> On Friday, March 19, 2021 at 6:37:30 AM UTC-9, Rainer Weikusat wrote:
>> Rainer Weikusat <rwei...@talktalk.net> writes:
>> > Assuming SIGALRM is blocked, a SIGALRM is pending and the alarm is reset
>> > with a new alarm call, what (if anything) happens to the pending signal?

[...]

>> Paragraph above is not entirely correct: Prior to reprogramming the
>> alarm time, I'm doing an alarm(0) to kill a possibly still running alarm
>> followed by changing the signal disposition to SIG_IGN and back to get
>> rid of a possibly pending SIGALRM, followed by setting the new alarm.
>
> Given the context of "already blocked SIGALRM", this seems like the correct
> procedure to me.
>
> (For some simpler cases, instead of blocking it before the critical section it may
> be simpler to call alarm(0) and then see whether the alarm actually triggered
> and skip the processing if it did, but if there are prerequisites that have to be
> handled before the alarm can be safely canceled then that won't be reliable and
> I agree your block/cancel/ignore/catch/set is the right sequence.)

That's not possible in this case: I'm using sigwait(info) to wait for
three possible events (termination of a task running in a forked
process/ SIGCHLD, timeout/ SIGALRM, user request to abort it/
SIGINT). Assuming a timeout or an abort request occurs, a SIGTERM is
being sent to the process. The alarm is then reprogrammed (using a
shorter interval) to a second timeout triggering a SIGKILL.

>> AFAICT, this procedure is correct and SUS doesn't say anything re: What
>> happens to a pending SIGALRM when the alarm time is changed.
>
> It doesn't say anything because changing the alarm time has no effect on
> pending SIGALRM signals.

[...]

>> Judging from a cursory look at the Linux code, it doesn't seem to do
>> anything about this despite receiving an "old" SIGALRM after a new
>> alarm was set seems singularly useless (to me).
>
> That was the behavior of all the existing UNIX systems when POSIX was
> written, no?

UNIX is older than me (although not much), hence "I have no idea" :-).

It just not particularly sensible: The next SIGALRM received by a process
after a call to alarm (or similar) returned should correspond to the
most recently programmed alarm.

Scott Lurndal

unread,
Mar 19, 2021, 4:43:05 PM3/19/21
to
It's not older than I am.

The standards committees with rare exceptions, designed the standards
in such a way as to ensure that existing implementations would not
need to change their API's in incompatable ways.

exceptions being things like replacing short parameters with
types like uid_t, gid_t and pid_t for foward compatability.
It was really a pain to go from 16-bit to 32-bit ids in the
SVR4 timeframe - many applications were hardcoded using
short int's.


>
>It just not particularly sensible: The next SIGALRM received by a process
>after a call to alarm (or similar) returned should correspond to the
>most recently programmed alarm.

Note that the ability to block/mask/hold signals is relatively recent in the
Unix timeline and was part of the POSIX 1003.4 real-time extensions
that brought us pthreads.

Prior to that, the signal would be delivered as soon as it was
generated (generally on return from the kernel after a system
call by the application). So there was no way to get an ALARM
from a prior alarm() call after calling alarm() the second time
in pre SVR4.2 Unix systems.


Geoff Clare

unread,
Mar 22, 2021, 9:41:05 AM3/22/21
to
Scott Lurndal wrote:

> Note that the ability to block/mask/hold signals is relatively recent in the
> Unix timeline and was part of the POSIX 1003.4 real-time extensions
> that brought us pthreads.

No, you must be thinking of signal queueing and siginfo_t.

Blocking/masking with sigprocmask() has been in POSIX.1 since the
original 1988 standard.

--
Geoff Clare <net...@gclare.org.uk>

Philip Guenther

unread,
Mar 22, 2021, 5:47:38 PM3/22/21
to
On Monday, March 22, 2021 at 4:41:05 AM UTC-9, Geoff Clare wrote:
> Scott Lurndal wrote:
>
> > Note that the ability to block/mask/hold signals is relatively recent in the
> > Unix timeline and was part of the POSIX 1003.4 real-time extensions
> > that brought us pthreads.
> No, you must be thinking of signal queueing and siginfo_t.
>
> Blocking/masking with sigprocmask() has been in POSIX.1 since the
> original 1988 standard.

...and at least one predecessor, sigblock()/sigsetmask() was present in 4.2BSD, while XPG had sighold()/sigrelse()/...


Philip Guenther
0 new messages