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