6 #include <sys/select.h>
9 typedef struct _ObMainLoopTimer ObMainLoopTimer
;
10 typedef struct _ObMainLoopSignal ObMainLoopSignal
;
11 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType
;
12 typedef struct _ObMainLoopXHandlerType ObMainLoopXHandlerType
;
13 typedef struct _ObMainLoopFdHandlerType ObMainLoopFdHandlerType
;
15 /* this should be more than the number of possible signals on any
17 #define NUM_SIGNALS 99
19 /* all created ObMainLoops. Used by the signal handler to pass along signals */
20 static GSList
*all_loops
;
22 /* signals are global to all loops */
24 guint installed
; /* a ref count */
25 struct sigaction oldact
;
26 } all_signals
[NUM_SIGNALS
];
28 /* a set of all possible signals */
29 sigset_t all_signals_set
;
31 /* signals which cause a core dump, these can't be used for callbacks */
32 static gint core_signals
[] =
45 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
47 static void sighandler(gint sig
);
48 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
);
49 static void fd_handler_destroy(gpointer data
);
55 gboolean run
; /* do keep running */
56 gboolean running
; /* is still running */
60 gint fd_x
; /* The X fd is a special case! */
62 GHashTable
*fd_handlers
;
69 gboolean signal_fired
;
70 guint signals_fired
[NUM_SIGNALS
];
71 GSList
*signal_handlers
[NUM_SIGNALS
];
74 struct _ObMainLoopTimer
79 GDestroyNotify destroy
;
81 /* The timer needs to be freed */
83 /* The time the last fire should've been at */
85 /* When this timer will next trigger */
89 struct _ObMainLoopSignalHandlerType
94 ObMainLoopSignalHandler func
;
95 GDestroyNotify destroy
;
98 struct _ObMainLoopXHandlerType
102 ObMainLoopXHandler func
;
103 ObMainLoopXDoneHandler done_func
;
104 GDestroyNotify destroy
;
107 struct _ObMainLoopFdHandlerType
112 ObMainLoopFdHandler func
;
113 GDestroyNotify destroy
;
116 ObMainLoop
*ob_main_loop_new(Display
*display
)
120 loop
= g_new0(ObMainLoop
, 1);
121 loop
->display
= display
;
122 loop
->fd_x
= ConnectionNumber(display
);
123 FD_ZERO(&loop
->fd_set
);
124 FD_SET(loop
->fd_x
, &loop
->fd_set
);
125 loop
->fd_max
= loop
->fd_x
;
127 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
128 NULL
, fd_handler_destroy
);
130 g_get_current_time(&loop
->now
);
132 /* only do this if we're the first loop created */
135 struct sigaction action
;
138 /* initialize the all_signals_set */
139 sigfillset(&all_signals_set
);
141 sigemptyset(&sigset
);
142 action
.sa_handler
= sighandler
;
143 action
.sa_mask
= sigset
;
144 action
.sa_flags
= SA_NOCLDSTOP
;
146 /* grab all the signals that cause core dumps */
147 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
148 /* SIGABRT is curiously not grabbed here!! that's because when we
149 get one of the core_signals, we use abort() to dump the core.
150 And having the abort() only go back to our signal handler again
151 is less than optimal */
152 if (core_signals
[i
] != SIGABRT
) {
153 sigaction(core_signals
[i
], &action
,
154 &all_signals
[core_signals
[i
]].oldact
);
155 all_signals
[core_signals
[i
]].installed
++;
160 all_loops
= g_slist_prepend(all_loops
, loop
);
165 void ob_main_loop_destroy(ObMainLoop
*loop
)
171 g_assert(loop
->running
== FALSE
);
173 for (it
= loop
->x_handlers
; it
; it
= next
) {
174 ObMainLoopXHandlerType
*h
= it
->data
;
175 next
= g_slist_next(it
);
176 ob_main_loop_x_remove(loop
, h
->func
);
179 g_hash_table_destroy(loop
->fd_handlers
);
181 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
182 ObMainLoopTimer
*t
= it
->data
;
183 if (t
->destroy
) t
->destroy(t
->data
);
186 g_slist_free(loop
->timers
);
189 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
190 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
191 ObMainLoopSignalHandlerType
*h
= it
->data
;
192 next
= g_slist_next(it
);
193 ob_main_loop_signal_remove(loop
, h
->func
);
196 all_loops
= g_slist_remove(all_loops
, loop
);
198 /* only do this if we're the last loop destroyed */
202 /* grab all the signals that cause core dumps */
203 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
204 if (all_signals
[core_signals
[i
]].installed
) {
205 sigaction(core_signals
[i
],
206 &all_signals
[core_signals
[i
]].oldact
, NULL
);
207 all_signals
[core_signals
[i
]].installed
--;
216 static void fd_handle_foreach(gpointer key
,
220 ObMainLoopFdHandlerType
*h
= value
;
223 if (FD_ISSET(h
->fd
, set
))
224 h
->func(h
->fd
, h
->data
);
227 void ob_main_loop_run(ObMainLoop
*loop
)
230 struct timeval
*wait
;
235 loop
->running
= TRUE
;
238 if (loop
->signal_fired
) {
242 /* block signals so that we can do this without the data changing
244 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
246 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
247 while (loop
->signals_fired
[i
]) {
248 for (it
= loop
->signal_handlers
[i
];
249 it
; it
= g_slist_next(it
)) {
250 ObMainLoopSignalHandlerType
*h
= it
->data
;
253 loop
->signals_fired
[i
]--;
256 loop
->signal_fired
= FALSE
;
258 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
259 } else if (XPending(loop
->display
)) {
261 XNextEvent(loop
->display
, &e
);
263 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
264 ObMainLoopXHandlerType
*h
= it
->data
;
265 h
->func(&e
, h
->data
);
267 } while (XPending(loop
->display
));
269 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
270 ObMainLoopXHandlerType
*h
= it
->data
;
272 h
->done_func(h
->data
);
275 /* this only runs if there were no x events received */
277 timer_dispatch(loop
, (GTimeVal
**)&wait
);
278 selset
= loop
->fd_set
;
279 /* there is a small race condition here. if a signal occurs
280 between this if() and the select() then we will not process
281 the signal until 'wait' expires. possible solutions include
282 using GStaticMutex, and having the signal handler set 'wait'
284 if (!loop
->signal_fired
)
285 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
287 /* handle the X events with highest prioirity */
288 if (FD_ISSET(loop
->fd_x
, &selset
))
291 g_hash_table_foreach(loop
->fd_handlers
,
292 fd_handle_foreach
, &selset
);
296 loop
->running
= FALSE
;
299 void ob_main_loop_exit(ObMainLoop
*loop
)
304 /*** XEVENT WATCHERS ***/
306 void ob_main_loop_x_add(ObMainLoop
*loop
,
307 ObMainLoopXHandler handler
,
308 ObMainLoopXDoneHandler done_handler
,
310 GDestroyNotify notify
)
312 ObMainLoopXHandlerType
*h
;
314 h
= g_new(ObMainLoopXHandlerType
, 1);
317 h
->done_func
= done_handler
;
320 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
323 void ob_main_loop_x_remove(ObMainLoop
*loop
,
324 ObMainLoopXHandler handler
)
328 for (it
= loop
->x_handlers
; it
; it
= next
) {
329 ObMainLoopXHandlerType
*h
= it
->data
;
330 next
= g_slist_next(it
);
331 if (h
->func
== handler
) {
332 loop
->x_handlers
= g_slist_delete_link(loop
->x_handlers
, it
);
333 if (h
->destroy
) h
->destroy(h
->data
);
339 /*** SIGNAL WATCHERS ***/
341 static void sighandler(gint sig
)
346 g_return_if_fail(sig
< NUM_SIGNALS
);
348 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
349 if (sig
== core_signals
[i
]) {
350 /* XXX special case for signals that default to core dump.
351 but throw some helpful output here... */
353 fprintf(stderr
, "Fuck yah. Core dump. (Signal=%d)\n", sig
);
355 /* die with a core dump */
359 for (it
= all_loops
; it
; it
= g_slist_next(it
)) {
360 ObMainLoop
*loop
= it
->data
;
361 loop
->signal_fired
= TRUE
;
362 loop
->signals_fired
[sig
]++;
366 void ob_main_loop_signal_add(ObMainLoop
*loop
,
368 ObMainLoopSignalHandler handler
,
370 GDestroyNotify notify
)
372 ObMainLoopSignalHandlerType
*h
;
374 g_return_if_fail(signal
< NUM_SIGNALS
);
376 h
= g_new(ObMainLoopSignalHandlerType
, 1);
382 loop
->signal_handlers
[h
->signal
] =
383 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
385 if (!all_signals
[signal
].installed
) {
386 struct sigaction action
;
389 sigemptyset(&sigset
);
390 action
.sa_handler
= sighandler
;
391 action
.sa_mask
= sigset
;
392 action
.sa_flags
= SA_NOCLDSTOP
;
394 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
397 all_signals
[signal
].installed
++;
400 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
401 ObMainLoopSignalHandler handler
)
406 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
407 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
408 ObMainLoopSignalHandlerType
*h
= it
->data
;
410 next
= g_slist_next(it
);
412 if (h
->func
== handler
) {
413 g_assert(all_signals
[h
->signal
].installed
> 0);
415 all_signals
[h
->signal
].installed
--;
416 if (!all_signals
[h
->signal
].installed
) {
417 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
420 loop
->signal_handlers
[i
] =
421 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
422 if (h
->destroy
) h
->destroy(h
->data
);
431 /*** FILE DESCRIPTOR WATCHERS ***/
433 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
435 ObMainLoop
*loop
= data
;
438 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
441 static void calc_max_fd(ObMainLoop
*loop
)
443 loop
->fd_max
= loop
->fd_x
;
445 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
448 void ob_main_loop_fd_add(ObMainLoop
*loop
,
450 ObMainLoopFdHandler handler
,
452 GDestroyNotify notify
)
454 ObMainLoopFdHandlerType
*h
;
456 h
= g_new(ObMainLoopFdHandlerType
, 1);
463 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
464 FD_SET(h
->fd
, &loop
->fd_set
);
468 static void fd_handler_destroy(gpointer data
)
470 ObMainLoopFdHandlerType
*h
= data
;
472 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
478 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
481 g_hash_table_remove(loop
->fd_handlers
, &fd
);
486 #define NEAREST_TIMEOUT(loop) \
487 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
489 static long timecompare(GTimeVal
*a
, GTimeVal
*b
)
493 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
494 return b
->tv_usec
- a
->tv_usec
;
498 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
501 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
502 ObMainLoopTimer
*t
= it
->data
;
503 if (timecompare(&ins
->timeout
, &t
->timeout
) <= 0) {
504 loop
->timers
= g_slist_insert_before(loop
->timers
, it
, ins
);
508 if (it
== NULL
) /* didnt fit anywhere in the list */
509 loop
->timers
= g_slist_append(loop
->timers
, ins
);
512 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
516 GDestroyNotify notify
)
518 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
519 t
->delay
= microseconds
;
524 g_get_current_time(&loop
->now
);
525 t
->last
= t
->timeout
= loop
->now
;
526 g_time_val_add(&t
->timeout
, t
->delay
);
528 insert_timer(loop
, t
);
531 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
536 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
537 ObMainLoopTimer
*t
= it
->data
;
538 if (t
->func
== handler
)
543 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
,
549 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
550 ObMainLoopTimer
*t
= it
->data
;
551 if (t
->func
== handler
&& t
->data
== data
)
556 /* find the time to wait for the nearest timeout */
557 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
559 if (loop
->timers
== NULL
)
562 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
563 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
565 while (tm
->tv_usec
< 0) {
566 tm
->tv_usec
+= G_USEC_PER_SEC
;
569 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
570 tm
->tv_usec
%= G_USEC_PER_SEC
;
577 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
581 gboolean fired
= FALSE
;
583 g_get_current_time(&loop
->now
);
585 for (it
= loop
->timers
; it
; it
= next
) {
586 ObMainLoopTimer
*curr
;
588 next
= g_slist_next(it
);
592 /* since timer_stop doesn't actually free the timer, we have to do our
593 real freeing in here.
597 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
602 /* the queue is sorted, so if this timer shouldn't fire, none are
604 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) <= 0)
607 /* we set the last fired time to delay msec after the previous firing,
608 then re-insert. timers maintain their order and may trigger more
609 than once if they've waited more than one delay's worth of time.
611 loop
->timers
= g_slist_delete_link(loop
->timers
, it
);
612 g_time_val_add(&curr
->last
, curr
->delay
);
613 if (curr
->func(curr
->data
)) {
614 g_time_val_add(&curr
->timeout
, curr
->delay
);
615 insert_timer(loop
, curr
);
618 curr
->destroy(curr
->data
);
626 /* if at least one timer fires, then don't wait on X events, as there
627 may already be some in the queue from the timer callbacks.
629 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
630 *wait
= &loop
->ret_wait
;
631 } else if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
632 *wait
= &loop
->ret_wait
;