Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
183 | 0 | GDestroyNotify destroy = hook->destroy; |
184 | |
|
185 | 0 | if (destroy) |
186 | 0 | { |
187 | 0 | hook->destroy = NULL; |
188 | 0 | destroy (hook->data); |
189 | 0 | } |
190 | 0 | } |
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 | 0 | { |
205 | 0 | g_return_if_fail (hook_list != NULL); |
206 | 0 | g_return_if_fail (hook_size >= sizeof (GHook)); |
207 | | |
208 | 0 | hook_list->seq_id = 1; |
209 | 0 | hook_list->hook_size = hook_size; |
210 | 0 | hook_list->is_setup = TRUE; |
211 | 0 | hook_list->hooks = NULL; |
212 | 0 | hook_list->dummy3 = NULL; |
213 | 0 | hook_list->finalize_hook = default_finalize_hook; |
214 | 0 | hook_list->dummy[0] = NULL; |
215 | 0 | hook_list->dummy[1] = NULL; |
216 | 0 | } |
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 | 0 | { |
227 | 0 | g_return_if_fail (hook_list != NULL); |
228 | | |
229 | 0 | if (hook_list->is_setup) |
230 | 0 | { |
231 | 0 | GHook *hook; |
232 | | |
233 | 0 | hook_list->is_setup = FALSE; |
234 | | |
235 | 0 | hook = hook_list->hooks; |
236 | 0 | if (!hook) |
237 | 0 | { |
238 | | /* destroy hook_list->hook_memchunk */ |
239 | 0 | } |
240 | 0 | else |
241 | 0 | do |
242 | 0 | { |
243 | 0 | GHook *tmp; |
244 | | |
245 | 0 | g_hook_ref (hook_list, hook); |
246 | 0 | g_hook_destroy_link (hook_list, hook); |
247 | 0 | tmp = hook->next; |
248 | 0 | g_hook_unref (hook_list, hook); |
249 | 0 | hook = tmp; |
250 | 0 | } |
251 | 0 | while (hook); |
252 | 0 | } |
253 | 0 | } |
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 | 0 | { |
266 | 0 | GHook *hook; |
267 | | |
268 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
269 | 0 | g_return_val_if_fail (hook_list->is_setup, NULL); |
270 | | |
271 | 0 | hook = g_slice_alloc0 (hook_list->hook_size); |
272 | 0 | hook->data = NULL; |
273 | 0 | hook->next = NULL; |
274 | 0 | hook->prev = NULL; |
275 | 0 | hook->flags = G_HOOK_FLAG_ACTIVE; |
276 | 0 | hook->ref_count = 0; |
277 | 0 | hook->hook_id = 0; |
278 | 0 | hook->func = NULL; |
279 | 0 | hook->destroy = NULL; |
280 | | |
281 | 0 | return hook; |
282 | 0 | } |
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 | 0 | { |
295 | 0 | g_return_if_fail (hook_list != NULL); |
296 | 0 | g_return_if_fail (hook_list->is_setup); |
297 | 0 | g_return_if_fail (hook != NULL); |
298 | 0 | g_return_if_fail (G_HOOK_IS_UNLINKED (hook)); |
299 | 0 | g_return_if_fail (!G_HOOK_IN_CALL (hook)); |
300 | | |
301 | 0 | if(hook_list->finalize_hook != NULL) |
302 | 0 | hook_list->finalize_hook (hook_list, hook); |
303 | 0 | g_slice_free1 (hook_list->hook_size, hook); |
304 | 0 | } |
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 | 0 | { |
318 | 0 | g_return_if_fail (hook_list != NULL); |
319 | 0 | g_return_if_fail (hook != NULL); |
320 | | |
321 | 0 | hook->flags &= ~G_HOOK_FLAG_ACTIVE; |
322 | 0 | if (hook->hook_id) |
323 | 0 | { |
324 | 0 | hook->hook_id = 0; |
325 | 0 | g_hook_unref (hook_list, hook); /* counterpart to g_hook_insert_before */ |
326 | 0 | } |
327 | 0 | } |
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 | 0 | { |
342 | 0 | GHook *hook; |
343 | | |
344 | 0 | g_return_val_if_fail (hook_list != NULL, FALSE); |
345 | 0 | g_return_val_if_fail (hook_id > 0, FALSE); |
346 | | |
347 | 0 | hook = g_hook_get (hook_list, hook_id); |
348 | 0 | if (hook) |
349 | 0 | { |
350 | 0 | g_hook_destroy_link (hook_list, hook); |
351 | 0 | return TRUE; |
352 | 0 | } |
353 | | |
354 | 0 | return FALSE; |
355 | 0 | } |
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 | 0 | { |
370 | 0 | g_return_if_fail (hook_list != NULL); |
371 | 0 | g_return_if_fail (hook != NULL); |
372 | 0 | g_return_if_fail (hook->ref_count > 0); |
373 | | |
374 | 0 | hook->ref_count--; |
375 | 0 | if (!hook->ref_count) |
376 | 0 | { |
377 | 0 | g_return_if_fail (hook->hook_id == 0); |
378 | 0 | g_return_if_fail (!G_HOOK_IN_CALL (hook)); |
379 | | |
380 | 0 | if (hook->prev) |
381 | 0 | hook->prev->next = hook->next; |
382 | 0 | else |
383 | 0 | hook_list->hooks = hook->next; |
384 | 0 | if (hook->next) |
385 | 0 | { |
386 | 0 | hook->next->prev = hook->prev; |
387 | 0 | hook->next = NULL; |
388 | 0 | } |
389 | 0 | hook->prev = NULL; |
390 | |
|
391 | 0 | if (!hook_list->is_setup) |
392 | 0 | { |
393 | 0 | hook_list->is_setup = TRUE; |
394 | 0 | g_hook_free (hook_list, hook); |
395 | 0 | hook_list->is_setup = FALSE; |
396 | | |
397 | 0 | if (!hook_list->hooks) |
398 | 0 | { |
399 | | /* destroy hook_list->hook_memchunk */ |
400 | 0 | } |
401 | 0 | } |
402 | 0 | else |
403 | 0 | g_hook_free (hook_list, hook); |
404 | 0 | } |
405 | 0 | } |
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 | 0 | { |
420 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
421 | 0 | g_return_val_if_fail (hook != NULL, NULL); |
422 | 0 | g_return_val_if_fail (hook->ref_count > 0, NULL); |
423 | | |
424 | 0 | hook->ref_count++; |
425 | |
|
426 | 0 | return hook; |
427 | 0 | } |
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 | 0 | { |
448 | 0 | g_return_if_fail (hook_list != NULL); |
449 | | |
450 | 0 | g_hook_insert_before (hook_list, hook_list->hooks, hook); |
451 | 0 | } |
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 | 0 | { |
466 | 0 | g_return_if_fail (hook_list != NULL); |
467 | 0 | g_return_if_fail (hook_list->is_setup); |
468 | 0 | g_return_if_fail (hook != NULL); |
469 | 0 | g_return_if_fail (G_HOOK_IS_UNLINKED (hook)); |
470 | 0 | g_return_if_fail (hook->ref_count == 0); |
471 | | |
472 | 0 | hook->hook_id = hook_list->seq_id++; |
473 | 0 | hook->ref_count = 1; /* counterpart to g_hook_destroy_link */ |
474 | | |
475 | 0 | if (sibling) |
476 | 0 | { |
477 | 0 | if (sibling->prev) |
478 | 0 | { |
479 | 0 | hook->prev = sibling->prev; |
480 | 0 | hook->prev->next = hook; |
481 | 0 | hook->next = sibling; |
482 | 0 | sibling->prev = hook; |
483 | 0 | } |
484 | 0 | else |
485 | 0 | { |
486 | 0 | hook_list->hooks = hook; |
487 | 0 | hook->next = sibling; |
488 | 0 | sibling->prev = hook; |
489 | 0 | } |
490 | 0 | } |
491 | 0 | else |
492 | 0 | { |
493 | 0 | if (hook_list->hooks) |
494 | 0 | { |
495 | 0 | sibling = hook_list->hooks; |
496 | 0 | while (sibling->next) |
497 | 0 | sibling = sibling->next; |
498 | 0 | hook->prev = sibling; |
499 | 0 | sibling->next = hook; |
500 | 0 | } |
501 | 0 | else |
502 | 0 | hook_list->hooks = hook; |
503 | 0 | } |
504 | 0 | } |
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 | 0 | { |
519 | 0 | GHook *hook; |
520 | | |
521 | 0 | g_return_if_fail (hook_list != NULL); |
522 | 0 | g_return_if_fail (hook_list->is_setup); |
523 | | |
524 | 0 | hook = g_hook_first_valid (hook_list, may_recurse); |
525 | 0 | while (hook) |
526 | 0 | { |
527 | 0 | GHookFunc func; |
528 | 0 | gboolean was_in_call; |
529 | | |
530 | 0 | func = (GHookFunc) hook->func; |
531 | | |
532 | 0 | was_in_call = G_HOOK_IN_CALL (hook); |
533 | 0 | hook->flags |= G_HOOK_FLAG_IN_CALL; |
534 | 0 | func (hook->data); |
535 | 0 | if (!was_in_call) |
536 | 0 | hook->flags &= ~G_HOOK_FLAG_IN_CALL; |
537 | | |
538 | 0 | hook = g_hook_next_valid (hook_list, hook, may_recurse); |
539 | 0 | } |
540 | 0 | } |
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 | 0 | { |
556 | 0 | GHook *hook; |
557 | | |
558 | 0 | g_return_if_fail (hook_list != NULL); |
559 | 0 | g_return_if_fail (hook_list->is_setup); |
560 | | |
561 | 0 | hook = g_hook_first_valid (hook_list, may_recurse); |
562 | 0 | while (hook) |
563 | 0 | { |
564 | 0 | GHookCheckFunc func; |
565 | 0 | gboolean was_in_call; |
566 | 0 | gboolean need_destroy; |
567 | | |
568 | 0 | func = (GHookCheckFunc) hook->func; |
569 | | |
570 | 0 | was_in_call = G_HOOK_IN_CALL (hook); |
571 | 0 | hook->flags |= G_HOOK_FLAG_IN_CALL; |
572 | 0 | need_destroy = !func (hook->data); |
573 | 0 | if (!was_in_call) |
574 | 0 | hook->flags &= ~G_HOOK_FLAG_IN_CALL; |
575 | 0 | if (need_destroy) |
576 | 0 | g_hook_destroy_link (hook_list, hook); |
577 | | |
578 | 0 | hook = g_hook_next_valid (hook_list, hook, may_recurse); |
579 | 0 | } |
580 | 0 | } |
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 | 0 | { |
610 | 0 | GHook *hook; |
611 | | |
612 | 0 | g_return_if_fail (hook_list != NULL); |
613 | 0 | g_return_if_fail (hook_list->is_setup); |
614 | 0 | g_return_if_fail (marshaller != NULL); |
615 | | |
616 | 0 | hook = g_hook_first_valid (hook_list, may_recurse); |
617 | 0 | while (hook) |
618 | 0 | { |
619 | 0 | gboolean was_in_call; |
620 | 0 | gboolean need_destroy; |
621 | | |
622 | 0 | was_in_call = G_HOOK_IN_CALL (hook); |
623 | 0 | hook->flags |= G_HOOK_FLAG_IN_CALL; |
624 | 0 | need_destroy = !marshaller (hook, data); |
625 | 0 | if (!was_in_call) |
626 | 0 | hook->flags &= ~G_HOOK_FLAG_IN_CALL; |
627 | 0 | if (need_destroy) |
628 | 0 | g_hook_destroy_link (hook_list, hook); |
629 | | |
630 | 0 | hook = g_hook_next_valid (hook_list, hook, may_recurse); |
631 | 0 | } |
632 | 0 | } |
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 | 0 | { |
659 | 0 | GHook *hook; |
660 | | |
661 | 0 | g_return_if_fail (hook_list != NULL); |
662 | 0 | g_return_if_fail (hook_list->is_setup); |
663 | 0 | g_return_if_fail (marshaller != NULL); |
664 | | |
665 | 0 | hook = g_hook_first_valid (hook_list, may_recurse); |
666 | 0 | while (hook) |
667 | 0 | { |
668 | 0 | gboolean was_in_call; |
669 | | |
670 | 0 | was_in_call = G_HOOK_IN_CALL (hook); |
671 | 0 | hook->flags |= G_HOOK_FLAG_IN_CALL; |
672 | 0 | marshaller (hook, data); |
673 | 0 | if (!was_in_call) |
674 | 0 | hook->flags &= ~G_HOOK_FLAG_IN_CALL; |
675 | | |
676 | 0 | hook = g_hook_next_valid (hook_list, hook, may_recurse); |
677 | 0 | } |
678 | 0 | } |
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 | 0 | { |
698 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
699 | | |
700 | 0 | if (hook_list->is_setup) |
701 | 0 | { |
702 | 0 | GHook *hook; |
703 | | |
704 | 0 | hook = hook_list->hooks; |
705 | 0 | if (hook) |
706 | 0 | { |
707 | 0 | g_hook_ref (hook_list, hook); |
708 | 0 | if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook))) |
709 | 0 | return hook; |
710 | 0 | else |
711 | 0 | return g_hook_next_valid (hook_list, hook, may_be_in_call); |
712 | 0 | } |
713 | 0 | } |
714 | | |
715 | 0 | return NULL; |
716 | 0 | } |
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 | 0 | { |
738 | 0 | GHook *ohook = hook; |
739 | |
|
740 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
741 | | |
742 | 0 | if (!hook) |
743 | 0 | return NULL; |
744 | | |
745 | 0 | hook = hook->next; |
746 | 0 | while (hook) |
747 | 0 | { |
748 | 0 | if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook))) |
749 | 0 | { |
750 | 0 | g_hook_ref (hook_list, hook); |
751 | 0 | g_hook_unref (hook_list, ohook); |
752 | | |
753 | 0 | return hook; |
754 | 0 | } |
755 | 0 | hook = hook->next; |
756 | 0 | } |
757 | 0 | g_hook_unref (hook_list, ohook); |
758 | |
|
759 | 0 | return NULL; |
760 | 0 | } |
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 | 0 | { |
775 | 0 | GHook *hook; |
776 | | |
777 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
778 | 0 | g_return_val_if_fail (hook_id > 0, NULL); |
779 | | |
780 | 0 | hook = hook_list->hooks; |
781 | 0 | while (hook) |
782 | 0 | { |
783 | 0 | if (hook->hook_id == hook_id) |
784 | 0 | return hook; |
785 | 0 | hook = hook->next; |
786 | 0 | } |
787 | | |
788 | 0 | return NULL; |
789 | 0 | } |
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 | 0 | { |
821 | 0 | GHook *hook; |
822 | | |
823 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
824 | 0 | g_return_val_if_fail (func != NULL, NULL); |
825 | | |
826 | 0 | hook = hook_list->hooks; |
827 | 0 | while (hook) |
828 | 0 | { |
829 | 0 | GHook *tmp; |
830 | | |
831 | | /* test only non-destroyed hooks */ |
832 | 0 | if (!hook->hook_id) |
833 | 0 | { |
834 | 0 | hook = hook->next; |
835 | 0 | continue; |
836 | 0 | } |
837 | | |
838 | 0 | g_hook_ref (hook_list, hook); |
839 | | |
840 | 0 | if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_ACTIVE (hook))) |
841 | 0 | { |
842 | 0 | g_hook_unref (hook_list, hook); |
843 | | |
844 | 0 | return hook; |
845 | 0 | } |
846 | | |
847 | 0 | tmp = hook->next; |
848 | 0 | g_hook_unref (hook_list, hook); |
849 | 0 | hook = tmp; |
850 | 0 | } |
851 | | |
852 | 0 | return NULL; |
853 | 0 | } |
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 | 0 | { |
872 | 0 | GHook *hook; |
873 | | |
874 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
875 | | |
876 | 0 | hook = hook_list->hooks; |
877 | 0 | while (hook) |
878 | 0 | { |
879 | | /* test only non-destroyed hooks */ |
880 | 0 | if (hook->data == data && |
881 | 0 | hook->hook_id && |
882 | 0 | (!need_valids || G_HOOK_ACTIVE (hook))) |
883 | 0 | return hook; |
884 | | |
885 | 0 | hook = hook->next; |
886 | 0 | } |
887 | | |
888 | 0 | return NULL; |
889 | 0 | } |
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 | 0 | { |
908 | 0 | GHook *hook; |
909 | | |
910 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
911 | 0 | g_return_val_if_fail (func != NULL, NULL); |
912 | | |
913 | 0 | hook = hook_list->hooks; |
914 | 0 | while (hook) |
915 | 0 | { |
916 | | /* test only non-destroyed hooks */ |
917 | 0 | if (hook->func == func && |
918 | 0 | hook->hook_id && |
919 | 0 | (!need_valids || G_HOOK_ACTIVE (hook))) |
920 | 0 | return hook; |
921 | | |
922 | 0 | hook = hook->next; |
923 | 0 | } |
924 | | |
925 | 0 | return NULL; |
926 | 0 | } |
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 | 0 | { |
947 | 0 | GHook *hook; |
948 | | |
949 | 0 | g_return_val_if_fail (hook_list != NULL, NULL); |
950 | 0 | g_return_val_if_fail (func != NULL, NULL); |
951 | | |
952 | 0 | hook = hook_list->hooks; |
953 | 0 | while (hook) |
954 | 0 | { |
955 | | /* test only non-destroyed hooks */ |
956 | 0 | if (hook->data == data && |
957 | 0 | hook->func == func && |
958 | 0 | hook->hook_id && |
959 | 0 | (!need_valids || G_HOOK_ACTIVE (hook))) |
960 | 0 | return hook; |
961 | | |
962 | 0 | hook = hook->next; |
963 | 0 | } |
964 | | |
965 | 0 | return NULL; |
966 | 0 | } |
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 | 0 | { |
992 | 0 | GHook *sibling; |
993 | | |
994 | 0 | g_return_if_fail (hook_list != NULL); |
995 | 0 | g_return_if_fail (hook_list->is_setup); |
996 | 0 | g_return_if_fail (hook != NULL); |
997 | 0 | g_return_if_fail (G_HOOK_IS_UNLINKED (hook)); |
998 | 0 | g_return_if_fail (hook->func != NULL); |
999 | 0 | g_return_if_fail (func != NULL); |
1000 | | |
1001 | | /* first non-destroyed hook */ |
1002 | 0 | sibling = hook_list->hooks; |
1003 | 0 | while (sibling && !sibling->hook_id) |
1004 | 0 | sibling = sibling->next; |
1005 | | |
1006 | 0 | while (sibling) |
1007 | 0 | { |
1008 | 0 | GHook *tmp; |
1009 | | |
1010 | 0 | g_hook_ref (hook_list, sibling); |
1011 | 0 | if (func (hook, sibling) <= 0 && sibling->hook_id) |
1012 | 0 | { |
1013 | 0 | g_hook_unref (hook_list, sibling); |
1014 | 0 | break; |
1015 | 0 | } |
1016 | | |
1017 | | /* next non-destroyed hook */ |
1018 | 0 | tmp = sibling->next; |
1019 | 0 | while (tmp && !tmp->hook_id) |
1020 | 0 | tmp = tmp->next; |
1021 | |
|
1022 | 0 | g_hook_unref (hook_list, sibling); |
1023 | 0 | sibling = tmp; |
1024 | | |
1025 | 0 | } |
1026 | | |
1027 | 0 | g_hook_insert_before (hook_list, sibling, hook); |
1028 | 0 | } |
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 | 0 | { |
1044 | 0 | if (new_hook->hook_id < sibling->hook_id) |
1045 | 0 | return -1; |
1046 | 0 | else if (new_hook->hook_id > sibling->hook_id) |
1047 | 0 | return 1; |
1048 | | |
1049 | 0 | return 0; |
1050 | 0 | } |