setitimer vs select (was: talk session)
Chris Torek
torek at elf.ee.lbl.gov
Sat May 11 15:47:43 AEST 1991
[nb: include files removed in the quotes below]
>>In article <1489 at cvbnetPrime.COM> mkaminsk at cvbnet.prime.com (mailhost) writes:
>>: struct timeval timeout;
>>: timeout.tv_sec = 0;
>>: timeout.tv_usec = number_of_microseconds;
>>: select(1, NULL, NULL, NULL, &timeout);
>natha at hal.com (Nathaniel Ingersol) wrote:
>>Am I missing something here, or why can't you just make a usleep out
>>of setitimer? You could even use ITIMER_REAL.
setitimer is considerably less efficient:
In article <69033 at rtfm.Princeton.EDU> pfalstad at phoenix.princeton.edu
(Paul Falstad) writes:
>You could just do this, yes.
>
>int handler(){;} /* needed to make pause() work */
>
>usleep(unsigned usec) {
> struct itimerval ix;
> ix.it_value.tv_sec = 0;
> ix.it_value.tv_usec = usec;
> ix.it_interval.tv_sec = 0;
> ix.it_interval.tv_usec = 0;
> signal(SIGALRM,handler);
> setitimer(ITIMER_REAL,&ix,NULL);
> pause();
> signal(SIGALRM,SIG_DFL);
>}
>
>Looks harder to me. :-)
Actually, the usleep() above is unreliable, and you need something
more like this:
static struct sigvec prev;
static int caught; /* must be volatile in newer systems */
static int handler() /* void in newer systems */
{
(void) sigvec(SIGALRM, &prev, (struct sigvec *)NULL);
caught = 1;
}
int usleep(unsigned usec) {
struct itimerval ix, oix;
int saverr, again;
long omask;
struct sigvec saveprev, new;
/* many of the previous must be volatile in newer systems */
ix.it_value.tv_sec = usec / 1000;
ix.it_value.tv_usec = usec % 1000;
ix.it_interval.tv_sec = 0;
ix.it_interval.tv_usec = 0;
/* block interrupts so we can change state safely */
omask = sigblock(sigmask(SIGALRM));
/* try to set new and get old state */
if (setitimer(ITIMER_REAL, &ix, &oix)) {
saverr = errno;
(void) sigsetmask(omask);
errno = saverr;
return (-1);
}
/* see whether we have to correct for old state */
again = 0;
correct = 0;
if (oix.it_interval.tv_sec || oix.it_interval.tv_usec) {
/* old itimer had a reload value, which we must
restore to keep the timer from drifting back.
if old timer had a longer interval, we should
use the difference between our value and
its interval instead, but it then becomes
excessively difficult to reinstate the previous
interval. */
again = 1;
ix.it_interval = oix.it_interval;
}
if (oix.it_value.tv_sec < ix.it_value.tv_sec ||
oix.it_value.tv_sec == ix.it_value.tv_sec &&
oix.it_value.tv_usec < ix.it_value.tv_usec) {
/* previous alarm would fire before ours.
unclear how to handle this; we set ours
to the (smaller) old value. */
again = 1;
ix.it_value = oix.it_value;
} else if (oix.it_value.tv_sec || oix.it_value.tv_usec) {
/* there was a previous alarm, but it occurs
after ours. Remember the difference between
ours and its. This cause drift, but the
drift will vanish on the reload. If the
reload is shorter than the next scheduled
alarm, however, we should use the next
scheduled alarm instead. */
if (oix.it_value.tv_sec > oix.it_interval.tv_sec ||
oix.it_value.tv_sec == oix.it_interval.tv_sec &&
oix.it_value.tv_usec > oix.it_interval.tv_usec) {
/* I am too tired to keep this up.
Someone Out There will have to finish
this. Sorry. */
}
}
(void) setitimer(ITIMER_REAL, &ix, (struct itimerval *)NULL);
saveprev = prev;
new.sv_handler = handler;
new.sv_mask = 0;
new.sv_flags = 0;
/* cross fingers -- we are too far to bother backing out */
(void) sigvec(SIGALRM, &new, &prev);
/* okay, everything is set */
do {
/* atomically enable and wait for signal */
sigpause(omask);
/* keep doing it until we got the right one */
} while (!caught);
/* now that we are done sleeping, we must restore old state
(the alarm has fired exactly once, although the reload
interval may have loaded a new shorter time) */
prev = saveprev;
caught = 0;
if (correct) {
/* if we need to change the interval back, here is
where to do it. (see `tired' above) */
}
/* Finally, all state is back to where it was before we
started. Release the block on SIGALRM, and return 0. */
sigsetmask(omask);
return (0);
}
select() is MUCH simpler; the signal method requires, at a minimum
(you can drop much of the state saving for the usual case, where
signal handlers are not multiply nested, running on several stacks,
making heavy use of blocking masks, etc.), five separate system calls:
block signal
set handler
set timer
sigpause
unblock signal
The select() technique makes only one system call.
--
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA Domain: torek at ee.lbl.gov
More information about the Comp.unix.questions
mailing list