From owner-freebsd-hackers@FreeBSD.ORG Mon Mar 31 04:40:07 2003 Return-Path: Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 8C2DD37B404 for ; Mon, 31 Mar 2003 04:40:06 -0800 (PST) Received: from mail.openet-telecom.com (mail.openet-telecom.com [62.17.151.60]) by mx1.FreeBSD.org (Postfix) with ESMTP id 35B6443F75 for ; Mon, 31 Mar 2003 04:40:03 -0800 (PST) (envelope-from peter.edwards@openet-telecom.com) Received: from mail.openet-telecom.com (gpo.openet-telecom.lan) by mail.openet-telecom.com ; Mon, 31 Mar 2003 13:39:38 +0100 Received: from rocklobster.openet-telecom.lan (10.0.0.40) by mail.openet-telecom.com (NPlex 6.5.027) id 3E82B24600004F4A; Mon, 31 Mar 2003 13:42:26 +0100 From: Peter Edwards To: Sean Hamilton In-Reply-To: <001101c2f71d$8d9e4fb0$0300000a@slugabed.org> References: <001101c2f71d$8d9e4fb0$0300000a@slugabed.org> Content-Type: text/plain Organization: Openet Telecom Message-Id: <1049114603.29400.187.camel@rocklobster.openet-telecom.lan> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.2.2 Date: 31 Mar 2003 13:43:23 +0100 Content-Transfer-Encoding: 7bit cc: hackers@freebsd.org Subject: Re: wait()/alarm() race condition X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 31 Mar 2003 12:40:10 -0000 > Greetings, > > I have a loop which calls wait(), and I want another function to be > called > as close to once per minute as possible. Pseudo code: > [snip example] > > My concern is there is a small possibility that the alarm signal is > delivered after the if() but before the wait. So it is possible that > this > wait takes several minutes or longer. There's two ways of avoiding this race that no one has mentioned: Option 1: You could do the timer-based work in the signal handler itself, once you're sure that the signal is only unblocked when you're otherwise doing nothing, (and that any other handlers that do significant work are also blocked while in the signal handler) Option 2: If you'd rather have the real work done in the loop itself, you could use setjmp/longjmp to jump out of the signal handler back to a point in the code avoiding the blocking call, ensuring that the alarm can only be generated in a small window (see sample below) Of course, kqueue() avoids all this mucking around, at the expense of portability to non-FreeBSD systems. > #include > #include > #include > > static int alarmed = 0; > static jmp_buf jb; > > void > sigalarm() > { > alarmed = 1; > longjmp(jb, 1); > } > > int > main(int argc, char *argv[]) > { > sigset_t ss; > struct sigaction sa; > int rv; > > /* Create signal mask containing just SIGALRM */ > sigemptyset(&ss); > sigaddset(&ss, SIGALRM); > > /* Set up handler for SIGALRM */ > sa.sa_handler = sigalarm; > sa.sa_flags = 0; > sigemptyset(&sa.sa_mask); > sigaction(SIGALRM, &sa, 0); > sigprocmask(SIG_BLOCK, &ss, 0); /* Only unblock when idle */ > > /* Possibly start up child process, etc */ > for (;;) { > if (setjmp(jb) == 0) { > /* We may never get to call pause() below */ > rv = -1; > /* Start alarm */ > alarm(2); > /* Enable alarm signal */ > sigprocmask(SIG_UNBLOCK, &ss, 0); > A: > /* Wait for signal */ > rv = pause(); /* or wait, etc. */ > } > B: > /* > * At this point, either pause() finished, or SIGALRM > * happened between A and B (or both) > */ > > /* Block SIGALRM while we work */ > sigprocmask(SIG_BLOCK, &ss, 0); > > if (alarmed) { > /* Alarm fired: to timer-based stuff. */ > alarmed = 0; > printf("do work\n"); > } > > if (rv != -1) { > /* > * If we called wait() instead of pause(), we > * could deal with the consequences of a > * successful wait() here. > */ > } > } > return 0; > }