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