cast the key argument to g_hash_table_insert to a gpointer to avoid compiler warnings
[platform/upstream/evolution-data-server.git] / camel / camel-object.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-object.c: Base class for Camel */
3
4 /*
5  * Author:
6  *  Dan Winship <danw@ximian.com>
7  *
8  * Copyright 2000 Ximian, Inc. (www.ximian.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include "camel-object.h"
31
32 #ifdef ENABLE_THREADS
33 #include <pthread.h>
34 #include <e-util/e-msgport.h>
35 #endif
36
37 /* I just mashed the keyboard for these... */
38 #define CAMEL_OBJECT_MAGIC_VALUE           0x77A344EF
39 #define CAMEL_OBJECT_CLASS_MAGIC_VALUE     0xEE26A990
40 #define CAMEL_OBJECT_FINALIZED_VALUE       0x84AC3656
41 #define CAMEL_OBJECT_CLASS_FINALIZED_VALUE 0x7621ABCD
42
43 #define DEFAULT_PREALLOCS 8
44
45 #define BAST_CASTARD 1          /* Define to return NULL when casts fail */
46
47 #define NULL_PREP_VALUE ((gpointer)make_global_classfuncs)      /* See camel_object_class_declare_event */
48
49 /* ** Quickie type system ************************************************* */
50
51 typedef struct _CamelTypeInfo
52 {
53         CamelType self;
54         CamelType parent;
55         const gchar *name;
56
57         size_t instance_size;
58         GMemChunk *instance_chunk;
59         CamelObjectInitFunc instance_init;
60         CamelObjectFinalizeFunc instance_finalize;
61         GList *free_instances;
62
63         size_t classfuncs_size;
64         CamelObjectClassInitFunc class_init;
65         CamelObjectClassFinalizeFunc class_finalize;
66         CamelObjectClass *global_classfuncs;
67 }
68 CamelTypeInfo;
69
70 /* A 'locked' hooklist, that is only allocated on demand */
71 typedef struct _CamelHookList {
72         EMutex *lock;
73
74         unsigned int depth:30;  /* recursive event depth */
75         unsigned int flags:2;   /* flags, see below */
76
77         unsigned int list_length;
78         struct _CamelHookPair *list;
79 } CamelHookList;
80
81 #define CAMEL_HOOK_PAIR_REMOVED (1<<0)
82
83 /* a 'hook pair', actually a hook tuple, we just store all hooked events in the same list,
84    and just comapre as we go, rather than storing separate lists for each hook type
85
86    the name field just points directly to the key field in the class's preplist hashtable.
87    This way we can just use a direct pointer compare when scanning it, and also saves
88    copying the string */
89 typedef struct _CamelHookPair
90 {
91         struct _CamelHookPair *next; /* next MUST be the first member */
92
93         unsigned int flags;     /* removed, etc */
94
95         const char *name;       /* points to the key field in the classes preplist, static memory */
96         CamelObjectEventHookFunc func;
97         void *data;
98 } CamelHookPair;
99
100 /* ************************************************************************ */
101
102 static void camel_type_lock_up (void);
103 static void camel_type_lock_down (void);
104
105 static void obj_init (CamelObject * obj);
106 static void obj_finalize (CamelObject * obj);
107 static void obj_class_init (CamelObjectClass * class);
108 static void obj_class_finalize (CamelObjectClass * class);
109
110 static gboolean shared_is_of_type (CamelObjectShared * sh, CamelType ctype,
111                                    gboolean is_obj);
112 static void make_global_classfuncs (CamelTypeInfo * type_info);
113
114 static void camel_object_free_hooks(CamelObject *o);
115
116 /* ************************************************************************ */
117
118 G_LOCK_DEFINE_STATIC (type_system);
119 G_LOCK_DEFINE_STATIC (type_system_level);
120 static GPrivate *type_system_locklevel = NULL;
121
122 G_LOCK_DEFINE_STATIC (refcount);
123
124 static gboolean type_system_initialized = FALSE;
125 static GHashTable *ctype_to_typeinfo = NULL;
126 static GHashTable *name_to_typeinfo = NULL;
127 static const CamelType camel_object_type = 1;
128 static CamelType cur_max_type = CAMEL_INVALID_TYPE;
129
130 /* ************************************************************************ */
131
132 #define LOCK_VAL (GPOINTER_TO_INT (g_private_get (type_system_locklevel)))
133 #define LOCK_SET( val ) g_private_set (type_system_locklevel, GINT_TO_POINTER (val))
134
135 static void
136 camel_type_lock_up (void)
137 {
138         G_LOCK (type_system_level);
139
140         if (type_system_locklevel == NULL)
141                 type_system_locklevel = g_private_new (GINT_TO_POINTER (0));
142
143         if (LOCK_VAL == 0) {
144                 G_UNLOCK (type_system_level);
145                 G_LOCK (type_system);
146                 G_LOCK (type_system_level);
147         }
148
149         LOCK_SET (LOCK_VAL + 1);
150
151         G_UNLOCK (type_system_level);
152 }
153
154 static void
155 camel_type_lock_down (void)
156 {
157         G_LOCK (type_system_level);
158
159         if (type_system_locklevel == NULL) {
160                 g_warning
161                         ("camel_type_lock_down: lock down before a lock up?");
162                 type_system_locklevel = g_private_new (GINT_TO_POINTER (0));
163                 G_UNLOCK (type_system_level);
164                 return;
165         }
166
167         LOCK_SET (LOCK_VAL - 1);
168
169         if (LOCK_VAL == 0)
170                 G_UNLOCK (type_system);
171
172         G_UNLOCK (type_system_level);
173 }
174
175 void
176 camel_type_init (void)
177 {
178         CamelTypeInfo *obj_info;
179
180         camel_type_lock_up ();
181
182         if (type_system_initialized) {
183                 g_warning ("camel_type_init: type system already initialized.");
184                 camel_type_lock_down ();
185                 return;
186         }
187
188         type_system_initialized = TRUE;
189         ctype_to_typeinfo = g_hash_table_new (g_direct_hash, g_direct_equal);
190         name_to_typeinfo = g_hash_table_new (g_str_hash, g_str_equal);
191         
192         obj_info = g_new (CamelTypeInfo, 1);
193         obj_info->self = camel_object_type;
194         obj_info->parent = CAMEL_INVALID_TYPE;
195         obj_info->name = "CamelObject";
196
197         obj_info->instance_size = sizeof (CamelObject);
198         obj_info->instance_chunk =
199                 g_mem_chunk_create (CamelObject, DEFAULT_PREALLOCS,
200                                     G_ALLOC_ONLY);
201         obj_info->instance_init = obj_init;
202         obj_info->instance_finalize = obj_finalize;
203         obj_info->free_instances = NULL;
204
205         obj_info->classfuncs_size = sizeof (CamelObjectClass);
206         obj_info->class_init = obj_class_init;
207         obj_info->class_finalize = obj_class_finalize;
208
209         g_hash_table_insert (ctype_to_typeinfo,
210                              GINT_TO_POINTER (CAMEL_INVALID_TYPE), NULL);
211         g_hash_table_insert (ctype_to_typeinfo,
212                              GINT_TO_POINTER (camel_object_type), obj_info);
213         g_hash_table_insert (name_to_typeinfo, (gpointer) obj_info->name, obj_info);
214         
215         /* Sigh. Ugly */
216         make_global_classfuncs (obj_info);
217
218         cur_max_type = camel_object_type;
219
220         camel_type_lock_down ();
221 }
222
223 CamelType
224 camel_type_register (CamelType parent, const gchar * name,
225                      size_t instance_size, size_t classfuncs_size,
226                      CamelObjectClassInitFunc class_init,
227                      CamelObjectClassFinalizeFunc class_finalize,
228                      CamelObjectInitFunc instance_init,
229                      CamelObjectFinalizeFunc instance_finalize)
230 {
231         CamelTypeInfo *parent_info;
232         CamelTypeInfo *obj_info;
233         gchar *chunkname;
234
235         g_return_val_if_fail (parent != CAMEL_INVALID_TYPE,
236                               CAMEL_INVALID_TYPE);
237         g_return_val_if_fail (name, CAMEL_INVALID_TYPE);
238         g_return_val_if_fail (instance_size, CAMEL_INVALID_TYPE);
239         g_return_val_if_fail (classfuncs_size, CAMEL_INVALID_TYPE);
240
241         camel_type_lock_up ();
242
243         if (type_system_initialized == FALSE) {
244                 G_UNLOCK (type_system);
245                 camel_type_init ();
246                 G_LOCK (type_system);
247         }
248         
249         obj_info = g_hash_table_lookup (name_to_typeinfo, name);
250         if (obj_info != NULL) {
251                 /* looks like we've already registered this type... */
252                 camel_type_lock_down ();
253                 return obj_info->self;
254         }
255         
256         parent_info =
257                 g_hash_table_lookup (ctype_to_typeinfo,
258                                      GINT_TO_POINTER (parent));
259
260         if (parent_info == NULL) {
261                 g_warning
262                         ("camel_type_register: no such parent type %d of class `%s'",
263                          parent, name);
264                 camel_type_lock_down ();
265                 return CAMEL_INVALID_TYPE;
266         }
267
268         if (parent_info->instance_size > instance_size) {
269                 g_warning
270                         ("camel_type_register: instance of class `%s' would be smaller than parent `%s'",
271                          name, parent_info->name);
272                 camel_type_lock_down ();
273                 return CAMEL_INVALID_TYPE;
274         }
275
276         if (parent_info->classfuncs_size > classfuncs_size) {
277                 g_warning
278                         ("camel_type_register: classfuncs of class `%s' would be smaller than parent `%s'",
279                          name, parent_info->name);
280                 camel_type_lock_down ();
281                 return CAMEL_INVALID_TYPE;
282         }
283
284         cur_max_type++;
285
286         obj_info = g_new (CamelTypeInfo, 1);
287         obj_info->self = cur_max_type;
288         obj_info->parent = parent;
289         obj_info->name = name;
290
291         obj_info->instance_size = instance_size;
292         chunkname =
293                 g_strdup_printf ("chunk for instances of Camel type `%s'",
294                                  name);
295         obj_info->instance_chunk =
296                 g_mem_chunk_new (chunkname, instance_size,
297                                  instance_size * DEFAULT_PREALLOCS,
298                                  G_ALLOC_ONLY);
299         g_free (chunkname);
300         obj_info->instance_init = instance_init;
301         obj_info->instance_finalize = instance_finalize;
302         obj_info->free_instances = NULL;
303
304         obj_info->classfuncs_size = classfuncs_size;
305         obj_info->class_init = class_init;
306         obj_info->class_finalize = class_finalize;
307
308         g_hash_table_insert (ctype_to_typeinfo,
309                              GINT_TO_POINTER (obj_info->self), obj_info);
310         g_hash_table_insert (name_to_typeinfo, (gpointer) obj_info->name, obj_info);
311
312         /* Sigh. Ugly. */
313         make_global_classfuncs (obj_info);
314
315         camel_type_lock_down ();
316         return obj_info->self;
317 }
318
319 CamelObjectClass *
320 camel_type_get_global_classfuncs (CamelType type)
321 {
322         CamelTypeInfo *type_info;
323
324         g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL);
325
326         camel_type_lock_up ();
327         type_info =
328                 g_hash_table_lookup (ctype_to_typeinfo,
329                                      GINT_TO_POINTER (type));
330         camel_type_lock_down ();
331
332         g_return_val_if_fail (type_info != NULL, NULL);
333
334         return type_info->global_classfuncs;
335 }
336
337 const gchar *
338 camel_type_to_name (CamelType type)
339 {
340         CamelTypeInfo *type_info;
341
342         g_return_val_if_fail (type != CAMEL_INVALID_TYPE,
343                               "(the invalid type)");
344
345         camel_type_lock_up ();
346         type_info =
347                 g_hash_table_lookup (ctype_to_typeinfo,
348                                      GINT_TO_POINTER (type));
349         camel_type_lock_down ();
350
351         g_return_val_if_fail (type_info != NULL,
352                               "(a bad type parameter was specified)");
353
354         return type_info->name;
355 }
356
357 /* ** The CamelObject ***************************************************** */
358
359 static void
360 obj_init (CamelObject * obj)
361 {
362         obj->s.magic = CAMEL_OBJECT_MAGIC_VALUE;
363         obj->ref_count = 1;
364         obj->hooks = NULL;
365         obj->in_event = 0;
366         obj->destroying = 0;
367 }
368
369 static void
370 obj_finalize (CamelObject * obj)
371 {
372         g_return_if_fail (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE);
373         g_return_if_fail (obj->ref_count == 0);
374         g_return_if_fail (obj->in_event == 0);
375
376         obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE;
377
378         camel_object_free_hooks(obj);
379 }
380
381 static void
382 obj_class_init (CamelObjectClass * class)
383 {
384         class->s.magic = CAMEL_OBJECT_CLASS_MAGIC_VALUE;
385
386         camel_object_class_declare_event (class, "finalize", NULL);
387 }
388
389 static void
390 obj_class_finalize (CamelObjectClass * class)
391 {
392         g_return_if_fail (class->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE);
393
394         class->s.magic = CAMEL_OBJECT_CLASS_FINALIZED_VALUE;
395
396         if (class->event_to_preplist) {
397                 /* FIXME: This leaks the preplist slist entries */
398                 g_hash_table_foreach (class->event_to_preplist,
399                                       (GHFunc) g_free, NULL);
400                 g_hash_table_destroy (class->event_to_preplist);
401                 class->event_to_preplist = NULL;
402         }
403 }
404
405 CamelType
406 camel_object_get_type (void)
407 {
408         if (type_system_initialized == FALSE)
409                 camel_type_init ();
410
411         return camel_object_type;
412 }
413
414 CamelObject *
415 camel_object_new (CamelType type)
416 {
417         CamelTypeInfo *type_info;
418         GSList *parents = NULL;
419         GSList *head = NULL;
420         CamelObject *instance;
421
422         g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL);
423
424         /* Look up the type */
425
426         camel_type_lock_up ();
427
428         type_info =
429                 g_hash_table_lookup (ctype_to_typeinfo,
430                                      GINT_TO_POINTER (type));
431
432         if (type_info == NULL) {
433                 g_warning
434                         ("camel_object_new: trying to create object of invalid type %d",
435                          type);
436                 camel_type_lock_down ();
437                 return NULL;
438         }
439
440         /* Grab an instance out of the freed ones if possible, alloc otherwise */
441
442         if (type_info->free_instances) {
443                 GList *first;
444
445                 first = g_list_first (type_info->free_instances);
446                 instance = first->data;
447                 type_info->free_instances =
448                         g_list_remove_link (type_info->free_instances, first);
449                 g_list_free_1 (first);
450                 memset (instance, 0, type_info->instance_size);
451         } else {
452                 instance = g_mem_chunk_alloc0 (type_info->instance_chunk);
453         }
454
455         /* Init the instance and classfuncs a bit */
456
457         instance->s.type = type;
458         instance->classfuncs = type_info->global_classfuncs;
459
460         /* Loop through the parents in simplest -> most complex order, initing the class and instance.
461
462          * When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL
463          * because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh?
464          */
465
466         while (type_info) {
467                 parents = g_slist_prepend (parents, type_info);
468                 type_info =
469                         g_hash_table_lookup (ctype_to_typeinfo,
470                                              GINT_TO_POINTER (type_info->
471                                                               parent));
472         }
473
474         head = parents;
475
476         for (; parents && parents->data; parents = parents->next) {
477                 CamelTypeInfo *thisinfo;
478
479                 thisinfo = parents->data;
480                 if (thisinfo->instance_init)
481                         (thisinfo->instance_init) (instance);
482         }
483
484         g_slist_free (head);
485
486         camel_type_lock_down ();
487         return instance;
488 }
489
490 #ifdef camel_object_ref
491 #undef camel_object_ref
492 #endif
493
494 void
495 camel_object_ref (CamelObject * obj)
496 {
497         g_return_if_fail (CAMEL_IS_OBJECT (obj));
498
499         G_LOCK (refcount);
500         obj->ref_count++;
501         G_UNLOCK (refcount);
502 }
503
504 #ifdef camel_object_unref
505 #undef camel_object_unref
506 #endif
507
508 void
509 camel_object_unref (CamelObject * obj)
510 {
511         CamelTypeInfo *type_info;
512         CamelTypeInfo *iter;
513         GSList *parents = NULL;
514         GSList *head = NULL;
515
516         g_return_if_fail (CAMEL_IS_OBJECT (obj));
517
518         G_LOCK (refcount);
519         obj->ref_count--;
520
521         if (obj->ref_count > 0) {
522                 G_UNLOCK (refcount);
523                 return;
524         }
525
526         G_UNLOCK (refcount);
527
528         /* If the object already had its last unref, do not begin the
529          * destruction process again. This can happen if, for example,
530          * the object sends an event in its finalize handler (vfolders
531          * do this).
532          */
533
534         if (obj->destroying)
535                 return;
536         
537         obj->destroying = 1;
538
539         /* Send the finalize event */
540
541         camel_object_trigger_event (obj, "finalize", NULL);
542
543         /* Destroy it! hahaha! */
544
545         camel_type_lock_up ();
546
547         type_info =
548                 g_hash_table_lookup (ctype_to_typeinfo,
549                                      GINT_TO_POINTER (obj->s.type));
550
551         if (type_info == NULL) {
552                 g_warning
553                         ("camel_object_unref: seemingly valid object has a bad type %d",
554                          obj->s.type);
555                 camel_type_lock_down ();
556                 return;
557         }
558
559         /* Loop through the parents in most complex -> simplest order, finalizing the class 
560          * and instance.
561          *
562          * When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL
563          * because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh?
564          *
565          * Use iter to preserve type_info for free_{instance,classfunc}s
566          */
567
568         iter = type_info;
569
570         while (iter) {
571                 parents = g_slist_prepend (parents, iter);
572                 iter =
573                         g_hash_table_lookup (ctype_to_typeinfo,
574                                              GINT_TO_POINTER (iter->parent));
575         }
576
577         /* ok, done with the type stuff, and our data pointers
578          * won't go bad. */
579         camel_type_lock_down ();
580
581         parents = g_slist_reverse (parents);
582         head = parents;
583
584         for (; parents && parents->data; parents = parents->next) {
585                 CamelTypeInfo *thisinfo;
586
587                 thisinfo = parents->data;
588                 if (thisinfo->instance_finalize)
589                         (thisinfo->instance_finalize) (obj);
590         }
591
592         g_slist_free (head);
593
594         /* Sanity check */
595
596         if (obj->ref_count != 0)
597                 g_warning ("camel_object_unref: destroyed object %s at %p somehow got"
598                            " referenced in destruction chain.",
599                            camel_type_to_name (obj->s.type),
600                            obj);
601
602         /* A little bit of cleaning up.
603
604          * Don't erase the type, so we can peek at it if a finalized object
605          * is check_cast'ed somewhere.  Fill it with gunk to help detect
606          * other invalid ref's of it.
607          */
608
609         memset (obj, 0xEB, type_info->instance_size);
610         obj->s.type = type_info->self;
611         obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE;
612
613         /* Tuck away the pointer for use in a new object */
614
615         camel_type_lock_up ();
616
617         type_info->free_instances =
618                 g_list_prepend (type_info->free_instances, obj);
619
620         camel_type_lock_down ();
621 }
622
623 gboolean
624 camel_object_is_of_type (CamelObject * obj, CamelType ctype)
625 {
626         return shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE);
627 }
628
629 gboolean
630 camel_object_class_is_of_type (CamelObjectClass * class, CamelType ctype)
631 {
632         return shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE);
633 }
634
635 #ifdef BAST_CASTARD
636 #define ERRVAL NULL
637 #else
638 #define ERRVAL obj
639 #endif
640
641 CamelObject *
642 camel_object_check_cast (CamelObject * obj, CamelType ctype)
643 {
644         if (shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE))
645                 return obj;
646         return ERRVAL;
647 }
648
649 CamelObjectClass *
650 camel_object_class_check_cast (CamelObjectClass * class, CamelType ctype)
651 {
652         if (shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE))
653                 return class;
654         return ERRVAL;
655 }
656
657 #undef ERRVAL
658
659 gchar *
660 camel_object_describe (CamelObject * obj)
661 {
662         if (obj == NULL)
663                 return g_strdup ("a NULL pointer");
664
665         if (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE) {
666                 return g_strdup_printf ("an instance of `%s' at %p",
667                                         camel_type_to_name (obj->s.type),
668                                         obj);
669         } else if (obj->s.magic == CAMEL_OBJECT_FINALIZED_VALUE) {
670                 return g_strdup_printf ("a finalized instance of `%s' at %p",
671                                         camel_type_to_name (obj->s.type),
672                                         obj);
673         } else if (obj->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
674                 return g_strdup_printf ("the classfuncs of `%s' at %p",
675                                         camel_type_to_name (obj->s.type),
676                                         obj);
677         } else if (obj->s.magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) {
678                 return
679                         g_strdup_printf
680                         ("the finalized classfuncs of `%s' at %p",
681                          camel_type_to_name (obj->s.type), obj);
682         }
683
684         return g_strdup ("not a CamelObject");
685 }
686
687 /* This is likely to be called in the class_init callback,
688  * and the type will likely be somewhat uninitialized. 
689  * Is this a problem? We'll see....
690  */
691 void
692 camel_object_class_declare_event (CamelObjectClass * class,
693                                   const gchar * name,
694                                   CamelObjectEventPrepFunc prep)
695 {
696         g_return_if_fail (CAMEL_IS_OBJECT_CLASS (class));
697         g_return_if_fail (name);
698
699         if (class->event_to_preplist == NULL)
700                 class->event_to_preplist =
701                         g_hash_table_new (g_str_hash, g_str_equal);
702         else if (g_hash_table_lookup (class->event_to_preplist, name) != NULL) {
703                 g_warning
704                         ("camel_object_class_declare_event: event `%s' already declared for `%s'",
705                          name, camel_type_to_name (class->s.type));
706                 return;
707         }
708
709         /* AIEEEEEEEEEEEEEEEEEEEEEE
710
711          * I feel so naughty. Since it's valid to declare an event and not
712          * provide a hook, it should be valid to insert a NULL value into
713          * the table. However, then our lookup in trigger_event would be
714          * ambiguous, not telling us whether the event is undefined or whether
715          * it merely has no hook.
716          *
717          * So we create an 'NULL prep' value that != NULL... specifically, it
718          * equals the address of one of our static functions , because that
719          * can't possibly be your hook.
720          *
721          * Just don't forget to check for the 'evil value' and it'll work,
722          * I promise.
723          */
724
725         if (prep == NULL)
726                 prep = NULL_PREP_VALUE;
727
728         g_hash_table_insert (class->event_to_preplist, g_strdup (name), prep);
729 }
730
731 /* free hook data */
732 static void camel_object_free_hooks(CamelObject *o)
733 {
734         CamelHookPair *pair, *next;
735
736         if (o->hooks) {
737
738                 g_assert(o->hooks->depth == 0);
739                 g_assert((o->hooks->flags & CAMEL_HOOK_PAIR_REMOVED) == 0);
740
741                 pair = o->hooks->list;
742                 while (pair) {
743                         next = pair->next;
744                         g_free(pair);
745                         pair = next;
746                 }
747                 e_mutex_destroy(o->hooks->lock);
748                 g_free(o->hooks);
749                 o->hooks = NULL;
750         }
751 }
752
753 /* return (allocate if required) the object's hook list, locking at the same time */
754 static CamelHookList *camel_object_get_hooks(CamelObject *o)
755 {
756 #ifdef ENABLE_THREADS
757         static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
758 #endif
759         CamelHookList *hooks;
760
761         /* if we have it, we dont have to do any other locking,
762            otherwise use a global lock to setup the object's hook data */
763 #ifdef ENABLE_THREADS
764         if (o->hooks == NULL) {
765                 pthread_mutex_lock(&lock);
766 #endif
767                 if (o->hooks == NULL) {
768                         hooks = g_malloc(sizeof(*o->hooks));
769 #ifdef ENABLE_THREADS
770                         hooks->lock = e_mutex_new(E_MUTEX_REC);
771 #endif
772                         hooks->flags = 0;
773                         hooks->depth = 0;
774                         hooks->list_length = 0;
775                         hooks->list = NULL;
776                         o->hooks = hooks;
777                 }
778 #ifdef ENABLE_THREADS
779                 pthread_mutex_unlock(&lock);
780         }
781 #endif
782
783 #ifdef ENABLE_THREADS
784         e_mutex_lock(o->hooks->lock);
785 #endif
786         return o->hooks;        
787 }
788
789 /* unlock object hooks' list */
790 #ifdef ENABLE_THREADS
791 #define camel_object_unget_hooks(o) (e_mutex_unlock((CAMEL_OBJECT(o)->hooks->lock)))
792 #else
793 #define camel_object_unget_hooks(o)
794 #endif
795
796 void
797 camel_object_hook_event (CamelObject * obj, const char * name,
798                          CamelObjectEventHookFunc func, void *data)
799 {
800         CamelHookPair *pair;
801         const char *prepname;
802         CamelObjectEventPrepFunc prep;
803         CamelHookList *hooks;
804
805         g_return_if_fail (CAMEL_IS_OBJECT (obj));
806         g_return_if_fail (name != NULL);
807         g_return_if_fail (func != NULL);
808
809         /* first, does this event exist? */
810         if (obj->classfuncs->event_to_preplist == NULL
811             || !g_hash_table_lookup_extended(obj->classfuncs->event_to_preplist, name,
812                                              (void **)&prepname, (void **)&prep)) {
813                 g_warning("camel_object_hook_event: trying to hook event `%s' in class `%s' with no defined events.",
814                           name, camel_type_to_name (obj->s.type));
815                 return;
816         }
817
818         /* setup hook pair */
819         pair = g_malloc(sizeof(*pair));
820         pair->name = prepname;  /* effectively static! */
821         pair->func = func;
822         pair->data = data;
823         pair->flags = 0;
824
825         /* get the hook list object, locked, link in new event hook, unlock */
826         hooks = camel_object_get_hooks(obj);
827         pair->next = hooks->list;
828         hooks->list = pair;
829         hooks->list_length++;
830         camel_object_unget_hooks(obj);
831 }
832
833 void
834 camel_object_unhook_event (CamelObject * obj, const char * name,
835                            CamelObjectEventHookFunc func, void *data)
836 {
837         char *prepname;
838         CamelObjectEventPrepFunc prep;
839         CamelHookList *hooks;
840         CamelHookPair *pair, *parent;
841
842         g_return_if_fail (CAMEL_IS_OBJECT (obj));
843         g_return_if_fail (name != NULL);
844         g_return_if_fail (func != NULL);
845
846         if (obj->hooks == NULL) {
847                 g_warning("camel_object_unhook_event: trying to unhook `%s` from an instance of `%s' with no hooks",
848                           name, camel_type_to_name(obj->s.type));
849                 return;
850         }
851
852         /* get event name static pointer */
853         if (obj->classfuncs->event_to_preplist == NULL
854             || !g_hash_table_lookup_extended(obj->classfuncs->event_to_preplist, name,
855                                              (void **)&prepname, (void **)&prep)) {
856                 g_warning("camel_object_hook_event: trying to hook event `%s' in class `%s' with no defined events.",
857                           name, camel_type_to_name (obj->s.type));
858                 return;
859         }
860
861         /* scan hooks for this event, remove it, or flag it if we're busy */
862         hooks = camel_object_get_hooks(obj);
863         parent = (CamelHookPair *)&hooks->list;
864         pair = parent->next;
865         while (pair) {
866                 if (pair->name == prepname
867                     && pair->func == func
868                     && pair->data == data
869                     && (pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) {
870                         if (hooks->depth > 0) {
871                                 pair->flags |= CAMEL_HOOK_PAIR_REMOVED;
872                                 hooks->flags |= CAMEL_HOOK_PAIR_REMOVED;
873                         } else {
874                                 parent->next = pair->next;
875                                 g_free(pair);
876                                 hooks->list_length--;
877                         }
878                         camel_object_unget_hooks(obj);
879                         return;
880                 }
881                 parent = pair;
882                 pair = pair->next;
883         }
884         camel_object_unget_hooks(obj);
885
886         g_warning("camel_object_unhook_event: cannot find hook/data pair %p/%p in an instance of `%s' attached to `%s'",
887                   func, data, camel_type_to_name (obj->s.type), name);
888 }
889
890 void
891 camel_object_trigger_event (CamelObject * obj, const char * name, void *event_data)
892 {
893         CamelObjectEventPrepFunc prep;
894         const char *prepname;
895         CamelHookList *hooks;
896         CamelHookPair *pair, **pairs, *parent;
897         int i, size;
898
899         g_return_if_fail (CAMEL_IS_OBJECT (obj));
900         g_return_if_fail (name);
901
902         /* get event name static pointer/prep func */
903         if (obj->classfuncs->event_to_preplist == NULL
904             || !g_hash_table_lookup_extended(obj->classfuncs->event_to_preplist, name,
905                                              (void **)&prepname, (void **)&prep)) {
906                 g_warning("camel_object_hook_event: trying to hook event `%s' in class `%s' with no defined events.",
907                           name, camel_type_to_name (obj->s.type));
908                 return;
909         }
910
911         /* try prep function, if false, then quit */
912         if (prep != NULL_PREP_VALUE && !prep(obj, event_data))
913                 return;
914
915         /* also, no hooks, dont bother going further */
916         if (obj->hooks == NULL)
917                 return;
918
919         /* lock the object for hook emission */
920         camel_object_ref(obj);
921         hooks = camel_object_get_hooks(obj);
922
923         if (hooks->list) {
924                 /* first, copy the items in the list, and say we're in an event */
925                 hooks->depth++;
926                 pair = hooks->list;
927                 size = 0;
928                 pairs = alloca(sizeof(pairs[0]) * hooks->list_length);
929                 while (pair) {
930                         if (pair->name == prepname)
931                                 pairs[size++] = pair;
932                         pair = pair->next;
933                 }
934
935                 /* now execute the events we have, if they haven't been removed during our calls */
936                 for (i=0;i<size;i++) {
937                         pair = pairs[i];
938                         if ((pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0)
939                                 (pair->func) (obj, event_data, pair->data);
940                 }
941                 hooks->depth--;
942
943                 /* and if we're out of any events, then clean up any pending removes */
944                 if (hooks->depth == 0 && (hooks->flags & CAMEL_HOOK_PAIR_REMOVED)) {
945                         parent = (CamelHookPair *)&hooks->list;
946                         pair = parent->next;
947                         while (pair) {
948                                 if (pair->flags & CAMEL_HOOK_PAIR_REMOVED) {
949                                         parent->next = pair->next;
950                                         g_free(pair);
951                                         hooks->list_length--;
952                                 } else {
953                                         parent = pair;
954                                 }
955                                 pair = parent->next;
956                         }
957                         hooks->flags &= ~CAMEL_HOOK_PAIR_REMOVED;
958                 }
959         }
960
961         camel_object_unget_hooks(obj);
962         camel_object_unref(obj);
963 }
964
965 /* ** Static helpers ****************************************************** */
966
967 static gboolean
968 shared_is_of_type (CamelObjectShared * sh, CamelType ctype, gboolean is_obj)
969 {
970         CamelTypeInfo *type_info;
971         gchar *targtype;
972
973         if (is_obj)
974                 targtype = "instance";
975         else
976                 targtype = "classdata";
977
978         if (ctype == CAMEL_INVALID_TYPE) {
979                 g_warning
980                         ("shared_is_of_type: trying to cast to CAMEL_INVALID_TYPE");
981                 return FALSE;
982         }
983
984         if (sh == NULL) {
985                 g_warning
986                         ("shared_is_of_type: trying to cast NULL to %s of `%s'",
987                          targtype, camel_type_to_name (ctype));
988                 return FALSE;
989         }
990
991         if (sh->magic == CAMEL_OBJECT_FINALIZED_VALUE) {
992                 g_warning
993                         ("shared_is_of_type: trying to cast finalized instance "
994                          "of `%s' into %s of `%s'",
995                          camel_type_to_name (sh->type), targtype,
996                          camel_type_to_name (ctype));
997                 return FALSE;
998         }
999
1000         if (sh->magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) {
1001                 g_warning
1002                         ("shared_is_of_type: trying to cast finalized classdata "
1003                          "of `%s' into %s of `%s'",
1004                          camel_type_to_name (sh->type), targtype,
1005                          camel_type_to_name (ctype));
1006                 return FALSE;
1007         }
1008
1009         if (is_obj) {
1010                 if (sh->magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
1011                         g_warning
1012                                 ("shared_is_of_type: trying to cast classdata "
1013                                  "of `%s' into instance of `%s'",
1014                                  camel_type_to_name (sh->type),
1015                                  camel_type_to_name (ctype));
1016                         return FALSE;
1017                 }
1018
1019                 if (sh->magic != CAMEL_OBJECT_MAGIC_VALUE) {
1020                         g_warning
1021                                 ("shared_is_of_type: trying to cast junk data "
1022                                  "into instance of `%s'",
1023                                  camel_type_to_name (ctype));
1024                         return FALSE;
1025                 }
1026         } else {
1027                 if (sh->magic == CAMEL_OBJECT_MAGIC_VALUE) {
1028                         g_warning
1029                                 ("shared_is_of_type: trying to cast instance "
1030                                  "of `%s' into classdata of `%s'",
1031                                  camel_type_to_name (sh->type),
1032                                  camel_type_to_name (ctype));
1033                         return FALSE;
1034                 }
1035
1036                 if (sh->magic != CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
1037                         g_warning
1038                                 ("shared_is_of_type: trying to cast junk data "
1039                                  "into classdata of `%s'",
1040                                  camel_type_to_name (ctype));
1041                         return FALSE;
1042                 }
1043         }
1044
1045         camel_type_lock_up ();
1046
1047         type_info =
1048                 g_hash_table_lookup (ctype_to_typeinfo,
1049                                      GINT_TO_POINTER (sh->type));
1050
1051         if (type_info == NULL) {
1052                 g_warning ("shared_is_of_type: seemingly valid %s has "
1053                            "bad type %d.", targtype, sh->type);
1054                 camel_type_lock_down ();
1055                 return FALSE;
1056         }
1057
1058         while (type_info) {
1059                 if (type_info->self == ctype) {
1060                         camel_type_lock_down ();
1061                         return TRUE;
1062                 }
1063
1064                 type_info =
1065                         g_hash_table_lookup (ctype_to_typeinfo,
1066                                              GINT_TO_POINTER (type_info->
1067                                                               parent));
1068         }
1069
1070         /* this isn't an error, e.g. CAMEL_IS_FOLDER(folder), its upto  the
1071            caller to handle the false case */
1072         /*g_warning
1073                 ("shared_is_of_type: %s of `%s' (@%p) is not also %s of `%s'",
1074                  targtype, camel_type_to_name (sh->type), sh, targtype,
1075                  camel_type_to_name (ctype));*/
1076
1077         camel_type_lock_down ();
1078         return FALSE;
1079 }
1080
1081 static void
1082 make_global_classfuncs (CamelTypeInfo * type_info)
1083 {
1084         CamelObjectClass *funcs;
1085         GSList *parents;
1086         GSList *head;
1087
1088         g_assert (type_info);
1089
1090         funcs = g_malloc0 (type_info->classfuncs_size);
1091         funcs->s.type = type_info->self;
1092
1093         type_info->global_classfuncs = funcs;
1094
1095         parents = NULL;
1096         while (type_info) {
1097                 parents = g_slist_prepend (parents, type_info);
1098                 type_info =
1099                         g_hash_table_lookup (ctype_to_typeinfo,
1100                                              GINT_TO_POINTER (type_info->
1101                                                               parent));
1102         }
1103
1104         head = parents;
1105
1106         for (; parents && parents->data; parents = parents->next) {
1107                 CamelTypeInfo *thisinfo;
1108
1109                 thisinfo = parents->data;
1110                 if (thisinfo->class_init)
1111                         (thisinfo->class_init) (funcs);
1112         }
1113
1114         g_slist_free (head);
1115 }