[net.bugs.4bsd] New version of sleep

chris@umcp-cs.UUCP (Chris Torek) (12/02/84)

I looked at the source, fiddled with it, and so forth.  Here's a version
of sleep that should have no holes.  I'd post the whole thing but then
the lawyers would come after me, so you'll have to make do with context
diffs (which show almost all of it anyway):

*** sleep.c.old	Sun Sep 25 21:05:20 1983
--- sleep.c	Sat Dec  1 18:36:35 1984
***************
*** 8,11
  #define	mask(s)	(1<<((s)-1))
- #define	setvec(vec, a) \
- 	vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0
  

--- 8,9 -----
  #define	mask(s)	(1<<((s)-1))
  
***************
*** 14,16
  sleep(n)
! 	unsigned n;
  {

--- 12,14 -----
  sleep(n)
! 	unsigned int n;
  {
***************
*** 21,22
  
  	if (n == 0)

--- 19,23 -----
  
+ 	/*
+ 	 * If not sleeping at all, return immediately.
+ 	 */
  	if (n == 0)
***************
*** 23,24
  		return;
  	timerclear(&itp->it_interval);

--- 24,31 -----
  		return;
+ 	/*
+ 	 * We must look for any pending alarms, and set things
+ 	 * up so that the same alarm (or one as close as possible)
+ 	 * will go off after we are done.  What we'll do is set
+ 	 * a zero timer and get the previous one at the same time.
+ 	 */
  	timerclear(&itp->it_interval);
***************
*** 27,30
  		return;
! 	setvec(ovec, SIG_DFL);
! 	omask = sigblock(0);
  	itp->it_value.tv_sec = n;

--- 34,44 -----
  		return;
! 	/*
! 	 * Now we have all the info we need.  At this point we
! 	 * figure out when our alarm clock should go off.  We
! 	 * use the minimum of (n, oitv) to pretend compatibility
! 	 * with 4.1 and earlier, and (for more compatiblity) we
! 	 * will use one second as the restoration value for the
! 	 * alarm signal if we decide we should return early
! 	 * (that is, if oitv < n).
! 	 */
  	itp->it_value.tv_sec = n;
***************
*** 31,32
  	if (timerisset(&oitv.it_value)) {
  		if (timercmp(&oitv.it_value, &itp->it_value, >))

--- 45,51 -----
  	if (timerisset(&oitv.it_value)) {
+ 		/*
+ 		 * After we are done, we should send one more
+ 		 * alarm signal, as close as possible to the
+ 		 * original scheduled signal.
+ 		 */
  		if (timercmp(&oitv.it_value, &itp->it_value, >))
***************
*** 34,36
  		else {
- 			itp->it_value = oitv.it_value;
  			/*

--- 53,54 -----
  		else {
  			/*
***************
*** 36,41
  			/*
! 			 * This is a hack, but we must have time to
! 			 * return from the setitimer after the alarm
! 			 * or else it'll be restarted.  And, anyway,
! 			 * sleep never did anything more than this before.
  			 */

--- 54,56 -----
  			/*
! 			 * Here we go off early, as explained above.
  			 */
***************
*** 41,42
  			 */
  			oitv.it_value.tv_sec = 1;

--- 56,58 -----
  			 */
+ 			itp->it_value = oitv.it_value;
  			oitv.it_value.tv_sec = 1;
***************
*** 45,47
  	}
! 	setvec(vec, sleepx);
  	(void) sigvec(SIGALRM, &vec, &ovec);

--- 61,70 -----
  	}
! 	/*
! 	 * Right now, we can do anything we please, since no
! 	 * alarm is pending.  We'll set a signal trap for SIGALRM
! 	 * which will set "ringring" so we'll know when it occurs.
! 	 */
! 	vec.sv_handler = sleepx;
! 	vec.sv_mask = 0;
! 	vec.sv_onstack = 0;
  	(void) sigvec(SIGALRM, &vec, &ovec);
***************
*** 47,49
  	(void) sigvec(SIGALRM, &vec, &ovec);
! 	ringring = 0;
  	(void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);

--- 70,78 -----
  	(void) sigvec(SIGALRM, &vec, &ovec);
! 	ringring = 0;	/* hasn't occurred yet */
! 	/*
! 	 * Now we get to the tricky part.  We have to make tests
! 	 * on ringring atomic, so block SIGALRM, *then* set the
! 	 * alarm timer, and then wait.
! 	 */
! 	omask = sigblock(mask(SIGALRM));
  	(void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);
***************
*** 49,50
  	(void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);
  	while (!ringring)

--- 78,88 -----
  	(void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);
+ 	/*
+ 	 * Wait by checking whether the alarm has gone off yet,
+ 	 * and if not, atomically release SIGALRM and await any
+ 	 * signal.  When the signal occurs, the handler (if any)
+ 	 * will be called, and on its return, sigpause will restore
+ 	 * the signal mask as it was before sigpause was entered,
+ 	 * and return to us.  So when our timer finally goes off,
+ 	 * ringring will be set before sigpause returns.
+ 	 */
  	while (!ringring)
***************
*** 51,52
  		sigpause(omask &~ mask(SIGALRM));
  	(void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);

--- 89,100 -----
  		sigpause(omask &~ mask(SIGALRM));
+ 	/*
+ 	 * Ok, we're done.  Now we just have to restore the state.
+ 	 * Order of operations is important here.  Right now,
+ 	 * SIGALRM is still blocked, so we can fiddle with it
+ 	 * and with interval timers.  What we'll do is reinstall
+ 	 * the old handler, then reenable the signal, then reset
+ 	 * the timer.  We could do the other two in the other
+ 	 * order but it may be better to call the handler as
+ 	 * soon as possible.
+ 	 */
  	(void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
***************
*** 52,53
  	(void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
  	(void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0);

--- 100,102 -----
  	(void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
+ 	(void) sigsetmask(omask);
  	(void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0);
***************
*** 58,60
  {
! 
  	ringring = 1;

--- 107,111 -----
  {
! 	/*
! 	 * Just set the "alarm went off" flag and return.
! 	 */
  	ringring = 1;
-- 
(This line accidently left nonblank.)

In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (301) 454-7690
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland