]> Dogcows Code - chaz/openbox/blob - openbox/mainloop.c
using the ObMainLoop, which rulz the planet
[chaz/openbox] / openbox / mainloop.c
1 #include "mainloop.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <sys/select.h>
6 #include <signal.h>
7
8 typedef struct _ObMainLoopTimer ObMainLoopTimer;
9 typedef struct _ObMainLoopSignal ObMainLoopSignal;
10 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType;
11 typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType;
12 typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType;
13
14 /* this should be more than the number of possible signals on any
15 architecture... */
16 #define NUM_SIGNALS 99
17
18 /* all created ObMainLoops. Used by the signal handler to pass along signals */
19 static GSList *all_loops;
20
21 /* signals are global to all loops */
22 struct {
23 guint installed; /* a ref count */
24 struct sigaction oldact;
25 } all_signals[NUM_SIGNALS];
26
27 /* a set of all possible signals */
28 sigset_t all_signals_set;
29
30 /* signals which cause a core dump, these can't be used for callbacks */
31 static gint core_signals[] =
32 {
33 SIGABRT,
34 SIGSEGV,
35 SIGFPE,
36 SIGILL,
37 SIGQUIT,
38 SIGTRAP,
39 SIGSYS,
40 SIGBUS,
41 SIGXCPU,
42 SIGXFSZ
43 };
44 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
45
46 static void sighandler(gint sig);
47 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait);
48 static void fd_handler_destroy(gpointer data);
49
50 struct _ObMainLoop
51 {
52 Display *display;
53
54 gboolean run; /* do keep running */
55 gboolean running; /* is still running */
56
57 GSList *x_handlers;
58
59 gint fd_x; /* The X fd is a special case! */
60 gint fd_max;
61 GHashTable *fd_handlers;
62 fd_set fd_set;
63
64 GSList *timers;
65 GTimeVal now;
66 GTimeVal ret_wait;
67
68 gboolean signal_fired;
69 guint signals_fired[NUM_SIGNALS];
70 GSList *signal_handlers[NUM_SIGNALS];
71 };
72
73 struct _ObMainLoopTimer
74 {
75 gulong delay;
76 GSourceFunc func;
77 gpointer data;
78 GDestroyNotify destroy;
79
80 /* The timer needs to be freed */
81 gboolean del_me;
82 /* The time the last fire should've been at */
83 GTimeVal last;
84 /* When this timer will next trigger */
85 GTimeVal timeout;
86 };
87
88 struct _ObMainLoopSignalHandlerType
89 {
90 ObMainLoop *loop;
91 gint signal;
92 gpointer data;
93 ObMainLoopSignalHandler func;
94 GDestroyNotify destroy;
95 };
96
97 struct _ObMainLoopXHandlerType
98 {
99 ObMainLoop *loop;
100 gpointer data;
101 ObMainLoopXHandler func;
102 GDestroyNotify destroy;
103 };
104
105 struct _ObMainLoopFdHandlerType
106 {
107 ObMainLoop *loop;
108 gint fd;
109 gpointer data;
110 ObMainLoopFdHandler func;
111 GDestroyNotify destroy;
112 };
113
114 ObMainLoop *ob_main_loop_new(Display *display)
115 {
116 ObMainLoop *loop;
117
118 loop = g_new0(ObMainLoop, 1);
119 loop->display = display;
120 loop->fd_x = ConnectionNumber(display);
121 FD_ZERO(&loop->fd_set);
122 FD_SET(loop->fd_x, &loop->fd_set);
123 loop->fd_max = loop->fd_x;
124
125 loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
126 NULL, fd_handler_destroy);
127
128 g_get_current_time(&loop->now);
129
130 /* only do this if we're the first loop created */
131 if (!all_loops) {
132 guint i;
133 struct sigaction action;
134 sigset_t sigset;
135
136 /* initialize the all_signals_set */
137 sigfillset(&all_signals_set);
138
139 sigemptyset(&sigset);
140 action.sa_handler = sighandler;
141 action.sa_mask = sigset;
142 action.sa_flags = SA_NOCLDSTOP;
143
144 /* grab all the signals that cause core dumps */
145 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
146 /* SIGABRT is curiously not grabbed here!! that's because when we
147 get one of the core_signals, we use abort() to dump the core.
148 And having the abort() only go back to our signal handler again
149 is less than optimal */
150 if (core_signals[i] != SIGABRT) {
151 sigaction(core_signals[i], &action,
152 &all_signals[core_signals[i]].oldact);
153 all_signals[core_signals[i]].installed++;
154 }
155 }
156 }
157
158 all_loops = g_slist_prepend(all_loops, loop);
159
160 return loop;
161 }
162
163 void ob_main_loop_destroy(ObMainLoop *loop)
164 {
165 guint i;
166 GSList *it, *next;
167
168 if (loop) {
169 g_assert(loop->running == FALSE);
170
171 for (it = loop->x_handlers; it; it = next) {
172 ObMainLoopXHandlerType *h = it->data;
173 next = g_slist_next(it);
174 ob_main_loop_x_remove(loop, h->func);
175 }
176
177 g_hash_table_destroy(loop->fd_handlers);
178
179 for (it = loop->timers; it; it = g_slist_next(it)) {
180 ObMainLoopTimer *t = it->data;
181 if (t->destroy) t->destroy(t->data);
182 g_free(t);
183 }
184 g_slist_free(loop->timers);
185 loop->timers = NULL;
186
187 for (i = 0; i < NUM_SIGNALS; ++i)
188 for (it = loop->signal_handlers[i]; it; it = next) {
189 ObMainLoopSignalHandlerType *h = it->data;
190 next = g_slist_next(it);
191 ob_main_loop_signal_remove(loop, h->func);
192 }
193
194 all_loops = g_slist_remove(all_loops, loop);
195
196 /* only do this if we're the last loop destroyed */
197 if (!all_loops) {
198 guint i;
199
200 /* grab all the signals that cause core dumps */
201 for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
202 if (all_signals[core_signals[i]].installed) {
203 sigaction(core_signals[i],
204 &all_signals[core_signals[i]].oldact, NULL);
205 all_signals[core_signals[i]].installed--;
206 }
207 }
208 }
209
210 g_free(loop);
211 }
212 }
213
214 static void fd_handle_foreach(gpointer key,
215 gpointer value,
216 gpointer data)
217 {
218 ObMainLoopFdHandlerType *h = value;
219 fd_set *set = data;
220
221 if (FD_ISSET(h->fd, set))
222 h->func(h->fd, h->data);
223 }
224
225 void ob_main_loop_run(ObMainLoop *loop)
226 {
227 XEvent e;
228 struct timeval *wait;
229 fd_set selset;
230 GSList *it;
231
232 loop->run = TRUE;
233 loop->running = TRUE;
234
235 while (loop->run) {
236 if (loop->signal_fired) {
237 guint i;
238 sigset_t oldset;
239
240 /* block signals so that we can do this without the data changing
241 on us */
242 sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
243
244 for (i = 0; i < NUM_SIGNALS; ++i) {
245 while (loop->signals_fired[i]) {
246 for (it = loop->signal_handlers[i];
247 it; it = g_slist_next(it)) {
248 ObMainLoopSignalHandlerType *h = it->data;
249 h->func(i, h->data);
250 }
251 loop->signals_fired[i]--;
252 }
253 }
254 loop->signal_fired = FALSE;
255
256 sigprocmask(SIG_SETMASK, &oldset, NULL);
257 } else if (XPending(loop->display)) {
258 do {
259 XNextEvent(loop->display, &e);
260
261 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
262 ObMainLoopXHandlerType *h = it->data;
263 h->func(&e, h->data);
264 }
265 } while (XPending(loop->display));
266 } else {
267 /* this only runs if there were no x events received */
268
269 timer_dispatch(loop, (GTimeVal**)&wait);
270 selset = loop->fd_set;
271 /* there is a small race condition here. if a signal occurs
272 between this if() and the select() then we will not process
273 the signal until 'wait' expires. possible solutions include
274 using GStaticMutex, and having the signal handler set 'wait'
275 to 0 */
276 if (!loop->signal_fired)
277 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
278
279 /* handle the X events with highest prioirity */
280 if (FD_ISSET(loop->fd_x, &selset))
281 continue;
282
283 g_hash_table_foreach(loop->fd_handlers,
284 fd_handle_foreach, &selset);
285 }
286 }
287
288 loop->running = FALSE;
289 }
290
291 void ob_main_loop_exit(ObMainLoop *loop)
292 {
293 loop->run = FALSE;
294 }
295
296 /*** XEVENT WATCHERS ***/
297
298 void ob_main_loop_x_add(ObMainLoop *loop,
299 ObMainLoopXHandler handler,
300 gpointer data,
301 GDestroyNotify notify)
302 {
303 ObMainLoopXHandlerType *h;
304
305 h = g_new(ObMainLoopXHandlerType, 1);
306 h->loop = loop;
307 h->func = handler;
308 h->data = data;
309 h->destroy = notify;
310 loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
311 }
312
313 void ob_main_loop_x_remove(ObMainLoop *loop,
314 ObMainLoopXHandler handler)
315 {
316 GSList *it, *next;
317
318 for (it = loop->x_handlers; it; it = next) {
319 ObMainLoopXHandlerType *h = it->data;
320 next = g_slist_next(it);
321 if (h->func == handler) {
322 loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
323 if (h->destroy) h->destroy(h->data);
324 g_free(h);
325 }
326 }
327 }
328
329 /*** SIGNAL WATCHERS ***/
330
331 static void sighandler(gint sig)
332 {
333 GSList *it;
334 guint i;
335
336 g_return_if_fail(sig < NUM_SIGNALS);
337
338 for (i = 0; i < NUM_CORE_SIGNALS; ++i)
339 if (sig == core_signals[i]) {
340 /* XXX special case for signals that default to core dump.
341 but throw some helpful output here... */
342
343 fprintf(stderr, "Fuck ya. Core dump. (Signal=%d)\n", sig);
344
345 /* die with a core dump */
346 abort();
347 }
348
349 for (it = all_loops; it; it = g_slist_next(it)) {
350 ObMainLoop *loop = it->data;
351 loop->signal_fired = TRUE;
352 loop->signals_fired[sig]++;
353 }
354 }
355
356 void ob_main_loop_signal_add(ObMainLoop *loop,
357 gint signal,
358 ObMainLoopSignalHandler handler,
359 gpointer data,
360 GDestroyNotify notify)
361 {
362 ObMainLoopSignalHandlerType *h;
363
364 g_return_if_fail(signal < NUM_SIGNALS);
365
366 h = g_new(ObMainLoopSignalHandlerType, 1);
367 h->loop = loop;
368 h->signal = signal;
369 h->func = handler;
370 h->data = data;
371 h->destroy = notify;
372 loop->signal_handlers[h->signal] =
373 g_slist_prepend(loop->signal_handlers[h->signal], h);
374
375 if (!all_signals[signal].installed) {
376 struct sigaction action;
377 sigset_t sigset;
378
379 sigemptyset(&sigset);
380 action.sa_handler = sighandler;
381 action.sa_mask = sigset;
382 action.sa_flags = SA_NOCLDSTOP;
383
384 sigaction(signal, &action, &all_signals[signal].oldact);
385 }
386
387 all_signals[signal].installed++;
388 }
389
390 void ob_main_loop_signal_remove(ObMainLoop *loop,
391 ObMainLoopSignalHandler handler)
392 {
393 guint i;
394 GSList *it, *next;
395
396 for (i = 0; i < NUM_SIGNALS; ++i) {
397 for (it = loop->signal_handlers[i]; it; it = next) {
398 ObMainLoopSignalHandlerType *h = it->data;
399
400 next = g_slist_next(it);
401
402 if (h->func == handler) {
403 g_assert(all_signals[h->signal].installed > 0);
404
405 all_signals[h->signal].installed--;
406 if (!all_signals[h->signal].installed) {
407 sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
408 }
409
410 loop->signal_handlers[i] =
411 g_slist_delete_link(loop->signal_handlers[i], it);
412 if (h->destroy) h->destroy(h->data);
413
414 g_free(h);
415 }
416 }
417 }
418
419 }
420
421 /*** FILE DESCRIPTOR WATCHERS ***/
422
423 static void max_fd_func(gpointer key, gpointer value, gpointer data)
424 {
425 ObMainLoop *loop = data;
426
427 /* key is the fd */
428 loop->fd_max = MAX(loop->fd_max, *(gint*)key);
429 }
430
431 static void calc_max_fd(ObMainLoop *loop)
432 {
433 loop->fd_max = loop->fd_x;
434
435 g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
436 }
437
438 void ob_main_loop_fd_add(ObMainLoop *loop,
439 gint fd,
440 ObMainLoopFdHandler handler,
441 gpointer data,
442 GDestroyNotify notify)
443 {
444 ObMainLoopFdHandlerType *h;
445
446 h = g_new(ObMainLoopFdHandlerType, 1);
447 h->loop = loop;
448 h->fd = fd;
449 h->func = handler;
450 h->data = data;
451 h->destroy = notify;
452
453 g_hash_table_replace(loop->fd_handlers, &h->fd, h);
454 FD_SET(h->fd, &loop->fd_set);
455 calc_max_fd(loop);
456 }
457
458 static void fd_handler_destroy(gpointer data)
459 {
460 ObMainLoopFdHandlerType *h = data;
461
462 FD_CLR(h->fd, &h->loop->fd_set);
463
464 if (h->destroy)
465 h->destroy(h->data);
466 }
467
468 void ob_main_loop_fd_remove(ObMainLoop *loop,
469 gint fd)
470 {
471 g_hash_table_remove(loop->fd_handlers, &fd);
472 }
473
474 /*** TIMEOUTS ***/
475
476 #define NEAREST_TIMEOUT(loop) \
477 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
478
479 static long timecompare(GTimeVal *a, GTimeVal *b)
480 {
481 long r;
482
483 if ((r = b->tv_sec - a->tv_sec)) return r;
484 return b->tv_usec - a->tv_usec;
485
486 }
487
488 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
489 {
490 GSList *it;
491 for (it = loop->timers; it; it = g_slist_next(it)) {
492 ObMainLoopTimer *t = it->data;
493 if (timecompare(&ins->timeout, &t->timeout) <= 0) {
494 loop->timers = g_slist_insert_before(loop->timers, it, ins);
495 break;
496 }
497 }
498 if (it == NULL) /* didnt fit anywhere in the list */
499 loop->timers = g_slist_append(loop->timers, ins);
500 }
501
502 void ob_main_loop_timeout_add(ObMainLoop *loop,
503 gulong microseconds,
504 GSourceFunc handler,
505 gpointer data,
506 GDestroyNotify notify)
507 {
508 ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
509 t->delay = microseconds;
510 t->func = handler;
511 t->data = data;
512 t->destroy = notify;
513 t->del_me = FALSE;
514 t->last = t->timeout = loop->now;
515 g_time_val_add(&t->timeout, t->delay);
516
517 insert_timer(loop, t);
518 }
519
520 void ob_main_loop_timeout_remove(ObMainLoop *loop,
521 GSourceFunc handler)
522 {
523 GSList *it;
524
525 for (it = loop->timers; it; it = g_slist_next(it)) {
526 ObMainLoopTimer *t = it->data;
527 if (t->func == handler) {
528 t->del_me = TRUE;
529 break;
530 }
531 }
532 }
533
534 /* find the time to wait for the nearest timeout */
535 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
536 {
537 if (loop->timers == NULL)
538 return FALSE;
539
540 tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
541 tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
542
543 while (tm->tv_usec < 0) {
544 tm->tv_usec += G_USEC_PER_SEC;
545 tm->tv_sec--;
546 }
547 tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
548 tm->tv_usec %= G_USEC_PER_SEC;
549 if (tm->tv_sec < 0)
550 tm->tv_sec = 0;
551
552 return TRUE;
553 }
554
555 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
556 {
557 g_get_current_time(&loop->now);
558
559 while (loop->timers != NULL) {
560 ObMainLoopTimer *curr = loop->timers->data; /* get the top element */
561 /* since timer_stop doesn't actually free the timer, we have to do our
562 real freeing in here.
563 */
564 if (curr->del_me) {
565 /* delete the top */
566 loop->timers = g_slist_delete_link(loop->timers, loop->timers);
567 g_free(curr);
568 continue;
569 }
570
571 /* the queue is sorted, so if this timer shouldn't fire, none are
572 ready */
573 if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) <= 0)
574 break;
575
576 /* we set the last fired time to delay msec after the previous firing,
577 then re-insert. timers maintain their order and may trigger more
578 than once if they've waited more than one delay's worth of time.
579 */
580 loop->timers = g_slist_delete_link(loop->timers, loop->timers);
581 g_time_val_add(&curr->last, curr->delay);
582 if (curr->func(curr->data)) {
583 g_time_val_add(&curr->timeout, curr->delay);
584 insert_timer(loop, curr);
585 } else if (curr->destroy) {
586 curr->destroy(curr->data);
587 }
588
589 /* if at least one timer fires, then don't wait on X events, as there
590 may already be some in the queue from the timer callbacks.
591 */
592 loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
593 *wait = &loop->ret_wait;
594 return;
595 }
596
597 if (nearest_timeout_wait(loop, &loop->ret_wait))
598 *wait = &loop->ret_wait;
599 else
600 *wait = NULL;
601 }
This page took 0.065878 seconds and 4 git commands to generate.