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