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