ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/iom.C
Revision: 1.30
Committed: Thu Mar 3 16:54:34 2005 UTC (19 years, 4 months ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.29: +5 -3 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.1 /*
2 pcg 1.23 iom.C -- generic I/O multiplexer
3 pcg 1.30 Copyright (C) 2003, 2004 Marc Lehmann <gvpe@schmorp.de>
4 pcg 1.1
5 pcg 1.30 This file is part of GVPE.
6    
7     GVPE is free software; you can redistribute it and/or modify
8 pcg 1.1 it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License
18 pcg 1.30 along with gvpe; if not, write to the Free Software
19 pcg 1.1 Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21    
22 pcg 1.21 #include "iom.h"
23    
24 pcg 1.13 #include <cstdio>
25     #include <cstdlib>
26     #include <cerrno>
27 pcg 1.1
28 pcg 1.15 #include <sys/time.h>
29    
30 pcg 1.20 #include <assert.h>
31    
32 pcg 1.18 #if 1 // older unices need these includes for select (2)
33 pcg 1.15 # include <unistd.h>
34     # include <sys/types.h>
35 pcg 1.21 # include <time.h>
36     #endif
37    
38     // for IOM_SIG
39     #if IOM_SIG
40 pcg 1.28 # include <csignal>
41 pcg 1.21 # include <fcntl.h>
42 pcg 1.15 #endif
43    
44     // if the BSDs would at least be marginally POSIX-compatible.. *sigh*
45     // until that happens, sys/select.h must come last
46 pcg 1.13 #include <sys/select.h>
47 pcg 1.1
48 pcg 1.21 // TSTAMP_MAX must still fit into a positive struct timeval
49     #define TSTAMP_MAX (double)(1UL<<31)
50 pcg 1.20
51 pcg 1.21 #define TIMEVAL timeval
52     #define TV_FRAC tv_usec
53     #define TV_MULT 1000000L
54 pcg 1.11
55 pcg 1.21 #if IOM_IO
56     static io_manager_vec<io_watcher> iow;
57     #endif
58     #if IOM_CHECK
59     static io_manager_vec<check_watcher> cw;
60     #endif
61     #if IOM_TIME
62     static io_manager_vec<time_watcher> tw;
63     #endif
64     #if IOM_IDLE
65     static io_manager_vec<idle_watcher> iw;
66     #endif
67     #if IOM_SIG
68     static int sigpipe[2]; // signal signalling pipe
69     static sigset_t sigs;
70     struct sig_vec : io_manager_vec<sig_watcher> {
71     int pending;
72     sig_vec ()
73     : pending (false)
74     { }
75     };
76     static vector<sig_vec *> sw;
77     #endif
78 pcg 1.1
79 pcg 1.14 // this is a dummy time watcher to ensure that the first
80     // time watcher is _always_ valid, this gets rid of a lot
81     // of null-pointer-checks
82     // (must come _before_ iom is being defined)
83 pcg 1.18 static struct tw0 : time_watcher
84 pcg 1.14 {
85 pcg 1.18 void cb (time_watcher &w)
86     {
87     // should never get called
88     // reached end-of-time, or tstamp has a bogus definition,
89 pcg 1.21 // or compiler initialisation order broken, or something else :)
90 pcg 1.18 abort ();
91     }
92 pcg 1.14
93 pcg 1.18 tw0 ()
94 pcg 1.21 : time_watcher (this, &tw0::cb)
95     { }
96 pcg 1.20 } tw0;
97 pcg 1.14
98 pcg 1.10 tstamp NOW;
99 pcg 1.21
100     #if IOM_TIME
101 pcg 1.26 tstamp io_manager::now ()
102 pcg 1.21 {
103     struct timeval tv;
104    
105     gettimeofday (&tv, 0);
106 pcg 1.26 return (tstamp)tv.tv_sec + (tstamp)tv.tv_usec / 1000000.;
107     }
108    
109     void io_manager::set_now ()
110     {
111     NOW = now ();
112 pcg 1.21 }
113     #endif
114    
115 pcg 1.13 static bool iom_valid;
116 pcg 1.21
117     // used for initialisation only
118     static struct init {
119     init ()
120     {
121     #if IOM_SIG
122     sigemptyset (&sigs);
123    
124     if (pipe (sigpipe))
125     {
126     perror ("io_manager: unable to create signal pipe, aborting.");
127     abort ();
128     }
129    
130 pcg 1.26 fcntl (sigpipe[0], F_SETFL, O_NONBLOCK); fcntl (sigpipe[0], F_SETFD, FD_CLOEXEC);
131     fcntl (sigpipe[1], F_SETFL, O_NONBLOCK); fcntl (sigpipe[1], F_SETFD, FD_CLOEXEC);
132 pcg 1.21 #endif
133    
134     iom_valid = true;
135    
136     #if IOM_TIME
137 pcg 1.26 io_manager::set_now ();
138 pcg 1.21
139     tw0.start (TSTAMP_MAX);
140     #endif
141     }
142    
143     static void required ();
144     } init;
145    
146     void
147     init::required ()
148     {
149     if (!iom_valid)
150     {
151     write (2, "io_manager: early registration attempt, aborting.\n",
152     sizeof ("io_manager: early registration attempt, aborting.\n") - 1);
153     abort ();
154     }
155     }
156 pcg 1.10
157 pcg 1.13 template<class watcher>
158 pcg 1.21 void io_manager::reg (watcher &w, io_manager_vec<watcher> &queue)
159 pcg 1.4 {
160 pcg 1.21 init::required ();
161 pcg 1.4
162 pcg 1.21 if (!w.active)
163 pcg 1.11 {
164 pcg 1.21 queue.push_back (&w);
165     w.active = queue.size ();
166 pcg 1.11 }
167 pcg 1.10 }
168    
169 pcg 1.13 template<class watcher>
170 pcg 1.21 void io_manager::unreg (watcher &w, io_manager_vec<watcher> &queue)
171 pcg 1.10 {
172 pcg 1.13 if (!iom_valid)
173     return;
174 pcg 1.10
175 pcg 1.21 if (w.active)
176 pcg 1.11 {
177 pcg 1.21 queue [w.active - 1] = 0;
178     w.active = 0;
179 pcg 1.11 }
180 pcg 1.1 }
181    
182 pcg 1.13 #if IOM_TIME
183     void time_watcher::trigger ()
184 pcg 1.1 {
185 pcg 1.13 call (*this);
186 pcg 1.21 io_manager::reg (*this);
187 pcg 1.1 }
188    
189 pcg 1.21 void io_manager::reg (time_watcher &w) { io_manager::reg (w, tw); }
190     void io_manager::unreg (time_watcher &w) { io_manager::unreg (w, tw); }
191 pcg 1.13 #endif
192 pcg 1.6
193 pcg 1.13 #if IOM_IO
194 pcg 1.21 void io_manager::reg (io_watcher &w) { io_manager::reg (w, iow); }
195     void io_manager::unreg (io_watcher &w) { io_manager::unreg (w, iow); }
196 pcg 1.13 #endif
197 pcg 1.7
198 pcg 1.13 #if IOM_CHECK
199 pcg 1.21 void io_manager::reg (check_watcher &w) { io_manager::reg (w, cw); }
200     void io_manager::unreg (check_watcher &w) { io_manager::unreg (w, cw); }
201 pcg 1.13 #endif
202 pcg 1.1
203 pcg 1.13 #if IOM_IDLE
204 pcg 1.21 void io_manager::reg (idle_watcher &w) { io_manager::reg (w, iw); }
205     void io_manager::unreg (idle_watcher &w) { io_manager::unreg (w, iw); }
206 pcg 1.13 #endif
207 pcg 1.1
208 pcg 1.21 #if IOM_SIG
209     static void
210     sighandler (int signum)
211 pcg 1.1 {
212 pcg 1.21 sw [signum - 1]->pending = true;
213 pcg 1.1
214 pcg 1.21 // we use a pipe for signal notifications, as most current
215     // OSes (Linux...) do not implement pselect correctly. ugh.
216     char ch = signum; // actual content not used
217     write (sigpipe[1], &ch, 1);
218 pcg 1.20 }
219    
220 pcg 1.21 void io_manager::reg (sig_watcher &w)
221 pcg 1.20 {
222 pcg 1.21 assert (0 < w.signum);
223 pcg 1.20
224 pcg 1.21 sw.reserve (w.signum);
225 pcg 1.20
226 pcg 1.21 while (sw.size () < w.signum) // pathetic
227     sw.push_back (0);
228 pcg 1.20
229 pcg 1.21 sig_vec *&sv = sw[w.signum - 1];
230 pcg 1.20
231     if (!sv)
232     {
233     sv = new sig_vec;
234    
235 pcg 1.21 sigaddset (&sigs, w.signum);
236     sigprocmask (SIG_BLOCK, &sigs, NULL);
237    
238 pcg 1.20 struct sigaction sa;
239 pcg 1.21 sa.sa_handler = sighandler;
240 pcg 1.20 sigfillset (&sa.sa_mask);
241 pcg 1.21 sa.sa_flags = SA_RESTART;
242 pcg 1.20
243 pcg 1.21 if (sigaction (w.signum, &sa, 0))
244 pcg 1.20 {
245 pcg 1.21 perror ("io_manager: error while installing signal handler, ignoring.");
246 pcg 1.20 abort ();
247     }
248 pcg 1.21
249 pcg 1.20 }
250    
251 pcg 1.21 io_manager::reg (w, *sv);
252 pcg 1.20 }
253    
254 pcg 1.21 void io_manager::unreg (sig_watcher &w)
255 pcg 1.20 {
256 pcg 1.21 if (!w.active)
257     return;
258    
259     assert (0 < w.signum && w.signum <= sw.size ());
260 pcg 1.20
261 pcg 1.21 io_manager::unreg (w, *sw[w.signum - 1]);
262 pcg 1.20 }
263    
264     void sig_watcher::start (int signum)
265     {
266     stop ();
267     this->signum = signum;
268 pcg 1.21 io_manager::reg (*this);
269 pcg 1.20 }
270     #endif
271 pcg 1.21
272 pcg 1.1 void io_manager::loop ()
273     {
274 pcg 1.21 init::required ();
275    
276 pcg 1.13 #if IOM_TIME
277 pcg 1.1 set_now ();
278 pcg 1.13 #endif
279 pcg 1.1
280 pcg 1.4 for (;;)
281 pcg 1.1 {
282 pcg 1.13
283     #if IOM_TIME
284 pcg 1.22 // call pending time watchers
285     {
286     bool activity;
287 pcg 1.7
288 pcg 1.22 do
289     {
290     activity = false;
291 pcg 1.13
292 pcg 1.22 for (int i = tw.size (); i--; )
293     if (!tw[i])
294     tw.erase_unordered (i);
295 pcg 1.24 else if (tw[i]->at <= NOW)
296 pcg 1.22 {
297     time_watcher &w = *tw[i];
298    
299     unreg (w);
300     w.call (w);
301 pcg 1.13
302 pcg 1.22 activity = true;
303 pcg 1.13 }
304 pcg 1.22 }
305     while (activity);
306     }
307 pcg 1.13 #endif
308 pcg 1.1
309 pcg 1.13 #if IOM_CHECK
310 pcg 1.22 // call all check watchers
311 pcg 1.13 for (int i = cw.size (); i--; )
312     if (!cw[i])
313     cw.erase_unordered (i);
314     else
315     cw[i]->call (*cw[i]);
316 pcg 1.22 #endif
317 pcg 1.17
318 pcg 1.22 struct TIMEVAL *to = 0;
319     struct TIMEVAL tval;
320    
321     #if IOM_IDLE
322     if (iw.size ())
323 pcg 1.17 {
324     tval.tv_sec = 0;
325 pcg 1.21 tval.TV_FRAC = 0;
326 pcg 1.17 to = &tval;
327     }
328 pcg 1.22 else
329     #endif
330     {
331     #if IOM_TIME
332     // find earliest active watcher
333     time_watcher *next = tw[0]; // the first time-watcher must exist at ALL times
334    
335     for (io_manager_vec<time_watcher>::const_iterator i = tw.end (); i-- > tw.begin (); )
336     if (*i && (*i)->at < next->at)
337     next = *i;
338    
339     if (next->at > NOW && next != tw[0])
340     {
341     double diff = next->at - NOW;
342     tval.tv_sec = (int)diff;
343     tval.TV_FRAC = (int) ((diff - tval.tv_sec) * TV_MULT);
344     to = &tval;
345     }
346     }
347 pcg 1.13 #endif
348    
349 pcg 1.21 #if IOM_IO || IOM_SIG
350 pcg 1.18 fd_set rfd, wfd;
351 pcg 1.7
352 pcg 1.13 FD_ZERO (&rfd);
353     FD_ZERO (&wfd);
354 pcg 1.7
355 pcg 1.13 int fds = 0;
356    
357 pcg 1.21 # if IOM_IO
358 pcg 1.22 for (io_manager_vec<io_watcher>::const_iterator i = iow.end (); i-- > iow.begin (); )
359 pcg 1.13 if (*i)
360     {
361     if ((*i)->events & EVENT_READ ) FD_SET ((*i)->fd, &rfd);
362     if ((*i)->events & EVENT_WRITE) FD_SET ((*i)->fd, &wfd);
363 pcg 1.1
364 pcg 1.13 if ((*i)->fd >= fds) fds = (*i)->fd + 1;
365     }
366 pcg 1.21 # endif
367 pcg 1.1
368 pcg 1.22 if (!to && !fds) //TODO: also check idle_watchers and check_watchers?
369 pcg 1.13 break; // no events
370 pcg 1.1
371 pcg 1.21 # if IOM_SIG
372     FD_SET (sigpipe[0], &rfd);
373     if (sigpipe[0] >= fds) fds = sigpipe[0] + 1;
374     # endif
375    
376     # if IOM_SIG
377     // there is no race, as we use a pipe for signals, so select
378     // will return if a signal is caught.
379     sigprocmask (SIG_UNBLOCK, &sigs, NULL);
380     # endif
381 pcg 1.19 fds = select (fds, &rfd, &wfd, NULL, to);
382 pcg 1.21 # if IOM_SIG
383     sigprocmask (SIG_BLOCK, &sigs, NULL);
384     # endif
385    
386 pcg 1.13 # if IOM_TIME
387 pcg 1.29 {
388     // update time, try to compensate for gross non-monotonic time changes
389     tstamp diff = NOW;
390     set_now ();
391     diff = NOW - diff;
392    
393     if (diff < 0)
394     for (io_manager_vec<time_watcher>::const_iterator i = tw.end (); i-- > tw.begin (); )
395     if (*i)
396     (*i)->at += diff;
397     }
398 pcg 1.13 # endif
399 pcg 1.1
400 pcg 1.13 if (fds > 0)
401 pcg 1.21 {
402     # if IOM_SIG
403     if (FD_ISSET (sigpipe[0], &rfd))
404 pcg 1.13 {
405 pcg 1.21 char ch;
406    
407     while (read (sigpipe[0], &ch, 1) > 0)
408     ;
409 pcg 1.13
410 pcg 1.25 for (vector<sig_vec *>::iterator svp = sw.end (); svp-- > sw.begin (); )
411 pcg 1.21 if (*svp && (*svp)->pending)
412     {
413     sig_vec &sv = **svp;
414     for (int i = sv.size (); i--; )
415     if (!sv[i])
416     sv.erase_unordered (i);
417     else
418     sv[i]->call (*sv[i]);
419 pcg 1.13
420 pcg 1.21 sv.pending = false;
421     }
422 pcg 1.13 }
423 pcg 1.21 # endif
424    
425     # if IOM_IO
426     for (int i = iow.size (); i--; )
427     if (!iow[i])
428     iow.erase_unordered (i);
429     else
430     {
431 pcg 1.27 io_watcher &w = *iow[i];
432     short revents = w.events;
433 pcg 1.21
434 pcg 1.27 if (!FD_ISSET (w.fd, &rfd)) revents &= ~EVENT_READ;
435     if (!FD_ISSET (w.fd, &wfd)) revents &= ~EVENT_WRITE;
436 pcg 1.21
437     if (revents)
438 pcg 1.27 w.call (w, revents);
439 pcg 1.21 }
440     #endif
441     }
442 pcg 1.13 else if (fds < 0 && errno != EINTR)
443     {
444 pcg 1.21 perror ("io_manager: fatal error while waiting for I/O or time event, aborting.");
445 pcg 1.13 abort ();
446     }
447     #if IOM_IDLE
448     else
449     for (int i = iw.size (); i--; )
450     if (!iw[i])
451     iw.erase_unordered (i);
452     else
453     iw[i]->call (*iw[i]);
454     #endif
455    
456     #elif IOM_TIME
457     if (!to)
458     break;
459 pcg 1.9
460 pcg 1.13 select (0, 0, 0, 0, &to);
461     set_now ();
462     #else
463     break;
464     #endif
465 pcg 1.1 }
466     }
467