Date: Sun, 18 May 1997 15:51:49 +0800 From: Peter Wemm <peter@spinner.DIALix.COM> To: =?KOI8-R?B?4c7E0sXKIP7F0s7P1w==?= <ache@nagual.pp.ru> Cc: Bruce Evans <bde@zeta.org.au>, peter@FreeBSD.ORG, cvs-all@FreeBSD.ORG, cvs-committers@FreeBSD.ORG, cvs-lib@FreeBSD.ORG Subject: Re: cvs commit: src/lib/libc/gen sleep.c Message-ID: <199705180751.PAA05367@spinner.DIALix.COM> In-Reply-To: Your message of "Sun, 18 May 1997 11:28:07 %2B0400." <Pine.BSF.3.96.970518105438.711B-100000@nagual.pp.ru>
next in thread | previous in thread | raw e-mail | index | archive | help
=?KOI8-R?B?4c7E0sXKIP7F0s7P1w==?= wrote:
> On Sun, 18 May 1997, Peter Wemm wrote:
>
> > The SVR4.0 man page for sleep(3c) says:
> > ...
> > The routine is implemented by setting an alarm signal and pausing unt
il
> > it (or some other signal) occurs. The previous state of the alarm si
gnal
> > is saved and restored. The calling program may have set up an alarm
> > signal before calling sleep. [..guff about adjusting alarm(2)..]
> > ...
> >
> > ie: traditional sleep(3) behavior is that any signal interrupts the sleep.
>
> Hmm. It looks like SYSVism.
No..
> SunOS man page for sleep(3b) says:
>
> SIGALRM should _not_ be blocked or ignored during a call to
> sleep(). Only a prior call to alarm(2) should generate
> SIGALRM for the calling process during a call to sleep(). A
> signal-catching function should _not_ interrupt a call to
> sleep() to call siglongjmp(3C) or longjmp(3C) to restore an
> environment saved prior to the sleep() call.
This is just saying "don't block SIGALRM before calling sleep", and "don't
longjump out of a signal handler while a sleep is active". it doesn't say
that signals wont make the sleep return early.
> and old implementation don't pay attention on catched signals too.
> BTW, I am not shure we should keep it.
That's what I've been suggesting too, but we shouldn't break ourselves
when running a program that expects traditional behavior. :-)
FWIW, I can't find any other sleep() implementation that always returns
zero like ours has done up to now.
> In any case, _ignored_ signals should not interrupt sleep.
Hmm.. Are we doing this??
My current version of sleep(3) does this:
peter@spinner[3:40pm]~src/bin/sleep-275# sleep 1000 &
[1] 5301
peter@spinner[3:40pm]~src/bin/sleep-276# kill -URG 5301
peter@spinner[3:40pm]~src/bin/sleep-277# kill -ALRM 5301
[1] Done sleep 1000
peter@spinner[3:41pm]~src/bin/sleep-278# sleep 1000 &
[1] 5302
peter@spinner[3:41pm]~src/bin/sleep-279# kill -TERM 5302
[1] Terminated sleep 1000
peter@spinner[3:41pm]~src/bin/sleep-280#
(URG is ignored by default, and isn't interrupting sleep(3))
> There are some other maybe useful notes from the same place:
>
> sleep() is implemented by setting an interval timer and
> pausing until it expires. The previous state of this timer
> is saved and restored. If the sleep time exceeds the time
> to the expiration of the previous value of the timer, the
> process sleeps only until the timer would have expired, and
> the signal which occurs with the expiration of the timer is
> sent one second later.
Yes, if a SIGALRM arrives during sleep() from an outside source, we should
end the sleep rather than exit if the alarm signal isn't trapped.
However, I don't see how to do this without races while using nanosleep().
It could be possible to get a SIGALRM right after installing the handler
but before the nanosleep() starts.
> I see GNU libc sleep.c does some additional efforts to handle alarms
> which are set before sleep is started:
>
> /* SIGALRM signal handler for `sleep'. This does nothing but return,
> but SIG_IGN isn't supposed to break `pause'. */
> static void
> sleep_handler (sig)
> int sig;
> {
> return;
> }
My current version basically does this too..
> /* Make the process sleep for SECONDS seconds, or until a signal arrives
> and is not ignored. The function returns the number of seconds less
> than SECONDS which it actually slept (zero if it slept the full time).
> If a signal handler does a `longjmp' or modifies the handling of the
> SIGALRM signal while inside `sleep' call, the handling of the SIGALRM
> signal afterwards is undefined. There is no return value to indicate
> error, but if `sleep' returns SECONDS, it probably didn't work. */
> unsigned int
> sleep (seconds)
> unsigned int seconds;
> {
> unsigned int remaining, slept;
> time_t before, after;
> sigset_t set, oset;
> struct sigaction act, oact;
> int save = errno;
>
> if (seconds == 0)
> return 0;
>
> /* Block SIGALRM signals while frobbing the handler. */
> if (sigemptyset (&set) < 0 ||
> sigaddset (&set, SIGALRM) < 0 ||
> sigprocmask (SIG_BLOCK, &set, &oset))
> return seconds;
>
> act.sa_handler = sleep_handler;
> act.sa_flags = 0;
> act.sa_mask = oset; /* execute handler with original mask */
> if (sigaction (SIGALRM, &act, &oact) < 0)
> return seconds;
>
> before = time ((time_t *) NULL);
> remaining = alarm (seconds);
>
> if (remaining > 0 && remaining < seconds)
> {
> /* The user's alarm will expire before our own would.
> Restore the user's signal action state and let his alarm happen. */
> (void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);
> alarm (remaining); /* Restore sooner alarm. */
> sigsuspend (&oset); /* Wait for it to go off. */
> after = time ((time_t *) NULL);
> }
> else
> {
> /* Atomically restore the old signal mask
> (which had better not block SIGALRM),
> and wait for a signal to arrive. */
> sigsuspend (&oset);
>
> after = time ((time_t *) NULL);
>
> /* Restore the old signal action state. */
> (void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);
> }
>
> /* Notice how long we actually slept. */
> slept = after - before;
>
> /* Restore the user's alarm if we have not already past it.
> If we have, be sure to turn off the alarm in case a signal
> other than SIGALRM was what woke us up. */
> (void) alarm (remaining > slept ? remaining - slept : 0);
>
> /* Restore the original signal mask. */
> (void) sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
>
> /* Restore the `errno' value we started with.
> Some of the calls we made might have failed, but we didn't care. */
> __set_errno (save);
>
> return slept > seconds ? 0 : seconds - slept;
> }
My current version is compatable with this, *except* that it doesn't seem
convenient to mask SIGALRM while preparing to call nanosleep(). Doing a
longjump out of the signal handler seems like overkill...
I'm using this at present:
#define setvec(vec, a) \
vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0
static void
sleephandler()
{
return; /* interrupt nanosleep() */
}
unsigned int
sleep(seconds)
unsigned int seconds;
{
struct timespec time_to_sleep;
struct timespec time_remaining;
struct sigvec vec, ovec;
if (seconds != 0) {
time_to_sleep.tv_sec = seconds;
time_to_sleep.tv_nsec = 0;
setvec(vec, sleephandler);
(void) sigvec(SIGALRM, &vec, &ovec);
nanosleep(&time_to_sleep, &time_remaining);
(void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
seconds = time_remaining.tv_sec;
if (time_remaining.tv_nsec > 0)
seconds++; /* round up */
}
return (seconds);
}
> --
> Andrey A. Chernov
> <ache@null.net>
> http://www.nagual.pp.ru/~ache/
Cheers,
-Peter
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199705180751.PAA05367>
