1 #include "gskhook.h"
2 #include "gsktree.h"
3 #include "gskmainloop.h"
4 #include "gskdebug.h"
5 #include "debug.h"
6
7 /* privately used flags */
8 enum
9 {
10 GSK_HOOK_HAS_POLL = (1 << 8),
11 GSK_HOOK_IS_NOTIFYING = (1 << 9),
12 GSK_HOOK_IS_NOTIFYING_SHUTDOWN = (1 << 10),
13 GSK_HOOK_BLOCKED_NOTIFY = (1 << 11),
14 GSK_HOOK_BLOCKED_SHUTDOWN_NOTIFY = (1 << 12),
15 GSK_HOOK_UNTRAPPED_DURING_NOTIFY = (1 << 13),
16
17 /* aliases */
18 GSK_HOOK_IDLE_NOTIFY = GSK_HOOK_private_IDLE_NOTIFY,
19 GSK_HOOK_JUST_NEVER_BLOCKS = GSK_HOOK_private_JUST_NEVER_BLOCKS,
20 GSK_HOOK_NEVER_BLOCKS = GSK_HOOK_private_NEVER_BLOCKS,
21 GSK_HOOK_CAN_DEFER_SHUTDOWN = GSK_HOOK_private_CAN_DEFER_SHUTDOWN,
22 GSK_HOOK_SHUTTING_DOWN = GSK_HOOK_private_SHUTTING_DOWN
23 };
24
25 static GQuark gsk_hook_main_loop_quark = 0;
26
27 #ifdef GSK_DEBUG
28 static void
29 gsk_hook_debug (GskHook *hook,
30 const char *format,
31 ...) G_GNUC_PRINTF(2,3);
32 #define DEBUG(args) \
33 G_STMT_START{ \
34 if (GSK_IS_DEBUGGING (HOOK)) \
35 gsk_hook_debug args; \
36 }G_STMT_END
37 #else
38 #define DEBUG(args)
39 #endif
40
41 extern GskDebugFlags gsk_debug_flags;
42
43 /* --- implementation of non-blocking hooks --- */
44 typedef struct _PendingDestroyNotify PendingDestroyNotify;
45 struct _PendingDestroyNotify
46 {
47 /* WARNING: this structure must be no more than 3 pointers to work! */
48 gpointer data;
49 GDestroyNotify destroy;
50 PendingDestroyNotify *next;
51 };
52 /* abuse GList allocator */
53 #define pending_destroy_notify_alloc() (PendingDestroyNotify*)g_list_alloc()
54 #define pending_destroy_notify_free(notify) g_list_free_1((GList*)notify)
55
56 typedef struct
57 {
58 GskTree *hook_tree;
59 GskSource *source;
60 PendingDestroyNotify *first_destroy;
61 PendingDestroyNotify *last_destroy;
62 } NBThreadData;
63
64 static void
flush_pending_destroys(NBThreadData * nb_data)65 flush_pending_destroys (NBThreadData *nb_data)
66 {
67 while (nb_data->first_destroy)
68 {
69 PendingDestroyNotify *at = nb_data->first_destroy;
70 nb_data->first_destroy = at->next;
71 if (nb_data->first_destroy == NULL)
72 nb_data->last_destroy = NULL;
73 at->destroy (at->data);
74 pending_destroy_notify_free (at);
75 }
76 }
77
78 static gboolean
run_all_nonblocking_hooks(gpointer data)79 run_all_nonblocking_hooks (gpointer data)
80 {
81 NBThreadData *nb_data = data;
82 GskTree *tree = nb_data->hook_tree;
83 GskTreeNode *node;
84
85 flush_pending_destroys (nb_data);
86
87 node = gsk_tree_node_first (tree);
88 if (GSK_IS_DEBUGGING (HOOK))
89 g_message ("run_all_nonblocking_hooks: %d hooks, first=%p", gsk_tree_n_nodes (tree), node);
90
91 /* is the nonblocking thread totally inactive? if so, remove it. */
92 if (node == NULL)
93 {
94 nb_data->source = NULL;
95 return FALSE;
96 }
97 do
98 {
99 GskHook *hook = gsk_tree_node_peek_key (node);
100 DEBUG((hook, "about to run notify, since this hook is nonblocking"));
101 gsk_hook_notify (hook);
102 node = gsk_tree_node_next (tree, node);
103 }
104 while (node != NULL);
105
106 flush_pending_destroys (nb_data);
107 if (GSK_IS_DEBUGGING (HOOK))
108 g_message ("done -- run_all_nonblocking_hooks");
109 return TRUE;
110 }
111
112 static void
destroy_nonblocking_thread_data(gpointer data)113 destroy_nonblocking_thread_data (gpointer data)
114 {
115 NBThreadData *nb_data = data;
116 /* ignore source -- it's going away with the main-loop */
117 gsk_tree_unref (nb_data->hook_tree);
118 g_free (nb_data);
119 }
120
121 static gint
pointer_compare(gconstpointer a,gconstpointer b)122 pointer_compare (gconstpointer a, gconstpointer b)
123 {
124 const char *pa = a;
125 const char *pb = b;
126 return (pa < pb) ? -1 : (pa > pb) ? +1 : 0;
127 }
128
129 static inline NBThreadData *
main_loop_force_nonblocking_data(GskMainLoop * loop)130 main_loop_force_nonblocking_data (GskMainLoop *loop)
131 {
132 NBThreadData *data = g_object_get_qdata (G_OBJECT (loop), gsk_hook_main_loop_quark);
133 if (data == NULL)
134 {
135 data = g_new (NBThreadData, 1);
136 data->hook_tree = gsk_tree_new (pointer_compare);
137 data->source = NULL;
138 data->first_destroy = data->last_destroy = NULL;
139 g_object_set_qdata_full (G_OBJECT (loop), gsk_hook_main_loop_quark, data,
140 destroy_nonblocking_thread_data);
141 }
142 return data;
143 }
144
145 static inline void
verify_nonblocking_data_has_source(NBThreadData * nb_data,GskMainLoop * main_loop)146 verify_nonblocking_data_has_source (NBThreadData *nb_data,
147 GskMainLoop *main_loop)
148 {
149 if (nb_data->source == NULL)
150 nb_data->source = gsk_main_loop_add_idle (main_loop,
151 run_all_nonblocking_hooks,
152 nb_data, NULL);
153 }
154
155 static inline void
gsk_hook_add_to_nonblocking_list(GskHook * hook)156 gsk_hook_add_to_nonblocking_list (GskHook *hook)
157 {
158 GskMainLoop *loop = gsk_main_loop_default ();
159 NBThreadData *data = main_loop_force_nonblocking_data (loop);
160 gsk_tree_insert (data->hook_tree, hook, hook);
161 verify_nonblocking_data_has_source (data, loop);
162 }
163
164 static inline void
gsk_hook_remove_from_nonblocking_list(GskHook * hook)165 gsk_hook_remove_from_nonblocking_list (GskHook *hook)
166 {
167 GskMainLoop *loop = gsk_main_loop_default ();
168 NBThreadData *data = main_loop_force_nonblocking_data (loop);
169 gsk_tree_remove (data->hook_tree, hook);
170
171 /* note: to avoid deleting and re-adding the idle function repeatedly
172 * in certain cases, we always do the removal by returning FALSE
173 * from the run_all_nonblocking_hooks
174 */
175 }
176
177 static inline void
gsk_hook_maybe_defer_destroy(GDestroyNotify destroy,gpointer destroy_data)178 gsk_hook_maybe_defer_destroy (GDestroyNotify destroy, gpointer destroy_data)
179 {
180 if (destroy)
181 {
182 GskMainLoop *loop = gsk_main_loop_default ();
183 NBThreadData *data = main_loop_force_nonblocking_data (loop);
184 PendingDestroyNotify *notify = pending_destroy_notify_alloc ();
185 verify_nonblocking_data_has_source (data, loop);
186 notify->destroy = destroy;
187 notify->data = destroy_data;
188 notify->next = NULL;
189 if (data->last_destroy)
190 data->last_destroy->next = notify;
191 else
192 data->first_destroy = notify;
193 data->last_destroy = notify;
194 }
195 }
196
197 /* --- public interface --- */
198
199 /**
200 * gsk_hook_init:
201 * @hook: The hook to initialize.
202 * @flags: Special details about the hook.
203 * @inset: Offset in bytes of this hook inside the containing object.
204 * @class_set_poll_offset: Offset in bytes of the set_poll
205 * method in the containing object's class.
206 * @class_shutdown_offset: Offset in bytes of the shutdown
207 * method in the containing object's class, or 0 if there is no shutdown
208 * method.
209 *
210 * Prepare a GskHook to be used. This should almost always
211 * be done in the instance-init function of the class which contains the hook.
212 */
213 void
gsk_hook_init(GskHook * hook,GskHookFlags flags,guint inset,guint class_set_poll_offset,guint class_shutdown_offset)214 gsk_hook_init (GskHook *hook,
215 GskHookFlags flags,
216 guint inset,
217 guint class_set_poll_offset,
218 guint class_shutdown_offset)
219 {
220 /* currently these are stored in guint16's... */
221 g_return_if_fail (inset < 65536
222 && class_shutdown_offset < 65536
223 && class_set_poll_offset < 65536);
224
225 hook->flags = flags & ~_GSK_HOOK_FLAGS_RESERVED;
226 hook->block_count = 0;
227 hook->inset = inset;
228 hook->class_set_poll_offset = class_set_poll_offset;
229 hook->class_shutdown_offset = class_shutdown_offset;
230 hook->func = NULL;
231 hook->shutdown_func = NULL;
232 hook->data = NULL;
233 hook->destroy = NULL;
234 }
235
236 static inline void
gsk_hook_call_set_poll_func(GskHook * hook,gboolean value)237 gsk_hook_call_set_poll_func (GskHook *hook,
238 gboolean value)
239 {
240 GObject *object = GSK_HOOK_GET_OBJECT (hook);
241 GObjectClass *class = G_OBJECT_GET_CLASS (object);
242 GskHookSetPollFunc set_poll_func =
243 G_STRUCT_MEMBER (GskHookSetPollFunc, class, hook->class_set_poll_offset);
244 if (set_poll_func)
245 (*set_poll_func) (object, value);
246 }
247
248 static inline gboolean
gsk_hook_call_shutdown_func(GskHook * hook,GError ** error)249 gsk_hook_call_shutdown_func (GskHook *hook,
250 GError **error)
251 {
252 GObject *object = GSK_HOOK_GET_OBJECT (hook);
253 GObjectClass *class = G_OBJECT_GET_CLASS (object);
254 guint offset = hook->class_shutdown_offset;
255 GHookFunc shutdown_func =
256 (GHookFunc) G_STRUCT_MEMBER (GHookFunc, class, offset);
257 if (!shutdown_func)
258 return TRUE;
259 if (GSK_HOOK_TEST_FLAG (hook, CAN_HAVE_SHUTDOWN_ERROR))
260 return (* (GskHookShutdownErrorFunc) shutdown_func) (object, error);
261 else
262 {
263 (* (GskHookShutdownFunc) shutdown_func) (object);
264 return TRUE;
265 }
266 }
267
268 static inline void
gsk_hook_set_poll(GskHook * hook)269 gsk_hook_set_poll (GskHook *hook)
270 {
271 DEBUG ((hook, "gsk_hook_set_poll: idle-notify=%d",
272 GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY)));
273
274 GSK_HOOK_SET_FLAG (hook, HAS_POLL);
275 if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
276 gsk_hook_add_to_nonblocking_list (hook);
277 gsk_hook_call_set_poll_func (hook, TRUE);
278 }
279
280 static inline void
gsk_hook_clear_poll(GskHook * hook)281 gsk_hook_clear_poll (GskHook *hook)
282 {
283 DEBUG ((hook, "gsk_hook_clear_poll: idle-notify=%d",
284 GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY)));
285
286 GSK_HOOK_CLEAR_FLAG (hook, HAS_POLL);
287 if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
288 gsk_hook_remove_from_nonblocking_list (hook);
289 gsk_hook_call_set_poll_func (hook, FALSE);
290 }
291
292 /**
293 * gsk_hook_trap:
294 * @hook: the hook to trap
295 * @func: function to call with the hook is triggered.
296 * @shutdown: function to be called if the hook is shutting down and will
297 * never trigger again.
298 * @data: user-data to pass to func and shutdown.
299 * @destroy: function to call on user-data when the hook is untrapped.
300 *
301 * Traps hook triggers and shutdowns. The caller's functions will
302 * be run in response to #gsk_hook_notify.
303 */
304 void
gsk_hook_trap(GskHook * hook,GskHookFunc func,GskHookFunc shutdown,gpointer data,GDestroyNotify destroy)305 gsk_hook_trap (GskHook *hook,
306 GskHookFunc func,
307 GskHookFunc shutdown,
308 gpointer data,
309 GDestroyNotify destroy)
310 {
311 g_return_if_fail (hook->func == NULL);
312 g_return_if_fail (GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE));
313 DEBUG ((hook, "gsk_hook_trap"));
314 hook->func = func;
315 hook->shutdown_func = shutdown;
316 hook->data = data;
317 hook->destroy = destroy;
318 if (hook->block_count == 0
319 && !GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
320 gsk_hook_set_poll (hook);
321 }
322
323 /**
324 * gsk_hook_untrap:
325 * @hook: the hook to untrap
326 *
327 * Stops trapping the hook. The user's function will
328 * no longer be run, and invoke the user's destroy method,
329 * if it was supplied to #gsk_hook_trap.
330 *
331 * If you untrap the hook while it is executing,
332 * the destroy handler will be deferred
333 * until after the hook is done notifying and/or shutdown-notifying.
334 */
335 void
gsk_hook_untrap(GskHook * hook)336 gsk_hook_untrap (GskHook *hook)
337 {
338 GDestroyNotify old_destroy = hook->destroy;
339 gpointer old_data = hook->data;
340
341 DEBUG ((hook, "gsk_hook_untrap"));
342
343 hook->func = NULL;
344 hook->shutdown_func = NULL;
345 hook->data = NULL;
346 hook->destroy = NULL;
347
348 if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
349 gsk_hook_clear_poll (hook);
350
351 /* If a hook is untrapped while one of the user's callbacks is running,
352 gsk_hook_notify and gsk_hook_notify_shutdown need to ignore the
353 callback's return value. */
354 if (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING)
355 || GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN))
356 {
357 GSK_HOOK_SET_FLAG (hook, UNTRAPPED_DURING_NOTIFY);
358 gsk_hook_maybe_defer_destroy (old_destroy, old_data);
359 }
360 else if (old_destroy)
361 old_destroy (old_data);
362 }
363
364 /**
365 * gsk_hook_block:
366 * @hook: the hook to block.
367 *
368 * Temporarily block the hook from triggerring the callback.
369 * This may cause the set_poll method to be run.
370 *
371 * This increases a block_count, so you may call
372 * gsk_hook_block as many times as you like,
373 * but you must call #gsk_hook_unblock an equal number of times.
374 */
375 void
gsk_hook_block(GskHook * hook)376 gsk_hook_block (GskHook *hook)
377 {
378 hook->block_count++;
379 DEBUG ((hook, "gsk_hook_block: new-count=%d", hook->block_count));
380 if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
381 gsk_hook_clear_poll (hook);
382 }
383
384 /**
385 * gsk_hook_unblock:
386 * @hook: the hook to unblock.
387 *
388 * Undoes a gsk_hook_block(). The block count is decreased,
389 * and if it gets to 0, the hook may be eligible to start
390 * triggering again.
391 */
392 void
gsk_hook_unblock(GskHook * hook)393 gsk_hook_unblock (GskHook *hook)
394 {
395 g_return_if_fail (hook->block_count > 0);
396 hook->block_count--;
397 DEBUG ((hook, "gsk_hook_unblock: new-count=%d", hook->block_count));
398 if (hook->block_count == 0
399 && GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE)
400 && hook->func != NULL)
401 gsk_hook_set_poll (hook);
402 }
403
404 /**
405 * gsk_hook_shutdown:
406 * @hook: the hook to shut down.
407 * @error: optional error return.
408 *
409 * Shutdown a hook. You may call gsk_hook_shutdown on a hook more than
410 * once, but it will be ignored after the first time.
411 *
412 * Note that when gsk_hook_shutdown succeeds, the hook is not necessarily
413 * completely shut down. Some hooks (for example, for a socket waiting
414 * for a reply from a remote server) may remain in the SHUTTING_DOWN state
415 * for some time before the shutdown completes and the user's shutdown
416 * callback is invoked. You may test whether the shutdown has completed
417 * using GSK_HOOK_TEST_SHUTTING_DOWN (hook): if true, the shutdown is still
418 * in progress.
419 *
420 * Regardless of whether the shutdown succeeds, or whether the shutdown
421 * completes immediately, the hook will be marked unavailable after
422 * calling gsk_hook_shutdown. (That is, GSK_HOOK_TEST_IS_AVAILABLE (hook)
423 * will be false.)
424 *
425 * returns: true if successful, or false if an error occurred.
426 */
427 gboolean
gsk_hook_shutdown(GskHook * hook,GError ** error)428 gsk_hook_shutdown (GskHook *hook,
429 GError **error)
430 {
431 GObject *object = GSK_HOOK_GET_OBJECT (hook);
432 GError *dummy = NULL;
433 GError **err = error ? error : &dummy;
434 gboolean success, do_notify;
435
436 DEBUG ((hook, "gsk_hook_shutdown: is-available-initially=%d",
437 GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE)));
438
439 if (!GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE))
440 return TRUE;
441
442 if (GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN))
443 return TRUE;
444
445 g_object_ref (object);
446 GSK_HOOK_SET_FLAG (hook, SHUTTING_DOWN);
447 if (GSK_HOOK_TEST_FLAG (hook, CAN_DEFER_SHUTDOWN))
448 {
449 /* If the hook CAN_DEFER_SHUTDOWN, false indicates that the
450 shutdown was deferred, whether or not an error occurred. */
451 if (gsk_hook_call_shutdown_func (hook, err))
452 do_notify = TRUE;
453 else
454 {
455 do_notify = FALSE;
456 /* The class method should not have cleared this, since it
457 wanted to defer shutdown. */
458 g_return_val_if_fail (GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN),
459 FALSE);
460 }
461 success = (*err == NULL);
462 }
463 else
464 {
465 /* If the hook can't defer shutdown, false indicates that
466 an error occurred. */
467 if (gsk_hook_call_shutdown_func (hook, err))
468 success = (*err == NULL); /* TODO: ??? */
469 else
470 success = FALSE;
471
472 do_notify = TRUE;
473 }
474 if (do_notify)
475 {
476 gsk_hook_notify_shutdown (hook);
477
478 /* We should be completely shut down, unless we were already
479 notifying and shutdown notification was blocked. */
480 g_return_val_if_fail
481 (!GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN)
482 || (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING)
483 && GSK_HOOK_TEST_FLAG (hook, BLOCKED_SHUTDOWN_NOTIFY)),
484 FALSE);
485 }
486 /* We always clear this here, in case shutdown notification was blocked. */
487 GSK_HOOK_CLEAR_FLAG (hook, IS_AVAILABLE);
488
489 if (dummy)
490 g_error_free (dummy);
491
492 g_object_unref (object);
493 return success;
494 }
495
496 /* for use by classes which contain hooks */
497 /**
498 * gsk_hook_notify:
499 * @hook: the hook which is triggering.
500 *
501 * This is called by the implementation of a class
502 * which contains a hook to trigger it, i.e. call the user's
503 * callback.
504 *
505 * Generally, you should only call this if the user is ready
506 * for data (so that set_poll has been called with TRUE);
507 * however, if you call it, it will be remembered,
508 * and immediately triggered once it is allowed to:
509 * for example, when the block count gets to 0.
510 *
511 * Some gory details come up now and then:
512 *
513 * - there are reentrance guards which prevent recursive calls
514 * to do anything. Also, notify within shutdown is not allowed.
515 */
516 void
gsk_hook_notify(GskHook * hook)517 gsk_hook_notify (GskHook *hook)
518 {
519 GObject *object;
520 gboolean handler_rv;
521
522 g_return_if_fail (GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE));
523
524 DEBUG ((hook, "gsk_hook_notify: block-cnt=%d; notifying=%d, notifying-shutdown=%d, shutting-down=%d",
525 hook->block_count,
526 GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING),
527 GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN),
528 GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN)));
529
530 if (hook->block_count > 0
531 || GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING)
532 || GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN))
533 {
534 GSK_HOOK_SET_FLAG (hook, BLOCKED_NOTIFY);
535 return;
536 }
537
538 GSK_HOOK_CLEAR_FLAG (hook, BLOCKED_NOTIFY);
539
540 object = GSK_HOOK_GET_OBJECT (hook);
541 g_object_ref (object);
542
543 begin_notify:
544 if (hook->func == NULL)
545 {
546 goto done_notify;
547 }
548
549 GSK_HOOK_SET_FLAG (hook, IS_NOTIFYING);
550 handler_rv = hook->func (object, hook->data);
551 GSK_HOOK_CLEAR_FLAG (hook, IS_NOTIFYING);
552
553 if (!handler_rv
554 && !GSK_HOOK_TEST_FLAG (hook, UNTRAPPED_DURING_NOTIFY))
555 {
556 gsk_hook_untrap (hook);
557 }
558 GSK_HOOK_CLEAR_FLAG (hook, UNTRAPPED_DURING_NOTIFY);
559
560 if (GSK_HOOK_TEST_FLAG (hook, BLOCKED_SHUTDOWN_NOTIFY))
561 goto got_shutdown_notify;
562 if (GSK_HOOK_TEST_FLAG (hook, BLOCKED_NOTIFY))
563 {
564 /* restart */
565 GSK_HOOK_CLEAR_FLAG (hook, BLOCKED_NOTIFY);
566 goto begin_notify;
567 }
568
569 done_notify:
570 g_object_unref (object);
571 return;
572
573 got_shutdown_notify:
574 gsk_hook_notify_shutdown (hook);
575 g_object_unref (object);
576 return;
577 }
578
579 static inline void
_gsk_hook_clear_idle_notify(GskHook * hook)580 _gsk_hook_clear_idle_notify (GskHook *hook)
581 {
582 GSK_HOOK_CLEAR_FLAG (hook, IDLE_NOTIFY);
583 if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
584 gsk_hook_remove_from_nonblocking_list (hook);
585 }
586
587 /**
588 * gsk_hook_notify_shutdown:
589 * @hook: the hook which has shut down.
590 *
591 * Notify the user that a shutdown event has occurred.
592 * This may happen because you called gsk_hook_shutdown()
593 * or because the hook was shut down by the object:
594 * for example, the remote side of a connection may have shutdown.
595 *
596 * This does nothing if the hook is not presently available or in the
597 * middle of shutting down (i.e., IS_AVAILABLE || SHUTTING_DOWN); after
598 * this call the hook will be marked both unavailable and completely
599 * shut down (i.e., !IS_AVAILABLE && !SHUTTING_DOWN).
600 *
601 * A shutdown hook will no longer be idle-notified.
602 */
603 void
gsk_hook_notify_shutdown(GskHook * hook)604 gsk_hook_notify_shutdown (GskHook *hook)
605 {
606 DEBUG ((hook, "gsk_hook_notify_shutdown: available=%d, shutting-down=%d, notifying=%d, notifying-shutdown=%d",
607 GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE),
608 GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN),
609 GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING),
610 GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN)));
611
612 /* Notify-shutdown within notify-shutdown is not allowed. */
613 if (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING_SHUTDOWN))
614 return;
615
616 /* If the hook is neither available nor shutting down, either the user has
617 already been notified, or the hook was never available to begin with. */
618 if (!GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE)
619 && !GSK_HOOK_TEST_FLAG (hook, SHUTTING_DOWN))
620 return;
621
622 /* Notify-shutdown within notify will be deferred until the notification
623 is complete. */
624 if (GSK_HOOK_TEST_FLAG (hook, IS_NOTIFYING))
625 {
626 GSK_HOOK_SET_FLAG (hook, BLOCKED_SHUTDOWN_NOTIFY);
627 return;
628 }
629
630 /* The hook is now completely shut down. */
631 if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
632 _gsk_hook_clear_idle_notify (hook);
633 GSK_HOOK_CLEAR_FLAG (hook, IS_AVAILABLE);
634 GSK_HOOK_CLEAR_FLAG (hook, SHUTTING_DOWN);
635
636 /* Notify the user that the hook is completely shut down. */
637 if (hook->shutdown_func == NULL)
638 {
639 if (gsk_hook_is_trapped (hook))
640 gsk_hook_untrap (hook);
641 }
642 else
643 {
644 GObject *object = GSK_HOOK_GET_OBJECT (hook);
645 gboolean handler_rv;
646 g_object_ref (object);
647
648 GSK_HOOK_SET_FLAG (hook, IS_NOTIFYING_SHUTDOWN);
649 handler_rv = (*hook->shutdown_func) (object, hook->data);
650 GSK_HOOK_CLEAR_FLAG (hook, IS_NOTIFYING_SHUTDOWN);
651
652 if (!handler_rv
653 && !GSK_HOOK_TEST_FLAG (hook, UNTRAPPED_DURING_NOTIFY))
654 {
655 gsk_hook_untrap (hook);
656 }
657 GSK_HOOK_CLEAR_FLAG (hook, UNTRAPPED_DURING_NOTIFY);
658 g_object_unref (object);
659 }
660 }
661
662 /* an alternative to implementing set_poll */
663
664 /**
665 * gsk_hook_set_idle_notify:
666 * @hook: the hook to run constantly.
667 * @should_idle_notify: whether to run the user's callback
668 * continually.
669 *
670 * When idle_notify is set, the hook will run every cycle of the
671 * main-loop. Nothing will happen unless the user
672 * has trapped the event, and it's not blocked.
673 *
674 * Opposite of gsk_hook_clear_idle_notify().
675 */
676 void
gsk_hook_set_idle_notify(GskHook * hook,gboolean should_idle_notify)677 gsk_hook_set_idle_notify (GskHook *hook,
678 gboolean should_idle_notify)
679 {
680 /* TODO: optimize */
681 if (should_idle_notify)
682 gsk_hook_mark_idle_notify (hook);
683 else
684 gsk_hook_clear_idle_notify (hook);
685 }
686
687 /**
688 * gsk_hook_mark_idle_notify:
689 * @hook: the hook to run constantly.
690 *
691 * When idle_notify is set, the hook will run every cycle of the
692 * main-loop. Of course, nothing will happen unless the user
693 * has trapped the event, and it's not blocked.
694 *
695 * Opposite of gsk_hook_clear_idle_notify().
696 */
697 void
gsk_hook_mark_idle_notify(GskHook * hook)698 gsk_hook_mark_idle_notify (GskHook *hook)
699 {
700 g_return_if_fail (!GSK_HOOK_TEST_FLAG (hook, JUST_NEVER_BLOCKS));
701 if (!GSK_HOOK_TEST_FLAG (hook, IS_AVAILABLE))
702 return;
703 if (GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
704 return;
705
706 DEBUG ((hook, "gsk_hook_mark_idle_notify"));
707
708 GSK_HOOK_SET_FLAG (hook, IDLE_NOTIFY);
709 if (GSK_HOOK_TEST_FLAG (hook, HAS_POLL))
710 gsk_hook_add_to_nonblocking_list (hook);
711 }
712
713 /**
714 * gsk_hook_clear_idle_notify:
715 * @hook: the hook to stop running constantly.
716 *
717 * Stop running the hook at every cycle of the main-loop.
718 *
719 * Opposite of gsk_hook_set_idle_notify().
720 */
721 void
gsk_hook_clear_idle_notify(GskHook * hook)722 gsk_hook_clear_idle_notify (GskHook *hook)
723 {
724 g_return_if_fail (!GSK_HOOK_TEST_FLAG (hook, JUST_NEVER_BLOCKS));
725 if (!GSK_HOOK_TEST_FLAG (hook, IDLE_NOTIFY))
726 return;
727 DEBUG ((hook, "gsk_hook_clear_idle_notify"));
728 _gsk_hook_clear_idle_notify (hook);
729 }
730
731 /**
732 * gsk_hook_mark_never_blocks:
733 * @hook: the hook which will never block.
734 *
735 * Indicate that you will never block, ever.
736 */
737
738 void
gsk_hook_mark_never_blocks(GskHook * hook)739 gsk_hook_mark_never_blocks (GskHook *hook)
740 {
741 gsk_hook_mark_idle_notify (hook);
742 GSK_HOOK_SET_FLAG (hook, JUST_NEVER_BLOCKS);
743 }
744
745 /**
746 * gsk_hook_mark_can_defer_shutdown:
747 * @hook: the hook which can defer shutdown.
748 *
749 * Indicate that the hook may remain in the SHUTTING_DOWN state after the
750 * class's shutdown method is called, deferring shutdown notification until
751 * a later time.
752 *
753 * If you set this flag, your class's shutdown method must return FALSE
754 * if shutdown notification is deferred, or TRUE if shutdown is complete
755 * and the user should be notified immediately; failure is indicated only
756 * by setting the GError parameter to the shutdown method. (Therefore,
757 * setting this flag has no effect unless you also set SHUTDOWN_HAS_ERROR.)
758 */
759 void
gsk_hook_mark_can_defer_shutdown(GskHook * hook)760 gsk_hook_mark_can_defer_shutdown (GskHook *hook)
761 {
762 GSK_HOOK_SET_FLAG (hook, CAN_DEFER_SHUTDOWN);
763 }
764
765 /**
766 * gsk_hook_destruct:
767 * @hook: the hook to destroy.
768 *
769 * This should be called only by the class which contains the hook,
770 * from the instance's finalize method.
771 */
772 void
gsk_hook_destruct(GskHook * hook)773 gsk_hook_destruct (GskHook *hook)
774 {
775 if (hook->flags & GSK_HOOK_HAS_POLL)
776 gsk_hook_clear_poll (hook);
777 if (hook->destroy)
778 hook->destroy (hook->data);
779 }
780
781 /* per-class initialization */
782 typedef struct {
783 GType type;
784 const char *name;
785 } PerHookTypeInfo;
786 typedef struct {
787 guint num_info;
788 PerHookTypeInfo type_infos[1]; /* flexible array */
789 } PerOffsetInfo;
790
791 static GPtrArray *per_offset = NULL;
792
793 /**
794 * gsk_hook_class_init:
795 * @object_class: the class of the object which contains this hook.
796 * @name: an identifying name for this type of hook.
797 * @hook_offset: the offset of the hook in the structure.
798 *
799 * This is used to register a hook. This is mostly used
800 * to make debugging printouts easier to read.
801 */
802 void
gsk_hook_class_init(GObjectClass * object_class,const char * name,guint hook_offset)803 gsk_hook_class_init (GObjectClass *object_class,
804 const char *name,
805 guint hook_offset)
806 {
807 guint index;
808 PerOffsetInfo *oi;
809 g_assert (hook_offset % 4 == 0);
810 g_assert (hook_offset >= sizeof (GObject));
811 index = (hook_offset - sizeof (GObject)) / 4;
812 if (per_offset->len <= index)
813 g_ptr_array_set_size (per_offset, index + 1);
814 oi = per_offset->pdata[index];
815 if (!oi)
816 {
817 oi = g_new (PerOffsetInfo, 1);
818 oi->num_info = 0;
819 }
820 else
821 {
822 guint size = sizeof (PerOffsetInfo)
823 + sizeof (PerHookTypeInfo) * (oi->num_info);
824 oi = g_realloc (oi, size);
825 }
826 per_offset->pdata[index] = oi;
827 oi->type_infos[oi->num_info].type = G_OBJECT_CLASS_TYPE (object_class);
828 oi->type_infos[oi->num_info].name = name;
829 ++(oi->num_info);
830 }
831
832 /**
833 * gsk_hook_get_last_poll_state:
834 * @hook: test whether the last invocation of the set-poll function
835 * was called with TRUE or FALSE.
836 *
837 * Get whether this hook is supposed to be polling or not.
838 *
839 * returns: whether the hook is being polled.
840 */
841 gboolean
gsk_hook_get_last_poll_state(GskHook * hook)842 gsk_hook_get_last_poll_state(GskHook *hook)
843 {
844 return GSK_HOOK_TEST_FLAG (hook, HAS_POLL);
845 }
846
847 #ifdef GSK_DEBUG
848 static const char *
get_hook_name_and_type(GskHook * hook,GType * type_out)849 get_hook_name_and_type (GskHook *hook,
850 GType *type_out)
851 {
852 guint index = hook->inset;
853 PerOffsetInfo *oi;
854 GObject *object;
855 GType object_type;
856 guint i;
857 g_assert (index % 4 == 0 && index >= sizeof (GObject));
858 index -= sizeof (GObject);
859 index /= 4;
860 if (per_offset->len <= index)
861 goto fail;
862 oi = per_offset->pdata[index];
863 object = GSK_HOOK_GET_OBJECT (hook);
864 object_type = G_OBJECT_TYPE (object);
865 for (i = 0; i < oi->num_info; i++)
866 if (g_type_is_a (object_type, oi->type_infos[i].type))
867 {
868 *type_out = oi->type_infos[i].type;
869 return oi->type_infos[i].name;
870 }
871 fail:
872 *type_out = 0;
873 return NULL;
874 }
875
876 #include <stdio.h>
877 static void
gsk_hook_debug(GskHook * hook,const char * format,...)878 gsk_hook_debug (GskHook *hook,
879 const char *format,
880 ...)
881 {
882 GType hook_type;
883 const char *name = get_hook_name_and_type (hook, &hook_type);
884 GObject *instance = GSK_HOOK_GET_OBJECT (hook);
885 GType instance_type = G_OBJECT_TYPE (instance);
886 va_list args;
887 fprintf (stderr, "debug: hook: %s:%s [%s,%p]: ",
888 g_type_name (hook_type), name,
889 g_type_name (instance_type), instance);
890 va_start (args, format);
891 vfprintf (stderr, format, args);
892 va_end (args);
893 fprintf (stderr, ".\n");
894 }
895 #endif
896
897
898 /**
899 * _gsk_hook_init:
900 *
901 * Initialize the hook system.
902 */
_gsk_hook_init()903 void _gsk_hook_init ()
904 {
905 per_offset = g_ptr_array_new ();
906 gsk_hook_main_loop_quark = g_quark_from_static_string ("GskHook--main-loop-nonblocking");
907 }
908