Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 18 May 1997 11:28:07 +0400 (MSD)
From:      =?KOI8-R?B?4c7E0sXKIP7F0s7P1w==?= <ache@nagual.pp.ru>
To:        Peter Wemm <peter@spinner.dialix.com>
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:  <Pine.BSF.3.96.970518105438.711B-100000@nagual.pp.ru>
In-Reply-To: <199705180620.OAA04512@spinner.DIALix.COM>

next in thread | previous in thread | raw e-mail | index | archive | help
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 until
>       it (or some other signal) occurs.  The previous state of the alarm signal
>       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.

SunOS man page for sleep(3b) says:

     SIGALRM should _n_o_t 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  _n_o_t  interrupt  a call to
     sleep() to call siglongjmp(3C) or longjmp(3C) to restore  an
     environment saved prior to the sleep() call.

and old implementation don't pay attention on catched signals too.
BTW, I am not shure we should keep it.
In any case, _ignored_ signals should not interrupt sleep.

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.

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;
}

/* 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;
}

-- 
Andrey A. Chernov
<ache@null.net>
http://www.nagual.pp.ru/~ache/




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.3.96.970518105438.711B-100000>