1 /*****************************************************************************
4 idle.c -- code for interruptible delays without sleep(3).
7 interruptible_idle() -- delay for some time, interruptible by signal.
10 Sometimes you need more than one time delay per program, so alarm(3)
11 won't cut it. This code illustrates time delays with select(2).
14 Eric S. Raymond <esr@thyrsus.com>, 1997. This source code example
15 is part of fetchmail and the Unix Cookbook, and are released under the
16 MIT license. Compile with -DMAIN to build the demonstrator.
18 ******************************************************************************/
24 #include <fetchmail.h> /* for ROOT_UID */
31 volatile int lastsig; /* last signal received */
33 #ifdef SLEEP_WITH_ALARM
35 * The function of this variable is to remove the window during which a
36 * SIGALRM can hose the code (ALARM is triggered *before* pause() is called).
37 * This is a bit of a kluge; the real right thing would use sigprocmask(),
38 * sigsuspend(). This workaround lets the interval timer trigger the first
39 * alarm after the required interval and will then generate alarms
40 * seconds until it is certain that the critical section (ie., the window)
43 static sig_atomic_t alarm_latch = FALSE;
45 RETSIGTYPE gotsigalrm(int sig)
47 set_signal_handler(sig, gotsigalrm);
51 #endif /* SLEEP_WITH_ALARM */
54 /* Various EMX-specific definitions */
55 static int itimerflag;
57 void itimerthread(void* dummy)
59 if (outlevel >= O_VERBOSE)
61 GT_("fetchmail: thread sleeping for %d sec.\n"), poll_interval);
64 _sleep2(poll_interval*1000);
65 kill((getpid()), SIGALRM);
70 int interruptible_idle(int seconds)
71 /* time for a pause in the action; return TRUE if awakened by signal */
76 #ifdef SLEEP_WITH_ALARM /* not normally on */
78 * We can't use sleep(3) here because we need an alarm(3)
79 * equivalent in order to implement server nonresponse timeout.
80 * We'll just assume setitimer(2) is available since fetchmail
81 * has to have a BSDoid socket layer to work at all.
84 * This code stopped working under glibc-2, apparently due
85 * to the change in signal(2) semantics. (The siginterrupt
86 * line, added later, should fix this problem.) John Stracke
87 * <francis@netscape.com> wrote:
89 * The problem seems to be that, after hitting the interval
90 * timer while talking to the server, the process no longer
91 * responds to SIGALRM. I put in printf()s to see when it
92 * reached the pause() for the poll interval, and I checked
93 * the return from setitimer(), and everything seemed to be
94 * working fine, except that the pause() just ignored SIGALRM.
95 * I thought maybe the itimer wasn't being fired, so I hit
96 * it with a SIGALRM from the command line, and it ignored
97 * that, too. SIGUSR1 woke it up just fine, and it proceeded
98 * to repoll--but, when the dummy server didn't respond, it
99 * never timed out, and SIGALRM wouldn't make it.
101 * (continued below...)
104 struct itimerval ntimeout;
106 ntimeout.it_interval.tv_sec = 5; /* repeat alarm every 5 secs */
107 ntimeout.it_interval.tv_usec = 0;
108 ntimeout.it_value.tv_sec = seconds;
109 ntimeout.it_value.tv_usec = 0;
112 set_signal_handler(SIGALRM, gotsigalrm); /* first trap signals */
113 setitimer(ITIMER_REAL,&ntimeout,NULL); /* then start timer */
114 /* there is a very small window between the next two lines */
115 /* which could result in a deadlock. But this will now be */
116 /* caught by periodic alarms (see it_interval) */
120 ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_usec = 0;
121 ntimeout.it_value.tv_sec = ntimeout.it_value.tv_usec = 0;
122 setitimer(ITIMER_REAL,&ntimeout,NULL); /* now stop timer */
123 set_signal_handler(SIGALRM, SIG_IGN);
127 * So the workaround I used is to make it sleep by using
128 * select() instead of setitimer()/pause(). select() is
129 * perfectly happy being called with a timeout and
130 * no file descriptors; it just sleeps until it hits the
131 * timeout. The only concern I had was that it might
132 * implement its timeout with SIGALRM--there are some
133 * Unices where this is done, because select() is a library
134 * function--but apparently not.
137 struct timeval timeout;
139 timeout.tv_sec = seconds;
143 select(0,0,0,0, &timeout);
144 } while (lastsig == SIGCHLD);
149 set_signal_handler(SIGALRM, gotsigalrm);
150 _beginthread(itimerthread, NULL, 32768, NULL);
151 /* see similar code above */
154 set_signal_handler(SIGALRM, SIG_IGN);
156 if (lastsig == SIGUSR1 || ((seconds && getuid() == ROOT_UID)
157 && lastsig == SIGHUP))
160 /* now lock out interrupts again */
161 set_signal_handler(SIGUSR1, SIG_IGN);
162 if (getuid() == ROOT_UID)
163 set_signal_handler(SIGHUP, SIG_IGN);
165 return(awoken ? lastsig : 0);
169 int main(int argc, char **argv)
173 printf("How may I serve you, master?\n");
174 interruptible_idle(5);
179 /* idle.c ends here */