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