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