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