ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/iom.C
Revision: 1.32
Committed: Tue Apr 26 00:55:56 2005 UTC (19 years, 1 month ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_0
Changes since 1.31: +1 -1 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.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.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 #define TIMEVAL timeval
49     #define TV_FRAC tv_usec
50     #define TV_MULT 1000000L
51 pcg 1.11
52 pcg 1.21 #if IOM_IO
53     static io_manager_vec<io_watcher> iow;
54     #endif
55     #if IOM_CHECK
56     static io_manager_vec<check_watcher> cw;
57     #endif
58     #if IOM_TIME
59     static io_manager_vec<time_watcher> tw;
60     #endif
61     #if IOM_IDLE
62     static io_manager_vec<idle_watcher> iw;
63     #endif
64     #if IOM_SIG
65     static int sigpipe[2]; // signal signalling pipe
66     static sigset_t sigs;
67     struct sig_vec : io_manager_vec<sig_watcher> {
68     int pending;
69     sig_vec ()
70     : pending (false)
71     { }
72     };
73     static vector<sig_vec *> sw;
74     #endif
75 pcg 1.1
76 pcg 1.14 // this is a dummy time watcher to ensure that the first
77     // time watcher is _always_ valid, this gets rid of a lot
78     // of null-pointer-checks
79     // (must come _before_ iom is being defined)
80 pcg 1.18 static struct tw0 : time_watcher
81 pcg 1.14 {
82 pcg 1.18 void cb (time_watcher &w)
83     {
84     // should never get called
85     // reached end-of-time, or tstamp has a bogus definition,
86 pcg 1.21 // or compiler initialisation order broken, or something else :)
87 pcg 1.18 abort ();
88     }
89 pcg 1.14
90 pcg 1.18 tw0 ()
91 pcg 1.21 : time_watcher (this, &tw0::cb)
92     { }
93 pcg 1.20 } tw0;
94 pcg 1.14
95 pcg 1.10 tstamp NOW;
96 pcg 1.21
97     #if IOM_TIME
98 pcg 1.26 tstamp io_manager::now ()
99 pcg 1.21 {
100     struct timeval tv;
101    
102     gettimeofday (&tv, 0);
103 pcg 1.26 return (tstamp)tv.tv_sec + (tstamp)tv.tv_usec / 1000000.;
104     }
105    
106     void io_manager::set_now ()
107     {
108     NOW = now ();
109 pcg 1.21 }
110     #endif
111    
112 pcg 1.13 static bool iom_valid;
113 pcg 1.21
114     // used for initialisation only
115     static struct init {
116     init ()
117     {
118     #if IOM_SIG
119     sigemptyset (&sigs);
120    
121     if (pipe (sigpipe))
122     {
123     perror ("io_manager: unable to create signal pipe, aborting.");
124     abort ();
125     }
126    
127 pcg 1.26 fcntl (sigpipe[0], F_SETFL, O_NONBLOCK); fcntl (sigpipe[0], F_SETFD, FD_CLOEXEC);
128     fcntl (sigpipe[1], F_SETFL, O_NONBLOCK); fcntl (sigpipe[1], F_SETFD, FD_CLOEXEC);
129 pcg 1.21 #endif
130    
131     iom_valid = true;
132    
133     #if IOM_TIME
134 pcg 1.26 io_manager::set_now ();
135 pcg 1.21
136     tw0.start (TSTAMP_MAX);
137     #endif
138     }
139    
140     static void required ();
141     } init;
142    
143     void
144     init::required ()
145     {
146     if (!iom_valid)
147     {
148     write (2, "io_manager: early registration attempt, aborting.\n",
149     sizeof ("io_manager: early registration attempt, aborting.\n") - 1);
150     abort ();
151     }
152     }
153 pcg 1.10
154 pcg 1.13 template<class watcher>
155 pcg 1.21 void io_manager::reg (watcher &w, io_manager_vec<watcher> &queue)
156 pcg 1.4 {
157 pcg 1.21 init::required ();
158 pcg 1.4
159 pcg 1.21 if (!w.active)
160 pcg 1.11 {
161 pcg 1.21 queue.push_back (&w);
162     w.active = queue.size ();
163 pcg 1.11 }
164 pcg 1.10 }
165    
166 pcg 1.13 template<class watcher>
167 pcg 1.21 void io_manager::unreg (watcher &w, io_manager_vec<watcher> &queue)
168 pcg 1.10 {
169 pcg 1.13 if (!iom_valid)
170     return;
171 pcg 1.10
172 pcg 1.21 if (w.active)
173 pcg 1.11 {
174 pcg 1.21 queue [w.active - 1] = 0;
175     w.active = 0;
176 pcg 1.11 }
177 pcg 1.1 }
178    
179 pcg 1.13 #if IOM_TIME
180     void time_watcher::trigger ()
181 pcg 1.1 {
182 pcg 1.13 call (*this);
183 pcg 1.21 io_manager::reg (*this);
184 pcg 1.1 }
185    
186 pcg 1.21 void io_manager::reg (time_watcher &w) { io_manager::reg (w, tw); }
187     void io_manager::unreg (time_watcher &w) { io_manager::unreg (w, tw); }
188 pcg 1.13 #endif
189 pcg 1.6
190 pcg 1.13 #if IOM_IO
191 pcg 1.21 void io_manager::reg (io_watcher &w) { io_manager::reg (w, iow); }
192     void io_manager::unreg (io_watcher &w) { io_manager::unreg (w, iow); }
193 pcg 1.13 #endif
194 pcg 1.7
195 pcg 1.13 #if IOM_CHECK
196 pcg 1.21 void io_manager::reg (check_watcher &w) { io_manager::reg (w, cw); }
197     void io_manager::unreg (check_watcher &w) { io_manager::unreg (w, cw); }
198 pcg 1.13 #endif
199 pcg 1.1
200 pcg 1.13 #if IOM_IDLE
201 pcg 1.21 void io_manager::reg (idle_watcher &w) { io_manager::reg (w, iw); }
202     void io_manager::unreg (idle_watcher &w) { io_manager::unreg (w, iw); }
203 pcg 1.13 #endif
204 pcg 1.1
205 pcg 1.21 #if IOM_SIG
206     static void
207     sighandler (int signum)
208 pcg 1.1 {
209 pcg 1.21 sw [signum - 1]->pending = true;
210 pcg 1.1
211 pcg 1.21 // we use a pipe for signal notifications, as most current
212     // OSes (Linux...) do not implement pselect correctly. ugh.
213     char ch = signum; // actual content not used
214     write (sigpipe[1], &ch, 1);
215 pcg 1.20 }
216    
217 pcg 1.21 void io_manager::reg (sig_watcher &w)
218 pcg 1.20 {
219 pcg 1.21 assert (0 < w.signum);
220 pcg 1.20
221 pcg 1.21 sw.reserve (w.signum);
222 pcg 1.20
223 pcg 1.21 while (sw.size () < w.signum) // pathetic
224     sw.push_back (0);
225 pcg 1.20
226 pcg 1.21 sig_vec *&sv = sw[w.signum - 1];
227 pcg 1.20
228     if (!sv)
229     {
230     sv = new sig_vec;
231    
232 pcg 1.21 sigaddset (&sigs, w.signum);
233     sigprocmask (SIG_BLOCK, &sigs, NULL);
234    
235 pcg 1.20 struct sigaction sa;
236 pcg 1.21 sa.sa_handler = sighandler;
237 pcg 1.20 sigfillset (&sa.sa_mask);
238 pcg 1.21 sa.sa_flags = SA_RESTART;
239 pcg 1.20
240 pcg 1.21 if (sigaction (w.signum, &sa, 0))
241 pcg 1.20 {
242 pcg 1.21 perror ("io_manager: error while installing signal handler, ignoring.");
243 pcg 1.20 abort ();
244     }
245 pcg 1.21
246 pcg 1.20 }
247    
248 pcg 1.21 io_manager::reg (w, *sv);
249 pcg 1.20 }
250    
251 pcg 1.21 void io_manager::unreg (sig_watcher &w)
252 pcg 1.20 {
253 pcg 1.21 if (!w.active)
254     return;
255    
256     assert (0 < w.signum && w.signum <= sw.size ());
257 pcg 1.20
258 pcg 1.21 io_manager::unreg (w, *sw[w.signum - 1]);
259 pcg 1.20 }
260    
261     void sig_watcher::start (int signum)
262     {
263     stop ();
264     this->signum = signum;
265 pcg 1.21 io_manager::reg (*this);
266 pcg 1.20 }
267     #endif
268 pcg 1.21
269 pcg 1.1 void io_manager::loop ()
270     {
271 pcg 1.21 init::required ();
272    
273 pcg 1.13 #if IOM_TIME
274 pcg 1.1 set_now ();
275 pcg 1.13 #endif
276 pcg 1.1
277 pcg 1.4 for (;;)
278 pcg 1.1 {
279 pcg 1.13
280     #if IOM_TIME
281 pcg 1.22 // call pending time watchers
282     {
283     bool activity;
284 pcg 1.7
285 pcg 1.22 do
286     {
287     activity = false;
288 pcg 1.13
289 pcg 1.22 for (int i = tw.size (); i--; )
290     if (!tw[i])
291     tw.erase_unordered (i);
292 pcg 1.24 else if (tw[i]->at <= NOW)
293 pcg 1.22 {
294     time_watcher &w = *tw[i];
295    
296     unreg (w);
297     w.call (w);
298 pcg 1.13
299 pcg 1.22 activity = true;
300 pcg 1.13 }
301 pcg 1.22 }
302     while (activity);
303     }
304 pcg 1.13 #endif
305 pcg 1.1
306 pcg 1.13 #if IOM_CHECK
307 pcg 1.22 // call all check watchers
308 pcg 1.13 for (int i = cw.size (); i--; )
309     if (!cw[i])
310     cw.erase_unordered (i);
311     else
312     cw[i]->call (*cw[i]);
313 pcg 1.22 #endif
314 pcg 1.17
315 pcg 1.22 struct TIMEVAL *to = 0;
316     struct TIMEVAL tval;
317    
318     #if IOM_IDLE
319     if (iw.size ())
320 pcg 1.17 {
321     tval.tv_sec = 0;
322 pcg 1.21 tval.TV_FRAC = 0;
323 pcg 1.17 to = &tval;
324     }
325 pcg 1.22 else
326     #endif
327     {
328     #if IOM_TIME
329     // find earliest active watcher
330     time_watcher *next = tw[0]; // the first time-watcher must exist at ALL times
331    
332     for (io_manager_vec<time_watcher>::const_iterator i = tw.end (); i-- > tw.begin (); )
333     if (*i && (*i)->at < next->at)
334     next = *i;
335    
336     if (next->at > NOW && next != tw[0])
337     {
338     double diff = next->at - NOW;
339     tval.tv_sec = (int)diff;
340     tval.TV_FRAC = (int) ((diff - tval.tv_sec) * TV_MULT);
341     to = &tval;
342     }
343     }
344 pcg 1.13 #endif
345    
346 pcg 1.21 #if IOM_IO || IOM_SIG
347 pcg 1.18 fd_set rfd, wfd;
348 pcg 1.7
349 pcg 1.13 FD_ZERO (&rfd);
350     FD_ZERO (&wfd);
351 pcg 1.7
352 pcg 1.13 int fds = 0;
353    
354 pcg 1.21 # if IOM_IO
355 pcg 1.22 for (io_manager_vec<io_watcher>::const_iterator i = iow.end (); i-- > iow.begin (); )
356 pcg 1.13 if (*i)
357     {
358     if ((*i)->events & EVENT_READ ) FD_SET ((*i)->fd, &rfd);
359     if ((*i)->events & EVENT_WRITE) FD_SET ((*i)->fd, &wfd);
360 pcg 1.1
361 pcg 1.13 if ((*i)->fd >= fds) fds = (*i)->fd + 1;
362     }
363 pcg 1.21 # endif
364 pcg 1.1
365 pcg 1.22 if (!to && !fds) //TODO: also check idle_watchers and check_watchers?
366 pcg 1.13 break; // no events
367 pcg 1.1
368 pcg 1.21 # if IOM_SIG
369     FD_SET (sigpipe[0], &rfd);
370     if (sigpipe[0] >= fds) fds = sigpipe[0] + 1;
371     # endif
372    
373     # if IOM_SIG
374     // there is no race, as we use a pipe for signals, so select
375     // will return if a signal is caught.
376     sigprocmask (SIG_UNBLOCK, &sigs, NULL);
377     # endif
378 pcg 1.19 fds = select (fds, &rfd, &wfd, NULL, to);
379 pcg 1.21 # if IOM_SIG
380     sigprocmask (SIG_BLOCK, &sigs, NULL);
381     # endif
382    
383 pcg 1.13 # if IOM_TIME
384 pcg 1.29 {
385     // update time, try to compensate for gross non-monotonic time changes
386     tstamp diff = NOW;
387     set_now ();
388     diff = NOW - diff;
389    
390     if (diff < 0)
391     for (io_manager_vec<time_watcher>::const_iterator i = tw.end (); i-- > tw.begin (); )
392     if (*i)
393     (*i)->at += diff;
394     }
395 pcg 1.13 # endif
396 pcg 1.1
397 pcg 1.13 if (fds > 0)
398 pcg 1.21 {
399     # if IOM_SIG
400     if (FD_ISSET (sigpipe[0], &rfd))
401 pcg 1.13 {
402 pcg 1.21 char ch;
403    
404     while (read (sigpipe[0], &ch, 1) > 0)
405     ;
406 pcg 1.13
407 pcg 1.25 for (vector<sig_vec *>::iterator svp = sw.end (); svp-- > sw.begin (); )
408 pcg 1.21 if (*svp && (*svp)->pending)
409     {
410     sig_vec &sv = **svp;
411     for (int i = sv.size (); i--; )
412     if (!sv[i])
413     sv.erase_unordered (i);
414     else
415     sv[i]->call (*sv[i]);
416 pcg 1.13
417 pcg 1.21 sv.pending = false;
418     }
419 pcg 1.13 }
420 pcg 1.21 # endif
421    
422     # if IOM_IO
423     for (int i = iow.size (); i--; )
424     if (!iow[i])
425     iow.erase_unordered (i);
426     else
427     {
428 pcg 1.27 io_watcher &w = *iow[i];
429     short revents = w.events;
430 pcg 1.21
431 pcg 1.27 if (!FD_ISSET (w.fd, &rfd)) revents &= ~EVENT_READ;
432     if (!FD_ISSET (w.fd, &wfd)) revents &= ~EVENT_WRITE;
433 pcg 1.21
434     if (revents)
435 pcg 1.27 w.call (w, revents);
436 pcg 1.21 }
437     #endif
438     }
439 pcg 1.13 else if (fds < 0 && errno != EINTR)
440     {
441 pcg 1.21 perror ("io_manager: fatal error while waiting for I/O or time event, aborting.");
442 pcg 1.13 abort ();
443     }
444     #if IOM_IDLE
445     else
446     for (int i = iw.size (); i--; )
447     if (!iw[i])
448     iw.erase_unordered (i);
449     else
450     iw[i]->call (*iw[i]);
451     #endif
452    
453     #elif IOM_TIME
454     if (!to)
455     break;
456 pcg 1.9
457 pcg 1.13 select (0, 0, 0, 0, &to);
458     set_now ();
459     #else
460     break;
461     #endif
462 pcg 1.1 }
463     }
464