Problems with sleep()

donn at sdchema.UUCP donn at sdchema.UUCP
Sun Jul 10 20:06:52 AEST 1983


I don't see why there need be a problem with alarms coming before a
pause() is started in sleep().  One way to solve this on 4.n BSD is to
use the fancy signal package.  The extended signal() call, sigsys(),
allows you to 'hold' signals:  this lets you block signals without
throwing them away.  The next time you change the signal status to
allow a 'held' signal to be caught, the signal will take effect.  There
is yet another option which lets you combine a signal() operation with
a pause(), so that immediately after changing the status of a signal
your process will pause().  The two of these together let you define a
sleep() routine that doesn't have any holes.  My effort at such a
routine is the following:

sleep( secs )
	unsigned int	secs;
{
	int		(*sigsys())();
	int		(*func)();
	int		_nullf();
	unsigned int	alarm();
	unsigned int	osecs;

	if ( secs == 0 )
		return;

	/*
	 * Catch alarm signals and identify the handler, if any.
	 */
	osecs		= alarm( 0 );
	func		= sigsys( SIGALRM, SIG_HOLD );

	/*
	 * If there is an alarm pending that would go off while we slept,
	 *	fake things so that we only sleep until the alarm.  The
	 *	SIGDOPAUSE causes a pause() after the sigsys().
	 */
	if ( osecs > 0 && secs >= osecs ) {
		alarm( osecs );
		sigsys( SIGALRM | SIGDOPAUSE, func );
		return;
	}

	/*
	 * Go to sleep.  Hold alarms again when we wake up (using DEFERSIG).
	 */
	alarm( secs );
	sigsys( SIGALRM | SIGDOPAUSE, DEFERSIG(_nullf) );

	/*
	 * Reset the alarm and the signal handler.
	 */
	alarm( osecs > secs ? osecs - secs : 0 );
	sigsys( SIGALRM, func );
}

_nullf()
{
}

This version of sleep() is immune to the infinite pause bug.  At worst
the alarm occurs early and gets held until the sigsys() is performed,
at which time the implied pause() ends immediately.

Of course the ordinary 4.n BSD sleep() routine avoids races between
alarm() and pause(), too.  Here is a rough idea of what it does:

...
	/*
	 * We return here after an alarm.  The body of the if is
	 *	(only) performed then.
	 */
	if ( setjmp( jmp ) ) {
		signal( SIGALRM, oldsig );
		return;
	}

	/*
	 * Set the signal BEFORE setting the alarm.  If the alarm
	 *	goes off before we have a chance to pause, no problem;
	 *	the handler is already prepared.
	 */
	oldsig		= signal( SIGALRM, sleepy );
	alarm( secs );
	while ( 1 )
		pause();
...
sleepy()
{
	longjmp( jmp, 1 );
}

As umcp-cs!chris points out, this has the difficulty that if some other
signal comes in while we are asleep and the handler for that signal
doesn't realize that the alarm is still ticking, when the alarm goes
off the longjmp will clear the stack and we will return from sleep()
instead of finishing our work in the other signal handler.  On the
other hand, the sleep() routine that uses sigsys() will not have this
problem.  If the alarm goes off while the hangup handler is running,
only the user's function 'func' or the innocent little function
_nullf() will be executed; no unexpected non-local gotos can take
place.

Don't go leaping to install the sigsys() version of sleep(), because it
has a few problems yet.  For example, I have not put in code to check
the result of pausing: it is possible that some other signal broke the
implied pause(), in which case the alarm signal may still be pending.
There are a few things we could do in this circumstance.  It might be
useful to have sleep() return immediately, like pause(), in which case
we just reset the alarm and return.  Alternatively we can restart the
alarm and try to finish.  (After returning from a paused sigsys(), we
check the alarm clock, turning it off at the same time to prevent
surprises.  If time remains on it, then some other signal has caused us
to wake up.  In this case we need to hold alarm signals, adjust the
clock and loop back to the paused sigsys().)  The current 4.n BSD
sleep() waits for the alarm rather than terminating early.  (The way
the 4.n BSD sleep() is done makes it so that another signal handler
can't just turn off the alarm clock and expect the sleep to finish --
in fact this will cause the 4.n BSD sleep() to hang.  The other handler
must execute a non-local goto to terminate the sleep.  As outlined
above, the sigsys() version of sleep() will return if the alarm clock
is turned off.)

At any rate, it's hardly necessary to go back to V6 and recreate the
sleep() system call.

Donn Seeley  UCSD Chemistry Dept. RRCF  ucbvax!sdcsvax!sdchema!donn
             (619) 452-4016             sdamos!donn at nprdc

PS  I wonder how hard it would be to bring V6 up on a VAX, anyway?
I suppose Whitesmiths has done this already...



More information about the Comp.unix.wizards mailing list