[net.bugs.4bsd] alarm

jeff@voder.UUCP (07/10/84)

There is a bug in the 4.2 implementation of alarm(3).  In the following

	oldalarm = alarm(newalarm);

oldalarm gets the current value of the interval timer *rounded down*
to the nearest second.  So ...

	for (...) {
		oldalarm = alarm((unsigned) 0);
		...
		alarm(oldalarm);	
	}

causes the interval timer to be *repeatedly* rounded down.  End
result: the alarm goes off early.  And, of course, if the interval
timer just *happened* to be at some value *less* than a second the
alarm will be turned off completely.

Fortunately, there is an easy workaround; change the previous code
to read:

	for (...) {
		oldmask = sigblock(1<<SIGALRM);
		...
		sigblock(oldmask);
	}

The fix to alarm() is left as an exercise to the interested reader.

Jeff Gilliam

No pain, no gain ...	*sigh*

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (07/11/84)

The following fixes the round-down behavior of 4.2BSD alarm(3):

/*
	alarm -- system call emulation for 4.2BSD

	last edit:	14-Dec-1983	D A Gwyn
*/

extern int	_setitimer();

typedef struct
	{
	unsigned long	tv_sec;		/* seconds */
	long		tv_usec;	/* microseconds */
	}	timeval;

typedef struct
	{
	timeval		it_interval;	/* timer interval */
	timeval		it_value;	/* current value */
	}	itimerval;

unsigned
alarm( sec )
	unsigned	sec;		/* timeout in seconds */
	{
	itimerval	newit;		/* new interval data */
	itimerval	oldit;		/* old interval data */

	/* set alarm clock timeout interval (0 disables) */
	newit.it_value.tv_sec = (unsigned long)sec;
	newit.it_value.tv_usec = 0L;

	/* avoid retriggering once timer expires */
	newit.it_interval.tv_sec = 0L;
	newit.it_interval.tv_usec = 0L;

	if ( _setitimer( 0, &newit, &oldit ) < 0 )	/* real time */
		return -1;
	/* SIGALRM now pending */

	return (unsigned)oldit.it_value.tv_sec +
			(oldit.it_value.tv_usec > 500L ? 1 : 0);
	}

muller@sdccsu3.UUCP (Keith Muller) (07/11/84)

> Fortunately, there is an easy workaround; change the previous code
> to read:
> 
>	 for (...) {
>		 oldmask = sigblock(1<<SIGALRM);
>		 ...
>		 sigblock(oldmask);
>	 }

I should have looked closer as there is two bugs: (SIGALRM and the second
sigblock should be a sigsetmask)

	for (...) {
		oldmask = sigblock(1 << (SIGALRM - 1));
		....
		(void)sigsetmask(oldmask);
	}

Sigblock and sigsetmask together provide a nice way to encapsulate a
critical section to protect them from the delivery of asynchronous signals. 

Along the same grain, sigpause provides a neat way to set a signal mask
in an *atomic* manner:
	
	oldmask = sigblock(1 << (SIGALRM - 1));
	(void)setitimer(.....);
	sigpause(oldmask);

The above code is the only *sure* way to use sigpause with SIGALRM. As
you must block against delivery of the SIGALRM between the setitimer()
call and the sigpause(). If you don't the program will intermittantly
hang forever at the sigpause() (as the SIGALRM *can* be delivered *between*
the calls to setitime() and sigpause()).
Note that sigpause will restore the signal mask to oldmask after the
arrival of the SIGALRM.

		Keith Muller
		UCSD Computer Center

muller@sdccsu3.UUCP (Keith Muller) (07/17/84)

> Fortunately, there is an easy workaround; change the previous code
> to read:
>
>	for (...) {
>		oldmask = sigblock(1<<SIGALRM);
>		...
>		sigblock(oldmask);
>	}

This won't work, this will block off SIGTERM not SIGALRM. Sigblock()
requires that to block off signal i the i-th bit in the mask must
be set. (The bits are numbered from 1 to n). So that line must read:

		oldmask = sigblock(1 << (SIGALRM - 1));

Note this easily confused with select()'s mask which does NOT need to
have 1 subtracted since descriptors are numbered from 0 to n.

		Keith Muller
		UCSD Computer Center