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