Merge remote branch 'gvdb/master'
[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 Lesser 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  * 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, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
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
40
41 /* --- functions --- */
42 static void
43 default_finalize_hook (GHookList *hook_list,
44                        GHook     *hook)
45 {
46   GDestroyNotify destroy = hook->destroy;
47
48   if (destroy)
49     {
50       hook->destroy = NULL;
51       destroy (hook->data);
52     }
53 }
54
55 void
56 g_hook_list_init (GHookList *hook_list,
57                   guint      hook_size)
58 {
59   g_return_if_fail (hook_list != NULL);
60   g_return_if_fail (hook_size >= sizeof (GHook));
61   
62   hook_list->seq_id = 1;
63   hook_list->hook_size = hook_size;
64   hook_list->is_setup = TRUE;
65   hook_list->hooks = NULL;
66   hook_list->dummy3 = NULL;
67   hook_list->finalize_hook = default_finalize_hook;
68   hook_list->dummy[0] = NULL;
69   hook_list->dummy[1] = NULL;
70 }
71
72 void
73 g_hook_list_clear (GHookList *hook_list)
74 {
75   g_return_if_fail (hook_list != NULL);
76   
77   if (hook_list->is_setup)
78     {
79       GHook *hook;
80       
81       hook_list->is_setup = FALSE;
82       
83       hook = hook_list->hooks;
84       if (!hook)
85         {
86           /* destroy hook_list->hook_memchunk */
87         }
88       else
89         do
90           {
91             GHook *tmp;
92             
93             g_hook_ref (hook_list, hook);
94             g_hook_destroy_link (hook_list, hook);
95             tmp = hook->next;
96             g_hook_unref (hook_list, hook);
97             hook = tmp;
98           }
99         while (hook);
100     }
101 }
102
103 GHook*
104 g_hook_alloc (GHookList *hook_list)
105 {
106   GHook *hook;
107   
108   g_return_val_if_fail (hook_list != NULL, NULL);
109   g_return_val_if_fail (hook_list->is_setup, NULL);
110   
111   hook = g_slice_alloc0 (hook_list->hook_size);
112   hook->data = NULL;
113   hook->next = NULL;
114   hook->prev = NULL;
115   hook->flags = G_HOOK_FLAG_ACTIVE;
116   hook->ref_count = 0;
117   hook->hook_id = 0;
118   hook->func = NULL;
119   hook->destroy = NULL;
120   
121   return hook;
122 }
123
124 void
125 g_hook_free (GHookList *hook_list,
126              GHook     *hook)
127 {
128   g_return_if_fail (hook_list != NULL);
129   g_return_if_fail (hook_list->is_setup);
130   g_return_if_fail (hook != NULL);
131   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
132   g_return_if_fail (!G_HOOK_IN_CALL (hook));
133
134   if(hook_list->finalize_hook != NULL)
135       hook_list->finalize_hook (hook_list, hook);
136   g_slice_free1 (hook_list->hook_size, hook);
137 }
138
139 void
140 g_hook_destroy_link (GHookList *hook_list,
141                      GHook     *hook)
142 {
143   g_return_if_fail (hook_list != NULL);
144   g_return_if_fail (hook != NULL);
145
146   hook->flags &= ~G_HOOK_FLAG_ACTIVE;
147   if (hook->hook_id)
148     {
149       hook->hook_id = 0;
150       g_hook_unref (hook_list, hook); /* counterpart to g_hook_insert_before */
151     }
152 }
153
154 gboolean
155 g_hook_destroy (GHookList   *hook_list,
156                 gulong       hook_id)
157 {
158   GHook *hook;
159   
160   g_return_val_if_fail (hook_list != NULL, FALSE);
161   g_return_val_if_fail (hook_id > 0, FALSE);
162   
163   hook = g_hook_get (hook_list, hook_id);
164   if (hook)
165     {
166       g_hook_destroy_link (hook_list, hook);
167       return TRUE;
168     }
169   
170   return FALSE;
171 }
172
173 void
174 g_hook_unref (GHookList *hook_list,
175               GHook     *hook)
176 {
177   g_return_if_fail (hook_list != NULL);
178   g_return_if_fail (hook != NULL);
179   g_return_if_fail (hook->ref_count > 0);
180   
181   hook->ref_count--;
182   if (!hook->ref_count)
183     {
184       g_return_if_fail (hook->hook_id == 0);
185       g_return_if_fail (!G_HOOK_IN_CALL (hook));
186
187       if (hook->prev)
188         hook->prev->next = hook->next;
189       else
190         hook_list->hooks = hook->next;
191       if (hook->next)
192         {
193           hook->next->prev = hook->prev;
194           hook->next = NULL;
195         }
196       hook->prev = NULL;
197
198       if (!hook_list->is_setup)
199         {
200           hook_list->is_setup = TRUE;
201           g_hook_free (hook_list, hook);
202           hook_list->is_setup = FALSE;
203       
204           if (!hook_list->hooks)
205             {
206               /* destroy hook_list->hook_memchunk */
207             }
208         }
209       else
210         g_hook_free (hook_list, hook);
211     }
212 }
213
214 GHook *
215 g_hook_ref (GHookList *hook_list,
216             GHook     *hook)
217 {
218   g_return_val_if_fail (hook_list != NULL, NULL);
219   g_return_val_if_fail (hook != NULL, NULL);
220   g_return_val_if_fail (hook->ref_count > 0, NULL);
221   
222   hook->ref_count++;
223
224   return hook;
225 }
226
227 void
228 g_hook_prepend (GHookList *hook_list,
229                 GHook     *hook)
230 {
231   g_return_if_fail (hook_list != NULL);
232   
233   g_hook_insert_before (hook_list, hook_list->hooks, hook);
234 }
235
236 void
237 g_hook_insert_before (GHookList *hook_list,
238                       GHook     *sibling,
239                       GHook     *hook)
240 {
241   g_return_if_fail (hook_list != NULL);
242   g_return_if_fail (hook_list->is_setup);
243   g_return_if_fail (hook != NULL);
244   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
245   g_return_if_fail (hook->ref_count == 0);
246   
247   hook->hook_id = hook_list->seq_id++;
248   hook->ref_count = 1; /* counterpart to g_hook_destroy_link */
249   
250   if (sibling)
251     {
252       if (sibling->prev)
253         {
254           hook->prev = sibling->prev;
255           hook->prev->next = hook;
256           hook->next = sibling;
257           sibling->prev = hook;
258         }
259       else
260         {
261           hook_list->hooks = hook;
262           hook->next = sibling;
263           sibling->prev = hook;
264         }
265     }
266   else
267     {
268       if (hook_list->hooks)
269         {
270           sibling = hook_list->hooks;
271           while (sibling->next)
272             sibling = sibling->next;
273           hook->prev = sibling;
274           sibling->next = hook;
275         }
276       else
277         hook_list->hooks = hook;
278     }
279 }
280
281 void
282 g_hook_list_invoke (GHookList *hook_list,
283                     gboolean   may_recurse)
284 {
285   GHook *hook;
286   
287   g_return_if_fail (hook_list != NULL);
288   g_return_if_fail (hook_list->is_setup);
289
290   hook = g_hook_first_valid (hook_list, may_recurse);
291   while (hook)
292     {
293       GHookFunc func;
294       gboolean was_in_call;
295       
296       func = (GHookFunc) hook->func;
297       
298       was_in_call = G_HOOK_IN_CALL (hook);
299       hook->flags |= G_HOOK_FLAG_IN_CALL;
300       func (hook->data);
301       if (!was_in_call)
302         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
303       
304       hook = g_hook_next_valid (hook_list, hook, may_recurse);
305     }
306 }
307
308 void
309 g_hook_list_invoke_check (GHookList *hook_list,
310                           gboolean   may_recurse)
311 {
312   GHook *hook;
313   
314   g_return_if_fail (hook_list != NULL);
315   g_return_if_fail (hook_list->is_setup);
316   
317   hook = g_hook_first_valid (hook_list, may_recurse);
318   while (hook)
319     {
320       GHookCheckFunc func;
321       gboolean was_in_call;
322       gboolean need_destroy;
323       
324       func = (GHookCheckFunc) hook->func;
325       
326       was_in_call = G_HOOK_IN_CALL (hook);
327       hook->flags |= G_HOOK_FLAG_IN_CALL;
328       need_destroy = !func (hook->data);
329       if (!was_in_call)
330         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
331       if (need_destroy)
332         g_hook_destroy_link (hook_list, hook);
333       
334       hook = g_hook_next_valid (hook_list, hook, may_recurse);
335     }
336 }
337
338 void
339 g_hook_list_marshal_check (GHookList           *hook_list,
340                            gboolean             may_recurse,
341                            GHookCheckMarshaller marshaller,
342                            gpointer             data)
343 {
344   GHook *hook;
345   
346   g_return_if_fail (hook_list != NULL);
347   g_return_if_fail (hook_list->is_setup);
348   g_return_if_fail (marshaller != NULL);
349   
350   hook = g_hook_first_valid (hook_list, may_recurse);
351   while (hook)
352     {
353       gboolean was_in_call;
354       gboolean need_destroy;
355       
356       was_in_call = G_HOOK_IN_CALL (hook);
357       hook->flags |= G_HOOK_FLAG_IN_CALL;
358       need_destroy = !marshaller (hook, data);
359       if (!was_in_call)
360         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
361       if (need_destroy)
362         g_hook_destroy_link (hook_list, hook);
363       
364       hook = g_hook_next_valid (hook_list, hook, may_recurse);
365     }
366 }
367
368 void
369 g_hook_list_marshal (GHookList               *hook_list,
370                      gboolean                 may_recurse,
371                      GHookMarshaller          marshaller,
372                      gpointer                 data)
373 {
374   GHook *hook;
375   
376   g_return_if_fail (hook_list != NULL);
377   g_return_if_fail (hook_list->is_setup);
378   g_return_if_fail (marshaller != NULL);
379   
380   hook = g_hook_first_valid (hook_list, may_recurse);
381   while (hook)
382     {
383       gboolean was_in_call;
384       
385       was_in_call = G_HOOK_IN_CALL (hook);
386       hook->flags |= G_HOOK_FLAG_IN_CALL;
387       marshaller (hook, data);
388       if (!was_in_call)
389         hook->flags &= ~G_HOOK_FLAG_IN_CALL;
390       
391       hook = g_hook_next_valid (hook_list, hook, may_recurse);
392     }
393 }
394
395 GHook*
396 g_hook_first_valid (GHookList *hook_list,
397                     gboolean   may_be_in_call)
398 {
399   g_return_val_if_fail (hook_list != NULL, NULL);
400   
401   if (hook_list->is_setup)
402     {
403       GHook *hook;
404       
405       hook = hook_list->hooks;
406       if (hook)
407         {
408           g_hook_ref (hook_list, hook);
409           if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
410             return hook;
411           else
412             return g_hook_next_valid (hook_list, hook, may_be_in_call);
413         }
414     }
415   
416   return NULL;
417 }
418
419 GHook*
420 g_hook_next_valid (GHookList *hook_list,
421                    GHook     *hook,
422                    gboolean   may_be_in_call)
423 {
424   GHook *ohook = hook;
425
426   g_return_val_if_fail (hook_list != NULL, NULL);
427
428   if (!hook)
429     return NULL;
430   
431   hook = hook->next;
432   while (hook)
433     {
434       if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
435         {
436           g_hook_ref (hook_list, hook);
437           g_hook_unref (hook_list, ohook);
438           
439           return hook;
440         }
441       hook = hook->next;
442     }
443   g_hook_unref (hook_list, ohook);
444
445   return NULL;
446 }
447
448 GHook*
449 g_hook_get (GHookList *hook_list,
450             gulong     hook_id)
451 {
452   GHook *hook;
453   
454   g_return_val_if_fail (hook_list != NULL, NULL);
455   g_return_val_if_fail (hook_id > 0, NULL);
456   
457   hook = hook_list->hooks;
458   while (hook)
459     {
460       if (hook->hook_id == hook_id)
461         return hook;
462       hook = hook->next;
463     }
464   
465   return NULL;
466 }
467
468 GHook*
469 g_hook_find (GHookList    *hook_list,
470              gboolean      need_valids,
471              GHookFindFunc func,
472              gpointer      data)
473 {
474   GHook *hook;
475   
476   g_return_val_if_fail (hook_list != NULL, NULL);
477   g_return_val_if_fail (func != NULL, NULL);
478   
479   hook = hook_list->hooks;
480   while (hook)
481     {
482       GHook *tmp;
483
484       /* test only non-destroyed hooks */
485       if (!hook->hook_id)
486         {
487           hook = hook->next;
488           continue;
489         }
490       
491       g_hook_ref (hook_list, hook);
492       
493       if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_ACTIVE (hook)))
494         {
495           g_hook_unref (hook_list, hook);
496           
497           return hook;
498         }
499
500       tmp = hook->next;
501       g_hook_unref (hook_list, hook);
502       hook = tmp;
503     }
504   
505   return NULL;
506 }
507
508 GHook*
509 g_hook_find_data (GHookList *hook_list,
510                   gboolean   need_valids,
511                   gpointer   data)
512 {
513   GHook *hook;
514   
515   g_return_val_if_fail (hook_list != NULL, NULL);
516   
517   hook = hook_list->hooks;
518   while (hook)
519     {
520       /* test only non-destroyed hooks */
521       if (hook->data == data &&
522           hook->hook_id &&
523           (!need_valids || G_HOOK_ACTIVE (hook)))
524         return hook;
525
526       hook = hook->next;
527     }
528   
529   return NULL;
530 }
531
532 GHook*
533 g_hook_find_func (GHookList *hook_list,
534                   gboolean   need_valids,
535                   gpointer   func)
536 {
537   GHook *hook;
538   
539   g_return_val_if_fail (hook_list != NULL, NULL);
540   g_return_val_if_fail (func != NULL, NULL);
541   
542   hook = hook_list->hooks;
543   while (hook)
544     {
545       /* test only non-destroyed hooks */
546       if (hook->func == func &&
547           hook->hook_id &&
548           (!need_valids || G_HOOK_ACTIVE (hook)))
549         return hook;
550
551       hook = hook->next;
552     }
553   
554   return NULL;
555 }
556
557 GHook*
558 g_hook_find_func_data (GHookList *hook_list,
559                        gboolean   need_valids,
560                        gpointer   func,
561                        gpointer   data)
562 {
563   GHook *hook;
564   
565   g_return_val_if_fail (hook_list != NULL, NULL);
566   g_return_val_if_fail (func != NULL, NULL);
567   
568   hook = hook_list->hooks;
569   while (hook)
570     {
571       /* test only non-destroyed hooks */
572       if (hook->data == data &&
573           hook->func == func &&
574           hook->hook_id &&
575           (!need_valids || G_HOOK_ACTIVE (hook)))
576         return hook;
577
578       hook = hook->next;
579     }
580   
581   return NULL;
582 }
583
584 void
585 g_hook_insert_sorted (GHookList       *hook_list,
586                       GHook           *hook,
587                       GHookCompareFunc func)
588 {
589   GHook *sibling;
590   
591   g_return_if_fail (hook_list != NULL);
592   g_return_if_fail (hook_list->is_setup);
593   g_return_if_fail (hook != NULL);
594   g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
595   g_return_if_fail (hook->func != NULL);
596   g_return_if_fail (func != NULL);
597
598   /* first non-destroyed hook */
599   sibling = hook_list->hooks;
600   while (sibling && !sibling->hook_id)
601     sibling = sibling->next;
602   
603   while (sibling)
604     {
605       GHook *tmp;
606       
607       g_hook_ref (hook_list, sibling);
608       if (func (hook, sibling) <= 0 && sibling->hook_id)
609         {
610           g_hook_unref (hook_list, sibling);
611           break;
612         }
613
614       /* next non-destroyed hook */
615       tmp = sibling->next;
616       while (tmp && !tmp->hook_id)
617         tmp = tmp->next;
618
619       g_hook_unref (hook_list, sibling);
620       sibling = tmp;
621     }
622   
623   g_hook_insert_before (hook_list, sibling, hook);
624 }
625
626 gint
627 g_hook_compare_ids (GHook *new_hook,
628                     GHook *sibling)
629 {
630   if (new_hook->hook_id < sibling->hook_id)
631     return -1;
632   else if (new_hook->hook_id > sibling->hook_id)
633     return 1;
634   
635   return 0;
636 }