From owner-cvs-lib Sun May 18 00:52:50 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.5/8.8.5) id AAA16948 for cvs-lib-outgoing; Sun, 18 May 1997 00:52:50 -0700 (PDT) Received: from spinner.DIALix.COM (spinner.dialix.com [192.203.228.67]) by hub.freebsd.org (8.8.5/8.8.5) with ESMTP id AAA16920; Sun, 18 May 1997 00:52:29 -0700 (PDT) Received: from spinner.DIALix.COM (localhost.dialix.com.au [127.0.0.1]) by spinner.DIALix.COM with ESMTP id PAA05367; Sun, 18 May 1997 15:51:50 +0800 (WST) Message-Id: <199705180751.PAA05367@spinner.DIALix.COM> X-Mailer: exmh version 2.0gamma 1/27/96 To: =?KOI8-R?B?4c7E0sXKIP7F0s7P1w==?= cc: Bruce Evans , 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 In-reply-to: Your message of "Sun, 18 May 1997 11:28:07 +0400." Date: Sun, 18 May 1997 15:51:49 +0800 From: Peter Wemm Sender: owner-cvs-lib@FreeBSD.ORG X-Loop: FreeBSD.org Precedence: bulk =?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 > > http://www.nagual.pp.ru/~ache/ Cheers, -Peter