Skip site navigation (1)Skip section navigation (2)
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>