Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83 (MC840302); site philmds.UUCP Path: utzoo!watmath!clyde!burl!ulysses!harpo!decvax!mcvax!philmds!johan From: johan@philmds.UUCP (johan) Newsgroups: net.unix-wizards Subject: problems with alarm(0) Message-ID: <179@philmds.UUCP> Date: Tue, 22-May-84 06:26:13 EDT Article-I.D.: philmds.179 Posted: Tue May 22 06:26:13 1984 Date-Received: Wed, 23-May-84 19:33:36 EDT Organization: Philips S&I MDS Eindhoven Lines: 118 Due to a bug in the kernel it may happen that an alarm signal/interrupt is processed after the execution of alarm(0). This may cause problems as I will demonstrate. Several system calls may last long/forever, like read/write to a character device. Bounding this period can be done by using alarm()/signal(), like in onalarm() { signal(SIGALRM, onalarm); A: } { ... signal(SIGALRM, onalarm); alarm(2); B: i = read(fd, buf, sizeof(buf)); alarm(0); if (i == -1 && errno == EINTR) { ... /* handle timeout */ } else { ... /* handle data read */ } ... } This scheme may fail on busy systems if your process is suspended for several seconds at label B. This can be cured by restarting the timer in the routine onalarm() by adding the statement alarm(2) at label A. All this is not new and used in many situations in existing code. The problem is that even this last version is not behaving properly. In all UNIX kernels I have seen it is possible that a SIGALRM signal is processed whenever the alarm(0) returns from system to user mode. This causes the timer to be restarted and may lead to failures of any future interruptable system calls in that process. A short description of the sequence of events that makes this happen: - the kernel is processing the alarm(0) system call - a clock interrupt occurs - the statement 'if (++lbolt >= HZ)' in clock() happens to be true - BASEPRI(ps) is 0, so p_clktim processing is done in clock() - for our process p_clktim happens to reach 0, so psignal() is called - the appropriate bit for SIGALRM in p_sig is set - alarm(0) processing is continued - just before returning to user mode p_sig is checked and will cause the SIGALRM signal to be processed. BINGO. Two possible solutions I can think of: 1 - Change the kernel to avoid this strange behaviour. The p_sig bit corresponding to SIGALRM must be reset for alarm(0). The statement: p->p_clktim = uap->deltat; must be replaced by if ((p->p_clktim = uap->deltat) == 0) p->p_sig &= ~(1<<(SIGALRM-1)); Notice that this code is not surrounded by any spl()'s !!!! Adding a spl1()/spl0() pair may result in a more consistent return value from alarm(). Always resetting p_sig is possible if spl()'s added. 2 - For the time being the above piece of program can be modified slightly. The trick is to maintain a variable indicating when the timer must be restarted in onalarm(). Clear this variable just before the alarm(0). The modified example looks like: int keepticking; onalarm() { signal(SIGALRM, onalarm); if (keepticking) alarm(2); } { ... signal(SIGALRM, onalarm); keepticking = 1; alarm(2); i = read(fd, buf, sizeof(buf)); keepticking = 0; alarm(0); if (i == -1 && errno == EINTR) { ... /* handle timeout */ } else { ... /* handle data read */ } ... } A suggested alternative: while (alarm(0) == 0) ; fails because the return value can not be trusted unless the spl()'s are added. NOTE: We haven't seen this problem in real life, but came across the possibility during some discussions. It would be appreciated if anyone struck by this phenomenon could confirm. Johan Stevenson, Philips S&I, T&M, PMDS, Building TQV-5, Eindhoven, The Netherlands. phone: +31 40 784736 uucp: mcvax!philmds!johan