5 #include <sys/select.h>
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
;
14 /* this should be more than the number of possible signals on any
16 #define NUM_SIGNALS 99
18 /* all created ObMainLoops. Used by the signal handler to pass along signals */
19 static GSList
*all_loops
;
21 /* signals are global to all loops */
23 guint installed
; /* a ref count */
24 struct sigaction oldact
;
25 } all_signals
[NUM_SIGNALS
];
27 /* a set of all possible signals */
28 sigset_t all_signals_set
;
30 /* signals which cause a core dump, these can't be used for callbacks */
31 static gint core_signals
[] =
44 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
46 static void sighandler(gint sig
);
47 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
);
48 static void fd_handler_destroy(gpointer data
);
54 gboolean run
; /* do keep running */
55 gboolean running
; /* is still running */
59 gint fd_x
; /* The X fd is a special case! */
61 GHashTable
*fd_handlers
;
68 gboolean signal_fired
;
69 guint signals_fired
[NUM_SIGNALS
];
70 GSList
*signal_handlers
[NUM_SIGNALS
];
73 struct _ObMainLoopTimer
78 GDestroyNotify destroy
;
80 /* The timer needs to be freed */
82 /* The time the last fire should've been at */
84 /* When this timer will next trigger */
88 struct _ObMainLoopSignalHandlerType
93 ObMainLoopSignalHandler func
;
94 GDestroyNotify destroy
;
97 struct _ObMainLoopXHandlerType
101 ObMainLoopXHandler func
;
102 GDestroyNotify destroy
;
105 struct _ObMainLoopFdHandlerType
110 ObMainLoopFdHandler func
;
111 GDestroyNotify destroy
;
114 ObMainLoop
*ob_main_loop_new(Display
*display
)
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
;
125 loop
->fd_handlers
= g_hash_table_new_full(g_int_hash
, g_int_equal
,
126 NULL
, fd_handler_destroy
);
128 g_get_current_time(&loop
->now
);
130 /* only do this if we're the first loop created */
133 struct sigaction action
;
136 /* initialize the all_signals_set */
137 sigfillset(&all_signals_set
);
139 sigemptyset(&sigset
);
140 action
.sa_handler
= sighandler
;
141 action
.sa_mask
= sigset
;
142 action
.sa_flags
= SA_NOCLDSTOP
;
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
++;
158 all_loops
= g_slist_prepend(all_loops
, loop
);
163 void ob_main_loop_destroy(ObMainLoop
*loop
)
169 g_assert(loop
->running
== FALSE
);
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
);
177 g_hash_table_destroy(loop
->fd_handlers
);
179 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
180 ObMainLoopTimer
*t
= it
->data
;
181 if (t
->destroy
) t
->destroy(t
->data
);
184 g_slist_free(loop
->timers
);
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
);
194 all_loops
= g_slist_remove(all_loops
, loop
);
196 /* only do this if we're the last loop destroyed */
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
--;
214 static void fd_handle_foreach(gpointer key
,
218 ObMainLoopFdHandlerType
*h
= value
;
221 if (FD_ISSET(h
->fd
, set
))
222 h
->func(h
->fd
, h
->data
);
225 void ob_main_loop_run(ObMainLoop
*loop
)
228 struct timeval
*wait
;
233 loop
->running
= TRUE
;
236 if (loop
->signal_fired
) {
240 /* block signals so that we can do this without the data changing
242 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
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
;
251 loop
->signals_fired
[i
]--;
254 loop
->signal_fired
= FALSE
;
256 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
257 } else if (XPending(loop
->display
)) {
259 XNextEvent(loop
->display
, &e
);
261 for (it
= loop
->x_handlers
; it
; it
= g_slist_next(it
)) {
262 ObMainLoopXHandlerType
*h
= it
->data
;
263 h
->func(&e
, h
->data
);
265 } while (XPending(loop
->display
));
267 /* this only runs if there were no x events received */
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'
276 if (!loop
->signal_fired
)
277 select(loop
->fd_max
+ 1, &selset
, NULL
, NULL
, wait
);
279 /* handle the X events with highest prioirity */
280 if (FD_ISSET(loop
->fd_x
, &selset
))
283 g_hash_table_foreach(loop
->fd_handlers
,
284 fd_handle_foreach
, &selset
);
288 loop
->running
= FALSE
;
291 void ob_main_loop_exit(ObMainLoop
*loop
)
296 /*** XEVENT WATCHERS ***/
298 void ob_main_loop_x_add(ObMainLoop
*loop
,
299 ObMainLoopXHandler handler
,
301 GDestroyNotify notify
)
303 ObMainLoopXHandlerType
*h
;
305 h
= g_new(ObMainLoopXHandlerType
, 1);
310 loop
->x_handlers
= g_slist_prepend(loop
->x_handlers
, h
);
313 void ob_main_loop_x_remove(ObMainLoop
*loop
,
314 ObMainLoopXHandler handler
)
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
);
329 /*** SIGNAL WATCHERS ***/
331 static void sighandler(gint sig
)
336 g_return_if_fail(sig
< NUM_SIGNALS
);
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... */
343 fprintf(stderr
, "Fuck yah. Core dump. (Signal=%d)\n", sig
);
345 /* die with a core dump */
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
]++;
356 void ob_main_loop_signal_add(ObMainLoop
*loop
,
358 ObMainLoopSignalHandler handler
,
360 GDestroyNotify notify
)
362 ObMainLoopSignalHandlerType
*h
;
364 g_return_if_fail(signal
< NUM_SIGNALS
);
366 h
= g_new(ObMainLoopSignalHandlerType
, 1);
372 loop
->signal_handlers
[h
->signal
] =
373 g_slist_prepend(loop
->signal_handlers
[h
->signal
], h
);
375 if (!all_signals
[signal
].installed
) {
376 struct sigaction action
;
379 sigemptyset(&sigset
);
380 action
.sa_handler
= sighandler
;
381 action
.sa_mask
= sigset
;
382 action
.sa_flags
= SA_NOCLDSTOP
;
384 sigaction(signal
, &action
, &all_signals
[signal
].oldact
);
387 all_signals
[signal
].installed
++;
390 void ob_main_loop_signal_remove(ObMainLoop
*loop
,
391 ObMainLoopSignalHandler handler
)
396 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
397 for (it
= loop
->signal_handlers
[i
]; it
; it
= next
) {
398 ObMainLoopSignalHandlerType
*h
= it
->data
;
400 next
= g_slist_next(it
);
402 if (h
->func
== handler
) {
403 g_assert(all_signals
[h
->signal
].installed
> 0);
405 all_signals
[h
->signal
].installed
--;
406 if (!all_signals
[h
->signal
].installed
) {
407 sigaction(h
->signal
, &all_signals
[h
->signal
].oldact
, NULL
);
410 loop
->signal_handlers
[i
] =
411 g_slist_delete_link(loop
->signal_handlers
[i
], it
);
412 if (h
->destroy
) h
->destroy(h
->data
);
421 /*** FILE DESCRIPTOR WATCHERS ***/
423 static void max_fd_func(gpointer key
, gpointer value
, gpointer data
)
425 ObMainLoop
*loop
= data
;
428 loop
->fd_max
= MAX(loop
->fd_max
, *(gint
*)key
);
431 static void calc_max_fd(ObMainLoop
*loop
)
433 loop
->fd_max
= loop
->fd_x
;
435 g_hash_table_foreach(loop
->fd_handlers
, max_fd_func
, loop
);
438 void ob_main_loop_fd_add(ObMainLoop
*loop
,
440 ObMainLoopFdHandler handler
,
442 GDestroyNotify notify
)
444 ObMainLoopFdHandlerType
*h
;
446 h
= g_new(ObMainLoopFdHandlerType
, 1);
453 g_hash_table_replace(loop
->fd_handlers
, &h
->fd
, h
);
454 FD_SET(h
->fd
, &loop
->fd_set
);
458 static void fd_handler_destroy(gpointer data
)
460 ObMainLoopFdHandlerType
*h
= data
;
462 FD_CLR(h
->fd
, &h
->loop
->fd_set
);
468 void ob_main_loop_fd_remove(ObMainLoop
*loop
,
471 g_hash_table_remove(loop
->fd_handlers
, &fd
);
476 #define NEAREST_TIMEOUT(loop) \
477 (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
479 static long timecompare(GTimeVal
*a
, GTimeVal
*b
)
483 if ((r
= b
->tv_sec
- a
->tv_sec
)) return r
;
484 return b
->tv_usec
- a
->tv_usec
;
488 static void insert_timer(ObMainLoop
*loop
, ObMainLoopTimer
*ins
)
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
);
498 if (it
== NULL
) /* didnt fit anywhere in the list */
499 loop
->timers
= g_slist_append(loop
->timers
, ins
);
502 void ob_main_loop_timeout_add(ObMainLoop
*loop
,
506 GDestroyNotify notify
)
508 ObMainLoopTimer
*t
= g_new(ObMainLoopTimer
, 1);
509 t
->delay
= microseconds
;
514 g_get_current_time(&loop
->now
);
515 t
->last
= t
->timeout
= loop
->now
;
516 g_time_val_add(&t
->timeout
, t
->delay
);
518 insert_timer(loop
, t
);
521 void ob_main_loop_timeout_remove(ObMainLoop
*loop
,
526 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
527 ObMainLoopTimer
*t
= it
->data
;
528 if (t
->func
== handler
) {
535 /* find the time to wait for the nearest timeout */
536 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
538 if (loop
->timers
== NULL
)
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
;
544 while (tm
->tv_usec
< 0) {
545 tm
->tv_usec
+= G_USEC_PER_SEC
;
548 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
549 tm
->tv_usec
%= G_USEC_PER_SEC
;
556 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
558 g_get_current_time(&loop
->now
);
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.
567 loop
->timers
= g_slist_delete_link(loop
->timers
, loop
->timers
);
572 /* the queue is sorted, so if this timer shouldn't fire, none are
574 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) <= 0)
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.
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
);
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.
593 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
594 *wait
= &loop
->ret_wait
;
598 if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
599 *wait
= &loop
->ret_wait
;