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