properly handle the first valid hook to call, it maybe IN_CALL already.
[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  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 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  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 #include        "glib.h"
23
24
25 /* --- defines --- */
26 #define G_HOOKS_PREALLOC        (16)
27
28
29 /* --- functions --- */
30 void
31 g_hook_list_init (GHookList *hook_list,
32                   guint      hook_size)
33 {
34   g_return_if_fail (hook_list != NULL);
35   g_return_if_fail (hook_size >= sizeof (GHook));
36   
37   hook_list->seq_id = 1;
38   hook_list->hook_size = hook_size;
39   hook_list->is_setup = TRUE;
40   hook_list->hooks = NULL;
41   hook_list->hook_memchunk = g_mem_chunk_new ("GHook Memchunk",
42                                               hook_size,
43                                               hook_size * G_HOOKS_PREALLOC,
44                                               G_ALLOC_AND_FREE);
45 }
46
47 void
48 g_hook_list_clear (GHookList *hook_list)
49 {
50   g_return_if_fail (hook_list != NULL);
51   
52   if (hook_list->is_setup)
53     {
54       GHook *hook;
55       
56       hook_list->is_setup = FALSE;
57       
58       hook = hook_list->hooks;
59       if (!hook)
60         {
61           g_mem_chunk_destroy (hook_list->hook_memchunk);
62           hook_list->hook_memchunk = NULL;
63         }
64       else
65         do
66           {
67             register GHook *tmp;
68             
69             g_hook_ref (hook_list, hook);
70             g_hook_destroy_link (hook_list, hook);
71             tmp = hook->next;
72             g_hook_unref (hook_list, hook);
73             hook = tmp;
74           }
75         while (hook);
76     }
77 }
78
79 GHook*
80 g_hook_alloc (GHookList *hook_list)
81 {
82   GHook *hook;
83   
84   g_return_val_if_fail (hook_list != NULL, NULL);
85   g_return_val_if_fail (hook_list->is_setup, NULL);
86   
87   hook = g_chunk_new0 (GHook, hook_list->hook_memchunk);
88   hook->data = NULL;
89   hook->next = NULL;
90   hook->prev = NULL;
91   hook->flags = G_HOOK_ACTIVE;
92   hook->ref_count = 0;
93   hook->hook_id = 0;
94   hook->func = NULL;
95   hook->destroy = NULL;
96   
97   return hook;
98 }
99
100 void
101 g_hook_free (GHookList *hook_list,
102              GHook     *hook)
103 {
104   g_return_if_fail (hook_list != NULL);
105   g_return_if_fail (hook_list->is_setup);
106   g_return_if_fail (hook != NULL);
107   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
108   
109   g_chunk_free (hook, hook_list->hook_memchunk);
110 }
111
112 void
113 g_hook_destroy_link (GHookList *hook_list,
114                      GHook     *hook)
115 {
116   g_return_if_fail (hook_list != NULL);
117   g_return_if_fail (hook != NULL);
118   
119   if (hook->hook_id)
120     {
121       hook->hook_id = 0;
122       hook->flags &= ~G_HOOK_ACTIVE;
123       if (hook->destroy)
124         {
125           register GDestroyNotify destroy;
126           
127           destroy = hook->destroy;
128           hook->destroy = NULL;
129           destroy (hook->data);
130         }
131       g_hook_unref (hook_list, hook); /* counterpart to g_hook_insert_before */
132     }
133 }
134
135 gboolean
136 g_hook_destroy (GHookList   *hook_list,
137                 guint        hook_id)
138 {
139   GHook *hook;
140   
141   g_return_val_if_fail (hook_list != NULL, FALSE);
142   g_return_val_if_fail (hook_id > 0, FALSE);
143   
144   hook = g_hook_get (hook_list, hook_id);
145   if (hook)
146     {
147       g_hook_destroy_link (hook_list, hook);
148       return TRUE;
149     }
150   
151   return FALSE;
152 }
153
154 void
155 g_hook_unref (GHookList *hook_list,
156               GHook     *hook)
157 {
158   g_return_if_fail (hook_list != NULL);
159   g_return_if_fail (hook != NULL);
160   g_return_if_fail (hook->ref_count > 0);
161   
162   hook->ref_count--;
163   if (!hook->ref_count)
164     {
165       g_return_if_fail (hook->hook_id == 0);
166       g_return_if_fail (!G_HOOK_IS_IN_CALL (hook));
167       
168       if (hook->prev)
169         hook->prev->next = hook->next;
170       else
171         hook_list->hooks = hook->next;
172       if (hook->next)
173         {
174           hook->next->prev = hook->prev;
175           hook->next = NULL;
176         }
177       hook->prev = NULL;
178       
179       g_chunk_free (hook, hook_list->hook_memchunk);
180       
181       if (!hook_list->hooks &&
182           !hook_list->is_setup)
183         {
184           g_mem_chunk_destroy (hook_list->hook_memchunk);
185           hook_list->hook_memchunk = NULL;
186         }
187     }
188 }
189
190 void
191 g_hook_ref (GHookList *hook_list,
192             GHook     *hook)
193 {
194   g_return_if_fail (hook_list != NULL);
195   g_return_if_fail (hook != NULL);
196   g_return_if_fail (hook->ref_count > 0);
197   
198   hook->ref_count++;
199 }
200
201 void
202 g_hook_prepend (GHookList *hook_list,
203                 GHook     *hook)
204 {
205   g_return_if_fail (hook_list != NULL);
206   
207   g_hook_insert_before (hook_list, hook_list->hooks, hook);
208 }
209
210 void
211 g_hook_insert_before (GHookList *hook_list,
212                       GHook     *sibling,
213                       GHook     *hook)
214 {
215   g_return_if_fail (hook_list != NULL);
216   g_return_if_fail (hook_list->is_setup);
217   g_return_if_fail (hook != NULL);
218   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
219   g_return_if_fail (hook->func != NULL);
220   
221   hook->hook_id = hook_list->seq_id++;
222   hook->ref_count = 1; /* counterpart to g_hook_destroy_link */
223   
224   if (sibling)
225     {
226       if (sibling->prev)
227         {
228           hook->prev = sibling->prev;
229           hook->prev->next = hook;
230           hook->next = sibling;
231           sibling->prev = hook;
232         }
233       else
234         {
235           hook_list->hooks = hook;
236           hook->next = sibling;
237           sibling->prev = hook;
238         }
239     }
240   else
241     {
242       if (hook_list->hooks)
243         {
244           sibling = hook_list->hooks;
245           while (sibling->next)
246             sibling = sibling->next;
247           hook->prev = sibling;
248           sibling->next = hook;
249         }
250       else
251         hook_list->hooks = hook;
252     }
253 }
254
255 void
256 g_hook_list_invoke (GHookList *hook_list,
257                     gboolean   may_recurse)
258 {
259   GHook *hook;
260   
261   g_return_if_fail (hook_list != NULL);
262   g_return_if_fail (hook_list->is_setup);
263
264   if (may_recurse)
265     hook = g_hook_first_valid (hook_list);
266   else
267     do
268       hook = g_hook_first_valid (hook_list);
269     while (hook && G_HOOK_IS_IN_CALL (hook));
270   while (hook)
271     {
272       register GHook *tmp;
273       register GHookFunc func;
274       gboolean was_in_call;
275       
276       g_hook_ref (hook_list, hook);
277       func = hook->func;
278       
279       was_in_call = G_HOOK_IS_IN_CALL (hook);
280       hook->flags |= G_HOOK_IN_CALL;
281       func (hook->data);
282       if (!was_in_call)
283         hook->flags &= ~G_HOOK_IN_CALL;
284       
285       if (may_recurse)
286         tmp = g_hook_next_valid (hook);
287       else
288         do
289           tmp = g_hook_next_valid (hook);
290         while (tmp && G_HOOK_IS_IN_CALL (tmp));
291       
292       g_hook_unref (hook_list, hook);
293       hook = tmp;
294     }
295 }
296
297 void
298 g_hook_list_invoke_check (GHookList *hook_list,
299                           gboolean   may_recurse)
300 {
301   GHook *hook;
302   
303   g_return_if_fail (hook_list != NULL);
304   g_return_if_fail (hook_list->is_setup);
305   
306   if (may_recurse)
307     hook = g_hook_first_valid (hook_list);
308   else
309     do
310       hook = g_hook_first_valid (hook_list);
311     while (hook && G_HOOK_IS_IN_CALL (hook));
312   while (hook)
313     {
314       register GHook *tmp;
315       register GHookCheckFunc func;
316       gboolean was_in_call;
317       register gboolean need_destroy;
318       
319       g_hook_ref (hook_list, hook);
320       func = hook->func;
321       
322       was_in_call = G_HOOK_IS_IN_CALL (hook);
323       hook->flags |= G_HOOK_IN_CALL;
324       need_destroy = !func (hook->data);
325       if (!was_in_call)
326         hook->flags &= ~G_HOOK_IN_CALL;
327       if (need_destroy)
328         g_hook_destroy_link (hook_list, hook);
329       
330       if (may_recurse)
331         tmp = g_hook_next_valid (hook);
332       else
333         do
334           tmp = g_hook_next_valid (hook);
335         while (tmp && G_HOOK_IS_IN_CALL (tmp));
336       
337       g_hook_unref (hook_list, hook);
338       hook = tmp;
339     }
340 }
341
342 void
343 g_hook_list_marshal (GHookList               *hook_list,
344                      gboolean                 may_recurse,
345                      GHookMarshaller          marshaller,
346                      gpointer                 data)
347 {
348   GHook *hook;
349   
350   g_return_if_fail (hook_list != NULL);
351   g_return_if_fail (hook_list->is_setup);
352   g_return_if_fail (marshaller != NULL);
353   
354   if (may_recurse)
355     hook = g_hook_first_valid (hook_list);
356   else
357     do
358       hook = g_hook_first_valid (hook_list);
359     while (hook && G_HOOK_IS_IN_CALL (hook));
360   while (hook)
361     {
362       register GHook *tmp;
363       gboolean was_in_call;
364       
365       g_hook_ref (hook_list, hook);
366       
367       was_in_call = G_HOOK_IS_IN_CALL (hook);
368       hook->flags |= G_HOOK_IN_CALL;
369       marshaller (hook, data);
370       if (!was_in_call)
371         hook->flags &= ~G_HOOK_IN_CALL;
372       
373       if (may_recurse)
374         tmp = g_hook_next_valid (hook);
375       else
376         do
377           tmp = g_hook_next_valid (hook);
378         while (tmp && G_HOOK_IS_IN_CALL (tmp));
379       
380       g_hook_unref (hook_list, hook);
381       hook = tmp;
382     }
383 }
384
385 GHook*
386 g_hook_first_valid (GHookList *hook_list)
387 {
388   g_return_val_if_fail (hook_list != NULL, NULL);
389   
390   if (hook_list->is_setup)
391     {
392       GHook *hook;
393       
394       hook = hook_list->hooks;
395       if (hook)
396         {
397           if (G_HOOK_IS_VALID (hook))
398             return hook;
399           else
400             return g_hook_next_valid (hook);
401         }
402     }
403   
404   return NULL;
405 }
406
407 GHook*
408 g_hook_next_valid (GHook *hook)
409 {
410   if (!hook)
411     return NULL;
412   
413   hook = hook->next;
414   while (hook)
415     {
416       if (G_HOOK_IS_VALID (hook))
417         return hook;
418       hook = hook->next;
419     }
420   
421   return NULL;
422 }
423
424 GHook*
425 g_hook_get (GHookList *hook_list,
426             guint      hook_id)
427 {
428   GHook *hook;
429   
430   g_return_val_if_fail (hook_list != NULL, NULL);
431   g_return_val_if_fail (hook_id > 0, NULL);
432   
433   hook = hook_list->hooks;
434   while (hook)
435     {
436       if (hook->hook_id == hook_id)
437         return hook;
438       hook = hook->next;
439     }
440   
441   return NULL;
442 }
443
444 GHook*
445 g_hook_find (GHookList    *hook_list,
446              gboolean      need_valids,
447              GHookFindFunc func,
448              gpointer      data)
449 {
450   GHook *hook;
451   
452   g_return_val_if_fail (hook_list != NULL, NULL);
453   g_return_val_if_fail (func != NULL, NULL);
454   
455   hook = g_hook_first_valid (hook_list);
456   while (hook)
457     {
458       register GHook *tmp;
459       
460       g_hook_ref (hook_list, hook);
461       
462       if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_IS_ACTIVE (hook)))
463         {
464           g_hook_unref (hook_list, hook);
465           
466           return hook;
467         }
468       tmp = g_hook_next_valid (hook);
469       g_hook_unref (hook_list, hook);
470       hook = tmp;
471     }
472   
473   return NULL;
474 }
475
476 GHook*
477 g_hook_find_data (GHookList *hook_list,
478                   gboolean   need_valids,
479                   gpointer   data)
480 {
481   register GHook *hook;
482   
483   g_return_val_if_fail (hook_list != NULL, NULL);
484   
485   hook = g_hook_first_valid (hook_list);
486   while (hook)
487     {
488       if (hook->data == data && hook->hook_id && (!need_valids || G_HOOK_IS_ACTIVE (hook)))
489         return hook;
490       
491       hook = g_hook_next_valid (hook);
492     }
493   
494   return NULL;
495 }
496
497 GHook*
498 g_hook_find_func (GHookList *hook_list,
499                   gboolean   need_valids,
500                   gpointer   func)
501 {
502   register GHook *hook;
503   
504   g_return_val_if_fail (hook_list != NULL, NULL);
505   g_return_val_if_fail (func != NULL, NULL);
506   
507   hook = g_hook_first_valid (hook_list);
508   while (hook)
509     {
510       if (hook->func == func && hook->hook_id && (!need_valids || G_HOOK_IS_ACTIVE (hook)))
511         return hook;
512       
513       hook = g_hook_next_valid (hook);
514     }
515   
516   return NULL;
517 }
518
519 GHook*
520 g_hook_find_func_data (GHookList *hook_list,
521                        gboolean   need_valids,
522                        gpointer   func,
523                        gpointer   data)
524 {
525   register GHook *hook;
526   
527   g_return_val_if_fail (hook_list != NULL, NULL);
528   g_return_val_if_fail (func != NULL, NULL);
529   
530   hook = g_hook_first_valid (hook_list);
531   while (hook)
532     {
533       if (hook->data == data && hook->func == func && hook->hook_id && (!need_valids || G_HOOK_IS_ACTIVE (hook)))
534         return hook;
535       
536       hook = g_hook_next_valid (hook);
537     }
538   
539   return NULL;
540 }
541
542 void
543 g_hook_insert_sorted (GHookList       *hook_list,
544                       GHook           *hook,
545                       GHookCompareFunc func)
546 {
547   GHook *sibling;
548   
549   g_return_if_fail (hook_list != NULL);
550   g_return_if_fail (hook_list->is_setup);
551   g_return_if_fail (hook != NULL);
552   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
553   g_return_if_fail (hook->func != NULL);
554   g_return_if_fail (func != NULL);
555   
556   sibling = g_hook_first_valid (hook_list);
557   
558   while (sibling)
559     {
560       register GHook *tmp;
561       
562       g_hook_ref (hook_list, sibling);
563       if (func (hook, sibling) <= 0 && sibling->hook_id)
564         {
565           g_hook_unref (hook_list, sibling);
566           break;
567         }
568       tmp = g_hook_next_valid (sibling);
569       g_hook_unref (hook_list, sibling);
570       sibling = tmp;
571     }
572   
573   g_hook_insert_before (hook_list, sibling, hook);
574 }
575
576 gint
577 g_hook_compare_ids (GHook *new_hook,
578                     GHook *sibling)
579 {
580   return ((glong) new_hook->hook_id) - ((glong) sibling->hook_id);
581 }