Skip site navigation (1)Skip section navigation (2)
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>