torek@elf.ee.lbl.gov (Chris Torek) (05/11/91)
[nb: include files removed in the quotes below] >>In article <1489@cvbnetPrime.COM> mkaminsk@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@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@rtfm.Princeton.EDU> pfalstad@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@ee.lbl.gov