Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83; site sdccsu3.UUCP Path: utzoo!watmath!clyde!floyd!harpo!decvax!ittvax!dcdwest!sdcsvax!sdccsu3!muller From: muller@sdccsu3.UUCP Newsgroups: net.unix-wizards Subject: Re: bug with setitimer on 4.2 BSD? Message-ID: <1698@sdccsu3.UUCP> Date: Sat, 31-Mar-84 13:55:36 EST Article-I.D.: sdccsu3.1698 Posted: Sat Mar 31 13:55:36 1984 Date-Received: Sun, 1-Apr-84 08:23:51 EST References: <634@sdcsvax.UUCP> Organization: U.C. San Diego, Computer Center Lines: 87 There is not a problem with setitimer on 4.2, but in fact this is a classic critical section problem. The important issue in this example is the period that ITIMER_REAL is small: 30 millisecs. The call to setitimer causes a context switch to the kernel to start the counter. The counter is set and another context switch is performed back to the program. Once back in the program the sigpause is executed by performing another context switch to the kernal to wait for the signal from the counter. This is fine for time period values which are large in relation to the time required for the two context switches (from the kernal to the program after the timer was set, and from the program back to the kernel to execute the sigpause). However if the signal is delivered BEFORE the sigpause is executed, the handler is invoked, control flow returns to the main program, executes the sigpause, and the program waits forever for a signal that will never arrive! (The current ITIMER_REAL has expired). As with every critical section type problem you can never assume any "rate" at which a program executes. The "real time" that it takes the system to complete a context switch must be considered an unpredictable value. So what must be done is to guard against (prevent) the SIGALRM from being delivered after the ITIMER_REAL is set and before the sigpause is executed. On way is to use sigblock to block off SIGALRM. Now sigblock returns the old signal mask of blocked signals. Sigpause takes an arguement of a mask of signals that are blocked. What sigpause does nicely is to provide an ATOMIC operation (in one single context switch) that waits for a signal using the signal mask provided as an arguement. When an unmasked signal arrives the signal mask that exsisted before the call to sigpause is restored. (Note: I would not even attempt to do this with System V unix). So to make the program work you block off SIGALRM before the for loop saving the signal mask that is returned by sigblock. Now when setitimer returns no matter how long the context switches take the SIGALRM is blocked. So even if SIGALRM is pending, the sigpause is guarenteed to execute. The old mask returned by sigblock is used as the signal mask that sigpause uses to wait for the SIGALRM (that mask does NOT have SIGALRM masked). This provides protection of the "critical section" (the code between the setitimer and the sigpause). The following code is one way to protect the sigpause. /*******************************************************************/ #include #include #include struct itimerval timer; int wakeup; main(argc, argv) int argc; char **argv; { int alarmtrap(); register int i; int oldmask; signal(SIGALRM, alarmtrap); timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; timer.it_value.tv_sec = 0; wakeup = 0; /* * block off that nasty SIGALRM */ oldmask = sigblock(1 << (SIGALRM-1)); for (i=0; i<1000; i++) { timer.it_value.tv_usec = 30000; setitimer(ITIMER_REAL, &timer, 0); /* * crical section now protected from SIGALRM */ sigpause(oldmask); } } alarmtrap() { wakeup++; fprintf(stderr,"alarm %d\n", wakeup); } Keith Muller UCSD Computer Center ucbvax!sdcsvax!sdccsu3!muller