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