Imported Upstream version 2.74.3
[platform/upstream/glib.git] / glib / ghook.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GHook: Callback maintenance functions
5  * Copyright (C) 1998 Tim Janik
6  *
7  * SPDX-License-Identifier: LGPL-2.1-or-later
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21  */
22
23 /*
24  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GLib Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GLib at ftp://ftp.gtk.org/pub/gtk/.
28  */
29
30 /*
31  * MT safe
32  */
33
34 #include "config.h"
35
36 #include "ghook.h"
37
38 #include "gtestutils.h"
39 #include "gslice.h"
40
41 /**
42  * SECTION:hooks
43  * @title: Hook Functions
44  * @short_description: support for manipulating lists of hook functions
45  *
46  * The #GHookList, #GHook and their related functions provide support for
47  * lists of hook functions. Functions can be added and removed from the lists,
48  * and the list of hook functions can be invoked.
49  */
50
51 /**
52  * GHookList:
53  * @seq_id: the next free #GHook id
54  * @hook_size: the size of the #GHookList elements, in bytes
55  * @is_setup: 1 if the #GHookList has been initialized
56  * @hooks: the first #GHook element in the list
57  * @dummy3: unused
58  * @finalize_hook: the function to call to finalize a #GHook element.
59  *     The default behaviour is to call the hooks @destroy function
60  * @dummy: unused
61  *
62  * The #GHookList struct represents a list of hook functions.
63  */
64
65 /**
66  * GHookFinalizeFunc:
67  * @hook_list: a #GHookList
68  * @hook: the hook in @hook_list that gets finalized
69  *
70  * Defines the type of function to be called when a hook in a
71  * list of hooks gets finalized.
72  */
73
74 /**
75  * GHookFlagMask:
76  * @G_HOOK_FLAG_ACTIVE: set if the hook has not been destroyed
77  * @G_HOOK_FLAG_IN_CALL: set if the hook is currently being run
78  * @G_HOOK_FLAG_MASK: A mask covering all bits reserved for
79  *   hook flags; see %G_HOOK_FLAG_USER_SHIFT
80  *
81  * Flags used internally in the #GHook implementation.
82  */
83
84 /**
85  * G_HOOK_FLAGS:
86  * @hook: a #GHook
87  *
88  * Gets the flags of a hook.
89  */
90
91 /**
92  * G_HOOK_FLAG_USER_SHIFT:
93  *
94  * The position of the first bit which is not reserved for internal
95  * use be the #GHook implementation, i.e.
96  * `1 << G_HOOK_FLAG_USER_SHIFT` is the first
97  * bit which can be used for application-defined flags.
98  */
99
100 /**
101  * G_HOOK:
102  * @hook: a pointer
103  *
104  * Casts a pointer to a `GHook*`.
105  */
106
107 /**
108  * G_HOOK_IS_VALID:
109  * @hook: a #GHook
110  *
111  * Returns %TRUE if the #GHook is valid, i.e. it is in a #GHookList,
112  * it is active and it has not been destroyed.
113  *
114  * Returns: %TRUE if the #GHook is valid
115  */
116
117 /**
118  * G_HOOK_ACTIVE:
119  * @hook: a #GHook
120  *
121  * Returns %TRUE if the #GHook is active, which is normally the case
122  * until the #GHook is destroyed.
123  *
124  * Returns: %TRUE if the #GHook is active
125  */
126
127 /**
128  * G_HOOK_IN_CALL:
129  * @hook: a #GHook
130  *
131  * Returns %TRUE if the #GHook function is currently executing.
132  *
133  * Returns: %TRUE if the #GHook function is currently executing
134  */
135
136 /**
137  * G_HOOK_IS_UNLINKED:
138  * @hook: a #GHook
139  *
140  * Returns %TRUE if the #GHook is not in a #GHookList.
141  *
142  * Returns: %TRUE if the #GHook is not in a #GHookList
143  */
144
145 /**
146  * GHook:
147  * @data: data which is passed to func when this hook is invoked
148  * @next: pointer to the next hook in the list
149  * @prev: pointer to the previous hook in the list
150  * @ref_count: the reference count of this hook
151  * @hook_id: the id of this hook, which is unique within its list
152  * @flags: flags which are set for this hook. See #GHookFlagMask for
153  *     predefined flags
154  * @func: the function to call when this hook is invoked. The possible
155  *     signatures for this function are #GHookFunc and #GHookCheckFunc
156  * @destroy: the default @finalize_hook function of a #GHookList calls
157  *     this member of the hook that is being finalized
158  *
159  * The #GHook struct represents a single hook function in a #GHookList.
160  */
161
162 /**
163  * GHookFunc:
164  * @data: the data field of the #GHook is passed to the hook function here
165  *
166  * Defines the type of a hook function that can be invoked
167  * by g_hook_list_invoke().
168  */
169
170 /**
171  * GHookCheckFunc:
172  * @data: the data field of the #GHook is passed to the hook function here
173  *
174  * Defines the type of a hook function that can be invoked
175  * by g_hook_list_invoke_check().
176  *
177  * Returns: %FALSE if the #GHook should be destroyed
178  */
179
180 /* --- functions --- */
181 static void
182 default_finalize_hook (GHookList *hook_list,
183                        GHook     *hook)
184 {
185   GDestroyNotify destroy = hook->destroy;
186
187   if (destroy)
188     {
189       hook->destroy = NULL;
190       destroy (hook->data);
191     }
192 }
193
194 /**
195  * g_hook_list_init:
196  * @hook_list: a #GHookList
197  * @hook_size: the size of each element in the #GHookList,
198  *     typically `sizeof (GHook)`.
199  *
200  * Initializes a #GHookList.
201  * This must be called before the #GHookList is used.
202  */
203 void
204 g_hook_list_init (GHookList *hook_list,
205                   guint      hook_size)
206 {
207   g_return_if_fail (hook_list != NULL);
208   g_return_if_fail (hook_size >= sizeof (GHook));
209   
210   hook_list->seq_id = 1;
211   hook_list->hook_size = hook_size;
212   hook_list->is_setup = TRUE;
213   hook_list->hooks = NULL;
214   hook_list->dummy3 = NULL;
215   hook_list->finalize_hook = default_finalize_hook;
216   hook_list->dummy[0] = NULL;
217   hook_list->dummy[1] = NULL;
218 }
219
220 /**
221  * g_hook_list_clear:
222  * @hook_list: a #GHookList
223  *
224  * Removes all the #GHook elements from a #GHookList.
225  */
226 void
227 g_hook_list_clear (GHookList *hook_list)
228 {
229   g_return_if_fail (hook_list != NULL);
230   
231   if (hook_list->is_setup)
232     {
233       GHook *hook;
234       
235       hook_list->is_setup = FALSE;
236       
237       hook = hook_list->hooks;
238       if (!hook)
239         {
240           /* destroy hook_list->hook_memchunk */
241         }
242       else
243         do
244           {
245             GHook *tmp;
246             
247             g_hook_ref (hook_list, hook);
248             g_hook_destroy_link (hook_list, hook);
249             tmp = hook->next;
250             g_hook_unref (hook_list, hook);
251             hook = tmp;
252           }
253         while (hook);
254     }
255 }
256
257 /**
258  * g_hook_alloc:
259  * @hook_list: a #GHookList
260  *
261  * Allocates space for a #GHook and initializes it.
262  *
263  * Returns: a new #GHook
264  */
265 GHook*
266 g_hook_alloc (GHookList *hook_list)
267 {
268   GHook *hook;
269   
270   g_return_val_if_fail (hook_list != NULL, NULL);
271   g_return_val_if_fail (hook_list->is_setup, NULL);
272   
273   hook = g_slice_alloc0 (hook_list->hook_size);
274   hook->data = NULL;
275   hook->next = NULL;
276   hook->prev = NULL;
277   hook->flags = G_HOOK_FLAG_ACTIVE;
278   hook->ref_count = 0;
279   hook->hook_id = 0;
280   hook->func = NULL;
281   hook->destroy = NULL;
282   
283   return hook;
284 }
285 /**
286  * g_hook_free:
287  * @hook_list: a #GHookList
288  * @hook: the #GHook to free
289  *
290  * Calls the #GHookList @finalize_hook function if it exists,
291  * and frees the memory allocated for the #GHook.
292  */
293 void
294 g_hook_free (GHookList *hook_list,
295              GHook     *hook)
296 {
297   g_return_if_fail (hook_list != NULL);
298   g_return_if_fail (hook_list->is_setup);
299   g_return_if_fail (hook != NULL);
300   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
301   g_return_if_fail (!G_HOOK_IN_CALL (hook));
302
303   if(hook_list->finalize_hook != NULL)
304       hook_list->finalize_hook (hook_list, hook);
305   g_slice_free1 (hook_list->hook_size, hook);
306 }
307
308 /**
309  * g_hook_destroy_link:
310  * @hook_list: a #GHookList
311  * @hook: the #GHook to remove
312  *
313  * Removes one #GHook from a #GHookList, marking it
314  * inactive and calling g_hook_unref() on it.
315  */
316 void
317 g_hook_destroy_link (GHookList *hook_list,
318                      GHook     *hook)
319 {
320   g_return_if_fail (hook_list != NULL);
321   g_return_if_fail (hook != NULL);
322
323   hook->flags &= ~G_HOOK_FLAG_ACTIVE;
324   if (hook->hook_id)
325     {
326       hook->hook_id = 0;
327       g_hook_unref (hook_list, hook); /* counterpart to g_hook_insert_before */
328     }
329 }
330
331 /**
332  * g_hook_destroy:
333  * @hook_list: a #GHookList
334  * @hook_id: a hook ID
335  *
336  * Destroys a #GHook, given its ID.
337  *
338  * Returns: %TRUE if the #GHook was found in the #GHookList and destroyed
339  */
340 gboolean
341 g_hook_destroy (GHookList   *hook_list,
342                 gulong       hook_id)
343 {
344   GHook *hook;
345   
346   g_return_val_if_fail (hook_list != NULL, FALSE);
347   g_return_val_if_fail (hook_id > 0, FALSE);
348   
349   hook = g_hook_get (hook_list, hook_id);
350   if (hook)
351     {
352       g_hook_destroy_link (hook_list, hook);
353       return TRUE;
354     }
355   
356   return FALSE;
357 }
358
359 /**
360  * g_hook_unref:
361  * @hook_list: a #GHookList
362  * @hook: the #GHook to unref
363  *
364  * Decrements the reference count of a #GHook.
365  * If the reference count falls to 0, the #GHook is removed
366  * from the #GHookList and g_hook_free() is called to free it.
367  */
368 void
369 g_hook_unref (GHookList *hook_list,
370               GHook     *hook)
371 {
372   g_return_if_fail (hook_list != NULL);
373   g_return_if_fail (hook != NULL);
374   g_return_if_fail (hook->ref_count > 0);
375   
376   hook->ref_count--;
377   if (!hook->ref_count)
378     {
379       g_return_if_fail (hook->hook_id == 0);
380       g_return_if_fail (!G_HOOK_IN_CALL (hook));
381
382       if (hook->prev)
383         hook->prev->next = hook->next;
384       else
385         hook_list->hooks = hook->next;
386       if (hook->next)
387         {
388           hook->next->prev = hook->prev;
389           hook->next = NULL;
390         }
391       hook->prev = NULL;
392
393       if (!hook_list->is_setup)
394         {
395           hook_list->is_setup = TRUE;
396           g_hook_free (hook_list, hook);
397           hook_list->is_setup = FALSE;
398       
399           if (!hook_list->hooks)
400             {
401               /* destroy hook_list->hook_memchunk */
402             }
403         }
404       else
405         g_hook_free (hook_list, hook);
406     }
407 }
408
409 /**
410  * g_hook_ref:
411  * @hook_list: a #GHookList
412  * @hook: the #GHook to increment the reference count of
413  *
414  * Increments the reference count for a #GHook.
415  *
416  * Returns: the @hook that was passed in (since 2.6)
417  */
418 GHook *
419 g_hook_ref (GHookList *hook_list,
420             GHook     *hook)
421 {
422   g_return_val_if_fail (hook_list != NULL, NULL);
423   g_return_val_if_fail (hook != NULL, NULL);
424   g_return_val_if_fail (hook->ref_count > 0, NULL);
425   
426   hook->ref_count++;
427
428   return hook;
429 }
430
431 /**
432  * g_hook_append:
433  * @hook_list: a #GHookList
434  * @hook: the #GHook to add to the end of @hook_list
435  *
436  * Appends a #GHook onto the end of a #GHookList.
437  */
438
439 /**
440  * g_hook_prepend:
441  * @hook_list: a #GHookList
442  * @hook: the #GHook to add to the start of @hook_list
443  *
444  * Prepends a #GHook on the start of a #GHookList.
445  */
446 void
447 g_hook_prepend (GHookList *hook_list,
448                 GHook     *hook)
449 {
450   g_return_if_fail (hook_list != NULL);
451   
452   g_hook_insert_before (hook_list, hook_list->hooks, hook);
453 }
454
455 /**
456  * g_hook_insert_before:
457  * @hook_list: a #GHookList
458  * @sibling: (nullable): the #GHook to insert the new #GHook before
459  * @hook: the #GHook to insert
460  *
461  * Inserts a #GHook into a #GHookList, before a given #GHook.
462  */
463 void
464 g_hook_insert_before (GHookList *hook_list,
465                       GHook     *sibling,
466                       GHook     *hook)
467 {
468   g_return_if_fail (hook_list != NULL);
469   g_return_if_fail (hook_list->is_setup);
470   g_return_if_fail (hook != NULL);
471   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
472   g_return_if_fail (hook->ref_count == 0);
473   
474   hook->hook_id = hook_list->seq_id++;
475   hook->ref_count = 1; /* counterpart to g_hook_destroy_link */
476   
477   if (sibling)
478     {
479       if (sibling->prev)
480         {
481           hook->prev = sibling->prev;
482           hook->prev->next = hook;
483           hook->next = sibling;
484           sibling->prev = hook;
485         }
486       else
487         {
488           hook_list->hooks = hook;
489           hook->next = sibling;
490           sibling->prev = hook;
491         }
492     }
493   else
494     {
495       if (hook_list->hooks)
496         {
497           sibling = hook_list->hooks;
498           while (sibling->next)
499             sibling = sibling->next;
500           hook->prev = sibling;
501           sibling->next = hook;
502         }
503       else
504         hook_list->hooks = hook;
505     }
506 }
507
508 /**
509  * g_hook_list_invoke:
510  * @hook_list: a #GHookList
511  * @may_recurse: %TRUE if functions which are already running
512  *     (e.g. in another thread) can be called. If set to %FALSE,
513  *     these are skipped
514  *
515  * Calls all of the #GHook functions in a #GHookList.
516  */
517 void
518 g_hook_list_invoke (GHookList *hook_list,
519                     gboolean   may_recurse)
520 {
521   GHook *hook;
522   
523   g_return_if_fail (hook_list != NULL);
524   g_return_if_fail (hook_list->is_setup);
525
526   hook = g_hook_first_valid (hook_list, may_recurse);
527   while (hook)
528     {
529       GHookFunc func;
530       gboolean was_in_call;
531       
532       func = (GHookFunc) hook->func;
533       
534       was_in_call = G_HOOK_IN_CALL (hook);
535       hook->flags |= G_HOOK_FLAG_IN_CALL;
536       func (hook->data);
537       if (!was_in_call)
538         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
539       
540       hook = g_hook_next_valid (hook_list, hook, may_recurse);
541     }
542 }
543
544 /**
545  * g_hook_list_invoke_check:
546  * @hook_list: a #GHookList
547  * @may_recurse: %TRUE if functions which are already running
548  *     (e.g. in another thread) can be called. If set to %FALSE,
549  *     these are skipped
550  *
551  * Calls all of the #GHook functions in a #GHookList.
552  * Any function which returns %FALSE is removed from the #GHookList.
553  */
554 void
555 g_hook_list_invoke_check (GHookList *hook_list,
556                           gboolean   may_recurse)
557 {
558   GHook *hook;
559   
560   g_return_if_fail (hook_list != NULL);
561   g_return_if_fail (hook_list->is_setup);
562   
563   hook = g_hook_first_valid (hook_list, may_recurse);
564   while (hook)
565     {
566       GHookCheckFunc func;
567       gboolean was_in_call;
568       gboolean need_destroy;
569       
570       func = (GHookCheckFunc) hook->func;
571       
572       was_in_call = G_HOOK_IN_CALL (hook);
573       hook->flags |= G_HOOK_FLAG_IN_CALL;
574       need_destroy = !func (hook->data);
575       if (!was_in_call)
576         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
577       if (need_destroy)
578         g_hook_destroy_link (hook_list, hook);
579       
580       hook = g_hook_next_valid (hook_list, hook, may_recurse);
581     }
582 }
583
584 /**
585  * GHookCheckMarshaller:
586  * @hook: a #GHook
587  * @user_data: user data
588  *
589  * Defines the type of function used by g_hook_list_marshal_check().
590  *
591  * Returns: %FALSE if @hook should be destroyed
592  */
593
594 /**
595  * g_hook_list_marshal_check:
596  * @hook_list: a #GHookList
597  * @may_recurse: %TRUE if hooks which are currently running
598  *     (e.g. in another thread) are considered valid. If set to %FALSE,
599  *     these are skipped
600  * @marshaller: the function to call for each #GHook
601  * @marshal_data: data to pass to @marshaller
602  *
603  * Calls a function on each valid #GHook and destroys it if the
604  * function returns %FALSE.
605  */
606 void
607 g_hook_list_marshal_check (GHookList           *hook_list,
608                            gboolean             may_recurse,
609                            GHookCheckMarshaller marshaller,
610                            gpointer             data)
611 {
612   GHook *hook;
613   
614   g_return_if_fail (hook_list != NULL);
615   g_return_if_fail (hook_list->is_setup);
616   g_return_if_fail (marshaller != NULL);
617   
618   hook = g_hook_first_valid (hook_list, may_recurse);
619   while (hook)
620     {
621       gboolean was_in_call;
622       gboolean need_destroy;
623       
624       was_in_call = G_HOOK_IN_CALL (hook);
625       hook->flags |= G_HOOK_FLAG_IN_CALL;
626       need_destroy = !marshaller (hook, data);
627       if (!was_in_call)
628         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
629       if (need_destroy)
630         g_hook_destroy_link (hook_list, hook);
631       
632       hook = g_hook_next_valid (hook_list, hook, may_recurse);
633     }
634 }
635
636 /**
637  * GHookMarshaller:
638  * @hook: a #GHook
639  * @user_data: user data
640  *
641  * Defines the type of function used by g_hook_list_marshal().
642  */
643
644 /**
645  * g_hook_list_marshal:
646  * @hook_list: a #GHookList
647  * @may_recurse: %TRUE if hooks which are currently running
648  *     (e.g. in another thread) are considered valid. If set to %FALSE,
649  *     these are skipped
650  * @marshaller: the function to call for each #GHook
651  * @marshal_data: data to pass to @marshaller
652  *
653  * Calls a function on each valid #GHook.
654  */
655 void
656 g_hook_list_marshal (GHookList               *hook_list,
657                      gboolean                 may_recurse,
658                      GHookMarshaller          marshaller,
659                      gpointer                 data)
660 {
661   GHook *hook;
662   
663   g_return_if_fail (hook_list != NULL);
664   g_return_if_fail (hook_list->is_setup);
665   g_return_if_fail (marshaller != NULL);
666   
667   hook = g_hook_first_valid (hook_list, may_recurse);
668   while (hook)
669     {
670       gboolean was_in_call;
671       
672       was_in_call = G_HOOK_IN_CALL (hook);
673       hook->flags |= G_HOOK_FLAG_IN_CALL;
674       marshaller (hook, data);
675       if (!was_in_call)
676         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
677       
678       hook = g_hook_next_valid (hook_list, hook, may_recurse);
679     }
680 }
681
682 /**
683  * g_hook_first_valid:
684  * @hook_list: a #GHookList
685  * @may_be_in_call: %TRUE if hooks which are currently running
686  *     (e.g. in another thread) are considered valid. If set to %FALSE,
687  *     these are skipped
688  *
689  * Returns the first #GHook in a #GHookList which has not been destroyed.
690  * The reference count for the #GHook is incremented, so you must call
691  * g_hook_unref() to restore it when no longer needed. (Or call
692  * g_hook_next_valid() if you are stepping through the #GHookList.)
693  *
694  * Returns: the first valid #GHook, or %NULL if none are valid
695  */
696 GHook*
697 g_hook_first_valid (GHookList *hook_list,
698                     gboolean   may_be_in_call)
699 {
700   g_return_val_if_fail (hook_list != NULL, NULL);
701   
702   if (hook_list->is_setup)
703     {
704       GHook *hook;
705       
706       hook = hook_list->hooks;
707       if (hook)
708         {
709           g_hook_ref (hook_list, hook);
710           if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
711             return hook;
712           else
713             return g_hook_next_valid (hook_list, hook, may_be_in_call);
714         }
715     }
716   
717   return NULL;
718 }
719
720 /**
721  * g_hook_next_valid:
722  * @hook_list: a #GHookList
723  * @hook: the current #GHook
724  * @may_be_in_call: %TRUE if hooks which are currently running
725  *     (e.g. in another thread) are considered valid. If set to %FALSE,
726  *     these are skipped
727  *
728  * Returns the next #GHook in a #GHookList which has not been destroyed.
729  * The reference count for the #GHook is incremented, so you must call
730  * g_hook_unref() to restore it when no longer needed. (Or continue to call
731  * g_hook_next_valid() until %NULL is returned.)
732  *
733  * Returns: the next valid #GHook, or %NULL if none are valid
734  */
735 GHook*
736 g_hook_next_valid (GHookList *hook_list,
737                    GHook     *hook,
738                    gboolean   may_be_in_call)
739 {
740   GHook *ohook = hook;
741
742   g_return_val_if_fail (hook_list != NULL, NULL);
743
744   if (!hook)
745     return NULL;
746   
747   hook = hook->next;
748   while (hook)
749     {
750       if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
751         {
752           g_hook_ref (hook_list, hook);
753           g_hook_unref (hook_list, ohook);
754           
755           return hook;
756         }
757       hook = hook->next;
758     }
759   g_hook_unref (hook_list, ohook);
760
761   return NULL;
762 }
763
764 /**
765  * g_hook_get:
766  * @hook_list: a #GHookList
767  * @hook_id: a hook id
768  *
769  * Returns the #GHook with the given id, or %NULL if it is not found.
770  *
771  * Returns: the #GHook with the given id, or %NULL if it is not found
772  */
773 GHook*
774 g_hook_get (GHookList *hook_list,
775             gulong     hook_id)
776 {
777   GHook *hook;
778   
779   g_return_val_if_fail (hook_list != NULL, NULL);
780   g_return_val_if_fail (hook_id > 0, NULL);
781   
782   hook = hook_list->hooks;
783   while (hook)
784     {
785       if (hook->hook_id == hook_id)
786         return hook;
787       hook = hook->next;
788     }
789   
790   return NULL;
791 }
792
793 /**
794  * GHookFindFunc:
795  * @hook: a #GHook
796  * @user_data: user data passed to g_hook_find_func()
797  *
798  * Defines the type of the function passed to g_hook_find().
799  *
800  * Returns: %TRUE if the required #GHook has been found
801  */
802
803 /**
804  * g_hook_find:
805  * @hook_list: a #GHookList
806  * @need_valids: %TRUE if #GHook elements which have been destroyed
807  *     should be skipped
808  * @func: the function to call for each #GHook, which should return
809  *     %TRUE when the #GHook has been found
810  * @data: the data to pass to @func
811  *
812  * Finds a #GHook in a #GHookList using the given function to
813  * test for a match.
814  *
815  * Returns: the found #GHook or %NULL if no matching #GHook is found
816  */
817 GHook*
818 g_hook_find (GHookList    *hook_list,
819              gboolean      need_valids,
820              GHookFindFunc func,
821              gpointer      data)
822 {
823   GHook *hook;
824   
825   g_return_val_if_fail (hook_list != NULL, NULL);
826   g_return_val_if_fail (func != NULL, NULL);
827   
828   hook = hook_list->hooks;
829   while (hook)
830     {
831       GHook *tmp;
832
833       /* test only non-destroyed hooks */
834       if (!hook->hook_id)
835         {
836           hook = hook->next;
837           continue;
838         }
839       
840       g_hook_ref (hook_list, hook);
841       
842       if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_ACTIVE (hook)))
843         {
844           g_hook_unref (hook_list, hook);
845           
846           return hook;
847         }
848
849       tmp = hook->next;
850       g_hook_unref (hook_list, hook);
851       hook = tmp;
852     }
853   
854   return NULL;
855 }
856
857 /**
858  * g_hook_find_data:
859  * @hook_list: a #GHookList
860  * @need_valids: %TRUE if #GHook elements which have been destroyed
861  *     should be skipped
862  * @data: the data to find
863  *
864  * Finds a #GHook in a #GHookList with the given data.
865  *
866  * Returns: the #GHook with the given @data or %NULL if no matching
867  *     #GHook is found
868  */
869 GHook*
870 g_hook_find_data (GHookList *hook_list,
871                   gboolean   need_valids,
872                   gpointer   data)
873 {
874   GHook *hook;
875   
876   g_return_val_if_fail (hook_list != NULL, NULL);
877   
878   hook = hook_list->hooks;
879   while (hook)
880     {
881       /* test only non-destroyed hooks */
882       if (hook->data == data &&
883           hook->hook_id &&
884           (!need_valids || G_HOOK_ACTIVE (hook)))
885         return hook;
886
887       hook = hook->next;
888     }
889   
890   return NULL;
891 }
892
893 /**
894  * g_hook_find_func:
895  * @hook_list: a #GHookList
896  * @need_valids: %TRUE if #GHook elements which have been destroyed
897  *     should be skipped
898  * @func: the function to find
899  *
900  * Finds a #GHook in a #GHookList with the given function.
901  *
902  * Returns: the #GHook with the given @func or %NULL if no matching
903  *     #GHook is found
904  */
905 GHook*
906 g_hook_find_func (GHookList *hook_list,
907                   gboolean   need_valids,
908                   gpointer   func)
909 {
910   GHook *hook;
911   
912   g_return_val_if_fail (hook_list != NULL, NULL);
913   g_return_val_if_fail (func != NULL, NULL);
914   
915   hook = hook_list->hooks;
916   while (hook)
917     {
918       /* test only non-destroyed hooks */
919       if (hook->func == func &&
920           hook->hook_id &&
921           (!need_valids || G_HOOK_ACTIVE (hook)))
922         return hook;
923
924       hook = hook->next;
925     }
926   
927   return NULL;
928 }
929
930 /**
931  * g_hook_find_func_data:
932  * @hook_list: a #GHookList
933  * @need_valids: %TRUE if #GHook elements which have been destroyed
934  *     should be skipped
935  * @func: (not nullable): the function to find
936  * @data: the data to find
937  *
938  * Finds a #GHook in a #GHookList with the given function and data.
939  *
940  * Returns: the #GHook with the given @func and @data or %NULL if
941  *     no matching #GHook is found
942  */
943 GHook*
944 g_hook_find_func_data (GHookList *hook_list,
945                        gboolean   need_valids,
946                        gpointer   func,
947                        gpointer   data)
948 {
949   GHook *hook;
950   
951   g_return_val_if_fail (hook_list != NULL, NULL);
952   g_return_val_if_fail (func != NULL, NULL);
953   
954   hook = hook_list->hooks;
955   while (hook)
956     {
957       /* test only non-destroyed hooks */
958       if (hook->data == data &&
959           hook->func == func &&
960           hook->hook_id &&
961           (!need_valids || G_HOOK_ACTIVE (hook)))
962         return hook;
963
964       hook = hook->next;
965     }
966   
967   return NULL;
968 }
969
970 /**
971  * GHookCompareFunc:
972  * @new_hook: the #GHook being inserted
973  * @sibling: the #GHook to compare with @new_hook
974  *
975  * Defines the type of function used to compare #GHook elements in
976  * g_hook_insert_sorted().
977  *
978  * Returns: a value <= 0 if @new_hook should be before @sibling
979  */
980
981 /**
982  * g_hook_insert_sorted:
983  * @hook_list: a #GHookList
984  * @hook: the #GHook to insert
985  * @func: the comparison function used to sort the #GHook elements
986  *
987  * Inserts a #GHook into a #GHookList, sorted by the given function.
988  */
989 void
990 g_hook_insert_sorted (GHookList       *hook_list,
991                       GHook           *hook,
992                       GHookCompareFunc func)
993 {
994   GHook *sibling;
995   
996   g_return_if_fail (hook_list != NULL);
997   g_return_if_fail (hook_list->is_setup);
998   g_return_if_fail (hook != NULL);
999   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
1000   g_return_if_fail (hook->func != NULL);
1001   g_return_if_fail (func != NULL);
1002
1003   /* first non-destroyed hook */
1004   sibling = hook_list->hooks;
1005   while (sibling && !sibling->hook_id)
1006     sibling = sibling->next;
1007   
1008   while (sibling)
1009     {
1010       GHook *tmp;
1011       
1012       g_hook_ref (hook_list, sibling);
1013       if (func (hook, sibling) <= 0 && sibling->hook_id)
1014         {
1015           g_hook_unref (hook_list, sibling);
1016           break;
1017         }
1018
1019       /* next non-destroyed hook */
1020       tmp = sibling->next;
1021       while (tmp && !tmp->hook_id)
1022         tmp = tmp->next;
1023
1024       g_hook_unref (hook_list, sibling);
1025       sibling = tmp;
1026    
1027  }
1028   
1029   g_hook_insert_before (hook_list, sibling, hook);
1030 }
1031
1032 /**
1033  * g_hook_compare_ids:
1034  * @new_hook: a #GHook
1035  * @sibling: a #GHook to compare with @new_hook
1036  *
1037  * Compares the ids of two #GHook elements, returning a negative value
1038  * if the second id is greater than the first.
1039  *
1040  * Returns: a value <= 0 if the id of @sibling is >= the id of @new_hook
1041  */
1042 gint
1043 g_hook_compare_ids (GHook *new_hook,
1044                     GHook *sibling)
1045 {
1046   if (new_hook->hook_id < sibling->hook_id)
1047     return -1;
1048   else if (new_hook->hook_id > sibling->hook_id)
1049     return 1;
1050   
1051   return 0;
1052 }