ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/iom.C
Revision: 1.38
Committed: Sat Nov 10 05:14:22 2007 UTC (16 years, 6 months ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.37: +0 -0 lines
State: FILE REMOVED
Log Message:
first shot at converting from iom to libev

File Contents

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