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