edw@IUS1.CS.CMU.EDU (Eddie Wyatt) (11/09/87)
/************************************************************************** * * * msleep * * * ************************************************************************** Purpose : What you all been waiting for -- A sleep that works in microseconds!!!. Actually it depends on the granularity of the interrupt timer, but at least its better than the second wait. Programmer : Eddie Wyatt (orginally written by Paul Allen) Date : November 1987 Input : sec - number of seconds to sleep msec - number of micro seconds to sleep Output : None Locals : off - itimer value to turn off the timer interrupt. time - wait time oldtime - old itime value bob - the old interrupt handler Globals : ************************************************************************/ #include <signal.h> #include <sys/time.h> /* ARGSUSED */ static int signal_handler(sig,code,scp) int sig, code; struct sigcontext *scp; { return(1); } static struct itimerval off = {{0,0},{0,0}}; void msleep(sec,msec) int sec, msec; { struct itimerval time; struct itimerval oldtime; int (*bob)(); if ((sec == 0) && (msec == 0)) return; /* note that we have the timer interrupting continously as oppose to just once. This is because if we were to install the itimer to interrupt just once, there would be a critial section (the space between setitimer and sigpause) where if the interrupt sound then, it would be lost and we would wait forever in sigpause */ time.it_interval.tv_sec = sec; time.it_interval.tv_usec = msec; time.it_value.tv_sec = sec; time.it_value.tv_usec = msec; /* insert my interrupt handler and itimer val (order matters) while saving old values to be re-instated */ bob = signal(SIGALRM, signal_handler); if (setitimer(ITIMER_REAL, &time, &oldtime) < 0) perror("setitimer"); /* wait until an interrupt happens, hopefully a timer interrupt - but it need not be. */ sigpause(0); /* turn timer off while returning timer and interrupt handler back to the old state (order matters) */ if (setitimer(ITIMER_REAL, &off, (struct itimerval *) 0) < 0) perror("setitimer"); if ((int) signal(SIGALRM, bob) == -1) perror("signal"); if (setitimer(ITIMER_REAL, &oldtime, (struct itimerval *) 0)) perror("setitimer"); } -- That one looks Jewish -- And that one's a coon -- Who let all this riff raff into the room -- There's one smoking a joint -- and Anoter with spots -- If I had my way -- I'd have all of you shot ... Pink Floyd, "In the Flesh" Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
chris@mimsy.UUCP (Chris Torek) (11/12/87)
In article <332@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes: >/************************************************************************** > * * > * msleep * > * * > ************************************************************************** > > Purpose : What you all been waiting for -- A sleep that works in > microseconds!!!. Actually it depends on the granularity > of the interrupt timer, but at least its better than > the second wait. ... followed by code that uses setitimer. 0) Only certain 4BSD-based operating systems *have* setitimer. (What this *really* means is that if you need a sleep that works in milliseconds or microseconds or whatever, using a routine like msleep is imperative. Then the victim, er, user, of your code need rewrite only msleep.) 1) you can (and, I think, should) avoid the critical section by using sigblock and sigsetmask (see man 2 sigblock). 2) using setitimer is more expensive than calling select with three `(fd_set *)0' arguments. Select does have one problem: like sigpause, it is interrupted by signals; unlike sigpause, it does not take a signal mask, so it sometimes has unavoidable critical sections. (Yes, I want a sixth argument to select!) 3) The actual granularity of interval timers (including select timers): system granularity 4.[23]BSD Vax 10 ms Sun[23] running SunOS 3.x 20 ms -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
edw@IUS1.CS.CMU.EDU (Eddie Wyatt) (11/17/87)
In article <9310@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes: > > 0) Only certain 4BSD-based operating systems *have* setitimer. > (What this *really* means is that if you need a sleep that works > in milliseconds or microseconds or whatever, using a routine > like msleep is imperative. Then the victim, er, user, of your > code need rewrite only msleep.) > > 1) you can (and, I think, should) avoid the critical section by using > sigblock and sigsetmask (see man 2 sigblock). > > 2) using setitimer is more expensive than calling select with three > `(fd_set *)0' arguments. Select does have one problem: like > sigpause, it is interrupted by signals; unlike sigpause, it does > not take a signal mask, so it sometimes has unavoidable critical > sections. (Yes, I want a sixth argument to select!) > > 3) The actual granularity of interval timers (including select timers): > > system granularity > 4.[23]BSD Vax 10 ms > Sun[23] running SunOS 3.x 20 ms > -- > In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) > Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris Sorry about #0, I didn't know SYSV doesn't provide itimers. #1 Sorry, I think critical section was a misnomer for what I meant. The problem that I was describing had to do with setting the itimer up such that it interrupts only once (the second half of the structure set to zeros). If the ONLY timer interrupt was to come during the period between the call to setitimer and sigpause, my interrupt handle would be called and then NOTHING would happen in sigpause to make sigpause exit. The only timer interrupt sounded would not have caused sigpause to exit, since it sounded BEFORE entering into sigpause. This is the rational behind using continuous interrupts (the second half of the structure not set to zeros) when at first glance, someone might think you could get away with just one. BTW, yes I'm very familiar with the sigblock and sigsetmask functions, they are not needed to implement this facility, setitimer suffices. Point two about setitimer being slower. Does that really make sense to complain about how long setitimer takes as compared to select!!! If the interrupt timer's granularity is 10 millisecs, both select and msleep will usually time out at the same time (setitimer has to be in the order of 100 micros per call). Statistically though, msleep should have a mean wait time that is a little longer than select. Also, don't you consider the use of select as a timer to be an abuse of the function? Then again, are you one that argues, vfork is avid way to get shared mem in BSD. ;-) -- That one looks Jewish -- And that one's a coon -- Who let all this riff raff into the room -- There's one smoking a joint -- and Anoter with spots -- If I had my way -- I'd have all of you shot ... Pink Floyd, "In the Flesh" Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
davel@hpcupt1.HP.COM (Dave Lennert) (11/17/87)
> 2) using setitimer is more expensive than calling select with three > `(fd_set *)0' arguments. Select does have one problem: like > sigpause, it is interrupted by signals; unlike sigpause, it does > not take a signal mask, so it sometimes has unavoidable critical > sections. (Yes, I want a sixth argument to select!) > -- > In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) > Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris > ---------- In reality one would like every system call to accept a signal mask (like what sigpause() does for pause()). However, it is not impossible to code reliably; setjmp()...longjmp() work acceptably. You can't code reliably in the presense of signals (especially SIGALRM) without using setjmp()...longjmp() (with the one exception of sigpause()). If you timeout a system call other than sigpause() by arming SIGALRM and then immediately invoking the system call (without setjmp()) you have a race condition. -Dave Lennert HP ihnp4!hplabs!hpda!davel
chris@mimsy.UUCP (Chris Torek) (11/18/87)
In article <6060001@hpcupt1.HP.COM> davel@hpcupt1.HP.COM (Dave Lennert) writes: >In reality one would like every system call to accept a signal mask >(like what sigpause() does for pause()). However, it is not impossible >to code reliably; setjmp()...longjmp() work acceptably. Not so. There are two problems here; I shall describe the harder one first. In the presence of longjmp, C needs an unwind-protect mechanism. Example: The 4.3BSD fprintf routine temporarily buffers unbuffered streams. This is often a major performance win, since stderr is normally unbuffered. The problem is illustrated by the code (names changed to protect the innocent): fprintf(FILE *f, <other details left out>) { unsigned char buf[BUFSIZ]; int ret; int we_buffered_it = 0; if ((f->flags & BUFFERED) == 0) { f->buf = buf; f->bufsize = BUFSIZ; f->flags |= BUFFERED; we_buffered_it = 1; } ret = _doprnt(...); if (we_buffered_it) { f->buf = NULL; f->flags &= ~BUFFERED; } return (ret); } Aside from deferring all signals for the duration of _doprnt() (a bad idea!), there is no way to prevent a longjmp() that leaps out of _doprnt() and fprintf() from trashing FILE *f. With unwind-protect you might do this: unsigned char buf[BUFSIZ]; int ret, omask, we_buffered_it = 0; if ((f->flags & BUFFERED) == 0) { omask = sigblock(ALLSIGS); if (unwind_protect()) { f->buf = NULL; f->flags &= ~BUFFERED; return; /* continue longjmp-ing */ } f->buf = buf; f->bufsize = BUFSIZ; f->flags |= BUFFERED; we_buffered_it = 1; (void) sigsetmask(omask); } ret = _doprnt(...); if (we_buffered_it) { omask = sigblock(ALLSIGS); f->buf = NULL; f->flags &= ~BUFFERED; (void) sigsetmask(omask); } return (ret); The idea is that longjmp() would, as it unwinds stack frames, notice markers left by unwind-protect calls and `return' om there to let the function clean up after itself. Returning from the protection code would resume the longjmp. Back to the easier problem: >In reality one would like every system call to accept a signal mask >(like what sigpause() does for pause()). This also is not true. Any system call that is restarted after a signal does not need a signal mask, simply because having one would make no difference. In this case you *must* use longjmp to abort the system call (which leaves you with the unwind-protect problem above). Adding unwind-protect would fix both problems, actually. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
davel@hpcupt1.UUCP (12/05/87)
> / chris@mimsy.UUCP (Chris Torek) / 12:38 pm Nov 18, 1987 / > > In the presence of longjmp, C needs an unwind-protect mechanism. This is an intriguing idea. I like it. But it does require that all functions used (e.g., from a library) are correctly coded to use the unwind-protect mechanism as needed. I guess that goes without saying (so I said it ;-) ). My posting was meant to assume that standard unix signal handling precautions were being followed. It is known (and even documented in Posix !) that it is unsafe in the general case to do anything in a signal handler but the most simple things. (For example, set a variable or longjmp to a state which itself does trivial cleanup and exit.) (Worrying about the general case is very important for a library routine which establishes signal handlers, e.g., sleep().) However, if the application limits what is going on during the interval of time when the signal handler can be entered (e.g., make sure you aren't in stdio) then you have more freedom in the signal handler. Specifically, I was discussing the case where SIGALRM was used to timeout a system call. The longjmp() in this case merely returns to the same routine that invoked the system call; no stack frames are unwound at all. Thank you for pointing out the pitfalls in the general case and for describing the unwind-protect mechanism. -Dave Lennert HP ihnp4!hplabs!hpda!davel