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
)
533 void ob_main_loop_timeout_remove_data(ObMainLoop
*loop
,
539 for (it
= loop
->timers
; it
; it
= g_slist_next(it
)) {
540 ObMainLoopTimer
*t
= it
->data
;
541 if (t
->func
== handler
&& t
->data
== data
)
546 /* find the time to wait for the nearest timeout */
547 static gboolean
nearest_timeout_wait(ObMainLoop
*loop
, GTimeVal
*tm
)
549 if (loop
->timers
== NULL
)
552 tm
->tv_sec
= NEAREST_TIMEOUT(loop
).tv_sec
- loop
->now
.tv_sec
;
553 tm
->tv_usec
= NEAREST_TIMEOUT(loop
).tv_usec
- loop
->now
.tv_usec
;
555 while (tm
->tv_usec
< 0) {
556 tm
->tv_usec
+= G_USEC_PER_SEC
;
559 tm
->tv_sec
+= tm
->tv_usec
/ G_USEC_PER_SEC
;
560 tm
->tv_usec
%= G_USEC_PER_SEC
;
567 static void timer_dispatch(ObMainLoop
*loop
, GTimeVal
**wait
)
569 g_get_current_time(&loop
->now
);
571 while (loop
->timers
!= NULL
) {
572 ObMainLoopTimer
*curr
= loop
->timers
->data
; /* get the top element */
573 /* since timer_stop doesn't actually free the timer, we have to do our
574 real freeing in here.
578 loop
->timers
= g_slist_delete_link(loop
->timers
, loop
->timers
);
583 /* the queue is sorted, so if this timer shouldn't fire, none are
585 if (timecompare(&NEAREST_TIMEOUT(loop
), &loop
->now
) <= 0)
588 /* we set the last fired time to delay msec after the previous firing,
589 then re-insert. timers maintain their order and may trigger more
590 than once if they've waited more than one delay's worth of time.
592 loop
->timers
= g_slist_delete_link(loop
->timers
, loop
->timers
);
593 g_time_val_add(&curr
->last
, curr
->delay
);
594 if (curr
->func(curr
->data
)) {
595 g_time_val_add(&curr
->timeout
, curr
->delay
);
596 insert_timer(loop
, curr
);
597 } else if (curr
->destroy
) {
598 curr
->destroy(curr
->data
);
601 /* if at least one timer fires, then don't wait on X events, as there
602 may already be some in the queue from the timer callbacks.
604 loop
->ret_wait
.tv_sec
= loop
->ret_wait
.tv_usec
= 0;
605 *wait
= &loop
->ret_wait
;
609 if (nearest_timeout_wait(loop
, &loop
->ret_wait
))
610 *wait
= &loop
->ret_wait
;