ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/iom.C
Revision: 1.38
Committed: Thu Oct 25 10:44:14 2007 UTC (16 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.37: +165 -128 lines
Log Message:
*** empty log message ***

File Contents

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