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