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