Date: 31 Mar 2003 13:43:23 +0100 From: Peter Edwards <peter.edwards@openet-telecom.com> To: Sean Hamilton <sh@bel.bc.ca> Cc: hackers@freebsd.org Subject: Re: wait()/alarm() race condition Message-ID: <1049114603.29400.187.camel@rocklobster.openet-telecom.lan> In-Reply-To: <001101c2f71d$8d9e4fb0$0300000a@slugabed.org> References: <001101c2f71d$8d9e4fb0$0300000a@slugabed.org>
next in thread | previous in thread | raw e-mail | index | archive | help
> 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 <signal.h> > #include <stdio.h> > #include <setjmp.h> > > 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; > }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1049114603.29400.187.camel>