don't let repeating timers, with a very fast timer in the queue, cause the main loop...
[mikachu/openbox.git] / 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 }