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>