make the segfault swearing more clear
[mikachu/openbox.git] / 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 yah. 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     g_get_current_time(&loop->now);
515     t->last = t->timeout = loop->now;
516     g_time_val_add(&t->timeout, t->delay);
517
518     insert_timer(loop, t);
519 }
520
521 void ob_main_loop_timeout_remove(ObMainLoop *loop,
522                                  GSourceFunc handler)
523 {
524     GSList *it;
525
526     for (it = loop->timers; it; it = g_slist_next(it)) {
527         ObMainLoopTimer *t = it->data;
528         if (t->func == handler) {
529             t->del_me = TRUE;
530             break;
531         }
532     }
533 }
534
535 /* find the time to wait for the nearest timeout */
536 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
537 {
538   if (loop->timers == NULL)
539     return FALSE;
540
541   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
542   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
543
544   while (tm->tv_usec < 0) {
545     tm->tv_usec += G_USEC_PER_SEC;
546     tm->tv_sec--;
547   }
548   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
549   tm->tv_usec %= G_USEC_PER_SEC;
550   if (tm->tv_sec < 0)
551     tm->tv_sec = 0;
552
553   return TRUE;
554 }
555
556 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
557 {
558     g_get_current_time(&loop->now);
559
560     while (loop->timers != NULL) {
561         ObMainLoopTimer *curr = loop->timers->data; /* get the top element */
562         /* since timer_stop doesn't actually free the timer, we have to do our
563            real freeing in here.
564         */
565         if (curr->del_me) {
566             /* delete the top */
567             loop->timers = g_slist_delete_link(loop->timers, loop->timers); 
568             g_free(curr);
569             continue;
570         }
571         
572         /* the queue is sorted, so if this timer shouldn't fire, none are 
573            ready */
574         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) <= 0)
575             break;
576
577         /* we set the last fired time to delay msec after the previous firing,
578            then re-insert.  timers maintain their order and may trigger more
579            than once if they've waited more than one delay's worth of time.
580         */
581         loop->timers = g_slist_delete_link(loop->timers, loop->timers);
582         g_time_val_add(&curr->last, curr->delay);
583         if (curr->func(curr->data)) {
584             g_time_val_add(&curr->timeout, curr->delay);
585             insert_timer(loop, curr);
586         } else if (curr->destroy) {
587             curr->destroy(curr->data);
588         }
589
590         /* if at least one timer fires, then don't wait on X events, as there
591            may already be some in the queue from the timer callbacks.
592         */
593         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
594         *wait = &loop->ret_wait;
595         return;
596     }
597
598     if (nearest_timeout_wait(loop, &loop->ret_wait))
599         *wait = &loop->ret_wait;
600     else
601         *wait = NULL;
602 }