donn@sdchema.UUCP (07/10/83)
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@nprdc PS I wonder how hard it would be to bring V6 up on a VAX, anyway? I suppose Whitesmiths has done this already...