ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/iom.C
Revision: 1.33
Committed: Thu Jan 19 09:47:15 2006 UTC (18 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.32: +62 -16 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.30 Copyright (C) 2003, 2004 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_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 root 1.33 #if IOM_CHILD
78     static io_manager_vec<child_watcher> pw;
79     #endif
80 pcg 1.8
81 pcg 1.10 // 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.13 static struct tw0 : time_watcher
86 root 1.33 {
87     void cb (time_watcher &w)
88 pcg 1.10 {
89 root 1.33 // 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.10
95 root 1.33 tw0 ()
96     : time_watcher (this, &tw0::cb)
97     { }
98     } tw0;
99 pcg 1.10
100 pcg 1.1 tstamp NOW;
101 root 1.19
102 root 1.33 #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     w->stop ();
122     w->call (*w, status);
123     }
124     }
125    
126     }
127    
128     sw0 ()
129     : sig_watcher (this, &sw0::cb)
130     { }
131     } sw0;
132     #endif
133    
134 root 1.19 #if IOM_TIME
135 root 1.24 tstamp io_manager::now ()
136 root 1.19 {
137     struct timeval tv;
138    
139     gettimeofday (&tv, 0);
140 root 1.24 return (tstamp)tv.tv_sec + (tstamp)tv.tv_usec / 1000000.;
141     }
142    
143     void io_manager::set_now ()
144     {
145     NOW = now ();
146 root 1.19 }
147     #endif
148    
149 pcg 1.8 static bool iom_valid;
150 root 1.19
151     // used for initialisation only
152     static struct init {
153     init ()
154     {
155 root 1.33 iom_valid = true;
156    
157 root 1.19 #if IOM_SIG
158     sigemptyset (&sigs);
159    
160     if (pipe (sigpipe))
161     {
162     perror ("io_manager: unable to create signal pipe, aborting.");
163     abort ();
164     }
165    
166 root 1.25 fcntl (sigpipe[0], F_SETFL, O_NONBLOCK); fcntl (sigpipe[0], F_SETFD, FD_CLOEXEC);
167     fcntl (sigpipe[1], F_SETFL, O_NONBLOCK); fcntl (sigpipe[1], F_SETFD, FD_CLOEXEC);
168 root 1.19 #endif
169    
170 root 1.33 #if IOM_CHILD
171     sw0.start (SIGCHLD);
172     #endif
173 root 1.19
174     #if IOM_TIME
175 root 1.24 io_manager::set_now ();
176 root 1.19
177     tw0.start (TSTAMP_MAX);
178     #endif
179     }
180    
181 root 1.31 ~init ()
182     {
183     iom_valid = false;
184     }
185    
186 root 1.19 static void required ();
187     } init;
188    
189     void
190     init::required ()
191     {
192     if (!iom_valid)
193     {
194     write (2, "io_manager: early registration attempt, aborting.\n",
195     sizeof ("io_manager: early registration attempt, aborting.\n") - 1);
196     abort ();
197     }
198     }
199 pcg 1.1
200 pcg 1.4 template<class watcher>
201 root 1.19 void io_manager::reg (watcher &w, io_manager_vec<watcher> &queue)
202 pcg 1.4 {
203 root 1.19 init::required ();
204 pcg 1.8
205 root 1.19 if (!w.active)
206 pcg 1.8 {
207 root 1.19 queue.push_back (&w);
208     w.active = queue.size ();
209 pcg 1.8 }
210 pcg 1.4 }
211    
212     template<class watcher>
213 root 1.19 void io_manager::unreg (watcher &w, io_manager_vec<watcher> &queue)
214 pcg 1.4 {
215 pcg 1.8 if (!iom_valid)
216     return;
217 pcg 1.4
218 root 1.19 if (w.active)
219 pcg 1.8 {
220 root 1.19 queue [w.active - 1] = 0;
221     w.active = 0;
222 pcg 1.8 }
223 pcg 1.4 }
224    
225     #if IOM_TIME
226 pcg 1.1 void time_watcher::trigger ()
227     {
228     call (*this);
229 root 1.19 io_manager::reg (*this);
230 pcg 1.1 }
231    
232 root 1.19 void io_manager::reg (time_watcher &w) { io_manager::reg (w, tw); }
233     void io_manager::unreg (time_watcher &w) { io_manager::unreg (w, tw); }
234 pcg 1.8 #endif
235 pcg 1.3
236 pcg 1.8 #if IOM_IO
237 root 1.19 void io_manager::reg (io_watcher &w) { io_manager::reg (w, iow); }
238     void io_manager::unreg (io_watcher &w) { io_manager::unreg (w, iow); }
239 pcg 1.4 #endif
240 pcg 1.1
241 pcg 1.4 #if IOM_CHECK
242 root 1.19 void io_manager::reg (check_watcher &w) { io_manager::reg (w, cw); }
243     void io_manager::unreg (check_watcher &w) { io_manager::unreg (w, cw); }
244 pcg 1.4 #endif
245 pcg 1.1
246 pcg 1.7 #if IOM_IDLE
247 root 1.19 void io_manager::reg (idle_watcher &w) { io_manager::reg (w, iw); }
248     void io_manager::unreg (idle_watcher &w) { io_manager::unreg (w, iw); }
249 pcg 1.7 #endif
250    
251 root 1.19 #if IOM_SIG
252     static void
253     sighandler (int signum)
254 pcg 1.1 {
255 root 1.19 sw [signum - 1]->pending = true;
256 pcg 1.1
257 root 1.19 // we use a pipe for signal notifications, as most current
258     // OSes (Linux...) do not implement pselect correctly. ugh.
259     char ch = signum; // actual content not used
260     write (sigpipe[1], &ch, 1);
261 pcg 1.16 }
262    
263 root 1.19 void io_manager::reg (sig_watcher &w)
264 pcg 1.16 {
265 root 1.32 init::required ();
266    
267 root 1.19 assert (0 < w.signum);
268 pcg 1.16
269 root 1.19 sw.reserve (w.signum);
270 pcg 1.16
271 root 1.19 while (sw.size () < w.signum) // pathetic
272     sw.push_back (0);
273 pcg 1.16
274 root 1.19 sig_vec *&sv = sw[w.signum - 1];
275 pcg 1.16
276     if (!sv)
277     {
278     sv = new sig_vec;
279    
280 root 1.19 sigaddset (&sigs, w.signum);
281     sigprocmask (SIG_BLOCK, &sigs, NULL);
282    
283 pcg 1.16 struct sigaction sa;
284 root 1.19 sa.sa_handler = sighandler;
285 pcg 1.16 sigfillset (&sa.sa_mask);
286 root 1.19 sa.sa_flags = SA_RESTART;
287 pcg 1.16
288 root 1.19 if (sigaction (w.signum, &sa, 0))
289 pcg 1.16 {
290 root 1.19 perror ("io_manager: error while installing signal handler, ignoring.");
291 pcg 1.16 abort ();
292     }
293 root 1.19
294 pcg 1.16 }
295    
296 root 1.19 io_manager::reg (w, *sv);
297 pcg 1.16 }
298    
299 root 1.19 void io_manager::unreg (sig_watcher &w)
300 pcg 1.16 {
301 root 1.32 if (!w.active || !iom_valid)
302 root 1.19 return;
303    
304     assert (0 < w.signum && w.signum <= sw.size ());
305 pcg 1.16
306 root 1.19 io_manager::unreg (w, *sw[w.signum - 1]);
307 pcg 1.16 }
308    
309     void sig_watcher::start (int signum)
310     {
311     stop ();
312     this->signum = signum;
313 root 1.19 io_manager::reg (*this);
314 pcg 1.16 }
315     #endif
316 root 1.19
317 root 1.33 #if IOM_CHILD
318     void io_manager::reg (child_watcher &w) { io_manager::reg (w, pw); }
319     void io_manager::unreg (child_watcher &w) { io_manager::unreg (w, pw); }
320     #endif
321    
322 pcg 1.1 void io_manager::loop ()
323     {
324 root 1.19 init::required ();
325    
326 pcg 1.4 #if IOM_TIME
327 pcg 1.1 set_now ();
328 pcg 1.4 #endif
329 pcg 1.1
330     for (;;)
331     {
332    
333 pcg 1.7 #if IOM_TIME
334 root 1.23 // call pending time watchers
335     {
336     bool activity;
337 pcg 1.1
338 root 1.23 do
339     {
340     activity = false;
341 pcg 1.1
342 root 1.23 for (int i = tw.size (); i--; )
343     if (!tw[i])
344     tw.erase_unordered (i);
345     else if (tw[i]->at <= NOW)
346     {
347     time_watcher &w = *tw[i];
348    
349     unreg (w);
350     w.call (w);
351 pcg 1.7
352 root 1.23 activity = true;
353 pcg 1.7 }
354 root 1.23 }
355     while (activity);
356     }
357 pcg 1.7 #endif
358 pcg 1.6
359     #if IOM_CHECK
360 root 1.23 // call all check watchers
361 pcg 1.8 for (int i = cw.size (); i--; )
362     if (!cw[i])
363     cw.erase_unordered (i);
364     else
365     cw[i]->call (*cw[i]);
366 root 1.23 #endif
367 pcg 1.12
368 root 1.23 struct TIMEVAL *to = 0;
369     struct TIMEVAL tval;
370    
371     #if IOM_IDLE
372     if (iw.size ())
373 pcg 1.12 {
374     tval.tv_sec = 0;
375 root 1.19 tval.TV_FRAC = 0;
376 pcg 1.12 to = &tval;
377     }
378 root 1.23 else
379     #endif
380     {
381     #if IOM_TIME
382     // find earliest active watcher
383     time_watcher *next = tw[0]; // the first time-watcher must exist at ALL times
384    
385     for (io_manager_vec<time_watcher>::const_iterator i = tw.end (); i-- > tw.begin (); )
386     if (*i && (*i)->at < next->at)
387     next = *i;
388    
389     if (next->at > NOW && next != tw[0])
390     {
391     double diff = next->at - NOW;
392     tval.tv_sec = (int)diff;
393     tval.TV_FRAC = (int) ((diff - tval.tv_sec) * TV_MULT);
394     to = &tval;
395     }
396     }
397 pcg 1.4 #endif
398 pcg 1.1
399 root 1.19 #if IOM_IO || IOM_SIG
400 pcg 1.15 fd_set rfd, wfd;
401 pcg 1.1
402     FD_ZERO (&rfd);
403     FD_ZERO (&wfd);
404    
405     int fds = 0;
406    
407 root 1.19 # if IOM_IO
408 root 1.23 for (io_manager_vec<io_watcher>::const_iterator i = iow.end (); i-- > iow.begin (); )
409 pcg 1.8 if (*i)
410     {
411     if ((*i)->events & EVENT_READ ) FD_SET ((*i)->fd, &rfd);
412     if ((*i)->events & EVENT_WRITE) FD_SET ((*i)->fd, &wfd);
413 pcg 1.1
414 pcg 1.8 if ((*i)->fd >= fds) fds = (*i)->fd + 1;
415     }
416 root 1.19 # endif
417 pcg 1.1
418 root 1.23 if (!to && !fds) //TODO: also check idle_watchers and check_watchers?
419 pcg 1.5 break; // no events
420    
421 root 1.19 # if IOM_SIG
422     FD_SET (sigpipe[0], &rfd);
423     if (sigpipe[0] >= fds) fds = sigpipe[0] + 1;
424     # endif
425    
426     # if IOM_SIG
427     // there is no race, as we use a pipe for signals, so select
428     // will return if a signal is caught.
429     sigprocmask (SIG_UNBLOCK, &sigs, NULL);
430     # endif
431 pcg 1.15 fds = select (fds, &rfd, &wfd, NULL, to);
432 root 1.19 # if IOM_SIG
433     sigprocmask (SIG_BLOCK, &sigs, NULL);
434     # endif
435    
436 pcg 1.4 # if IOM_TIME
437 root 1.28 {
438     // update time, try to compensate for gross non-monotonic time changes
439     tstamp diff = NOW;
440     set_now ();
441     diff = NOW - diff;
442    
443     if (diff < 0)
444     for (io_manager_vec<time_watcher>::const_iterator i = tw.end (); i-- > tw.begin (); )
445     if (*i)
446     (*i)->at += diff;
447     }
448 pcg 1.4 # endif
449 pcg 1.1
450     if (fds > 0)
451 root 1.19 {
452     # if IOM_SIG
453     if (FD_ISSET (sigpipe[0], &rfd))
454 pcg 1.8 {
455 root 1.19 char ch;
456    
457     while (read (sigpipe[0], &ch, 1) > 0)
458     ;
459 pcg 1.1
460 root 1.23 for (vector<sig_vec *>::iterator svp = sw.end (); svp-- > sw.begin (); )
461 root 1.19 if (*svp && (*svp)->pending)
462     {
463     sig_vec &sv = **svp;
464     for (int i = sv.size (); i--; )
465     if (!sv[i])
466     sv.erase_unordered (i);
467     else
468     sv[i]->call (*sv[i]);
469 pcg 1.4
470 root 1.19 sv.pending = false;
471     }
472 pcg 1.8 }
473 root 1.19 # endif
474    
475     # if IOM_IO
476     for (int i = iow.size (); i--; )
477     if (!iow[i])
478     iow.erase_unordered (i);
479     else
480     {
481 root 1.27 io_watcher &w = *iow[i];
482     short revents = w.events;
483 root 1.19
484 root 1.27 if (!FD_ISSET (w.fd, &rfd)) revents &= ~EVENT_READ;
485     if (!FD_ISSET (w.fd, &wfd)) revents &= ~EVENT_WRITE;
486 root 1.19
487     if (revents)
488 root 1.27 w.call (w, revents);
489 root 1.19 }
490     #endif
491     }
492 pcg 1.8 else if (fds < 0 && errno != EINTR)
493     {
494 root 1.19 perror ("io_manager: fatal error while waiting for I/O or time event, aborting.");
495 pcg 1.8 abort ();
496     }
497 pcg 1.7 #if IOM_IDLE
498 pcg 1.8 else
499     for (int i = iw.size (); i--; )
500     if (!iw[i])
501     iw.erase_unordered (i);
502     else
503     iw[i]->call (*iw[i]);
504 pcg 1.7 #endif
505    
506 pcg 1.4 #elif IOM_TIME
507 pcg 1.5 if (!to)
508     break;
509    
510 pcg 1.4 select (0, 0, 0, 0, &to);
511     set_now ();
512 pcg 1.5 #else
513     break;
514 pcg 1.4 #endif
515 pcg 1.5 }
516 pcg 1.1 }
517