Fix FSF address (Tobias Mueller, #470445)
[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  *
3  * Author:
4  *  Michael Zucchi <notzed@ximian.com>
5  *
6  * Copyright 2000-2003 Ximian, Inc. (www.ximian.com)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program 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
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20  * USA
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <pthread.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include <glib.h>
33 #include <glib/gstdio.h>
34
35 #include <libedataserver/e-data-server-util.h>
36 #include <libedataserver/e-memory.h>
37 #include <libedataserver/e-msgport.h>
38
39 #include "camel-file-utils.h"
40 #include "camel-object.h"
41
42 #define d(x)
43 #define b(x)                    /* object bag */
44 #define h(x)                    /* hooks */
45
46 /* I just mashed the keyboard for these... */
47 #define CAMEL_OBJECT_MAGIC               0x77A344ED
48 #define CAMEL_OBJECT_CLASS_MAGIC         0xEE26A997
49 #define CAMEL_INTERFACE_MAGIC            0xBCE137A7
50 #define CAMEL_OBJECT_FINALISED_MAGIC       0x84AC365F
51 #define CAMEL_OBJECT_CLASS_FINALISED_MAGIC 0x7621ABCD
52 #define CAMEL_INTERFACE_FINALISED_MAGIC    0x7CB2FE71
53
54 /* ** Quickie type system ************************************************* */
55
56 /* A 'locked' hooklist, that is only allocated on demand */
57 typedef struct _CamelHookList {
58         GStaticRecMutex lock;
59
60         unsigned int depth:30;  /* recursive event depth */
61         unsigned int flags:2;   /* flags, see below */
62
63         unsigned int list_length;
64         struct _CamelHookPair *list;
65 } CamelHookList;
66
67 #define CAMEL_HOOK_PAIR_REMOVED (1<<0)
68
69 /* a 'hook pair', actually a hook tuple, we just store all hooked events in the same list,
70    and just comapre as we go, rather than storing separate lists for each hook type
71
72    the name field just points directly to the key field in the class's preplist hashtable.
73    This way we can just use a direct pointer compare when scanning it, and also saves
74    copying the string */
75 typedef struct _CamelHookPair
76 {
77         struct _CamelHookPair *next; /* next MUST be the first member */
78
79         unsigned int id:30;
80         unsigned int flags:2;   /* removed, etc */
81
82         const char *name;       /* points to the key field in the classes preplist, static memory */
83         union {
84                 CamelObjectEventHookFunc event;
85                 CamelObjectEventPrepFunc prep;
86                 char *filename;
87         } func;
88         void *data;
89 } CamelHookPair;
90
91 struct _CamelObjectBagKey {
92         struct _CamelObjectBagKey *next;
93
94         void *key;              /* the key reserved */
95         int waiters;            /* count of threads waiting for key */
96         pthread_t owner;        /* the thread that has reserved the bag for a new entry */
97         int have_owner;
98         GCond *cond;
99 };
100
101 struct _CamelObjectBag {
102         GHashTable *object_table; /* object by key */
103         GHashTable *key_table;  /* key by object */
104         GEqualFunc equal_key;
105         CamelCopyFunc copy_key;
106         GFreeFunc free_key;
107
108         struct _CamelObjectBagKey *reserved;
109 };
110
111 /* used to tag a bag hookpair */
112 static const char bag_name[] = "object:bag";
113
114 /* meta-data stuff */
115 static void co_metadata_free(CamelObject *obj, CamelObjectMeta *meta);
116 static CamelObjectMeta *co_metadata_get(CamelObject *obj);
117 static CamelHookPair *co_metadata_pair(CamelObject *obj, int create);
118
119 static const char meta_name[] = "object:meta";
120 #define CAMEL_OBJECT_STATE_FILE_MAGIC "CLMD"
121
122 /* interface stuff */
123 static const char interface_name[] = "object:interface";
124
125 /* ********************************************************************** */
126
127 static CamelHookList *camel_object_get_hooks(CamelObject *o);
128 static void camel_object_free_hooks(CamelObject *o);
129 static void camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks);
130
131 #define camel_object_unget_hooks(o) \
132         (g_static_rec_mutex_unlock(&CAMEL_OBJECT(o)->hooks->lock))
133
134
135 /* ********************************************************************** */
136
137 static pthread_mutex_t chunks_lock = PTHREAD_MUTEX_INITIALIZER;
138
139 static EMemChunk *pair_chunks;
140 static EMemChunk *hook_chunks;
141 static unsigned int pair_id = 1;
142
143 /* type-lock must be recursive, for atomically creating classes */
144 static GStaticRecMutex type_lock = G_STATIC_REC_MUTEX_INIT;
145 /* ref-lock must be global :-(  for object bags to work */
146 static GMutex *ref_lock;
147
148 static GHashTable *type_table;
149 static EMemChunk *type_chunks;
150
151 /* fundamental types are accessed via global */
152 CamelType camel_object_type;
153 CamelType camel_interface_type;
154
155 #define P_LOCK(l) (pthread_mutex_lock(&l))
156 #define P_UNLOCK(l) (pthread_mutex_unlock(&l))
157 #define CLASS_LOCK(k) (g_mutex_lock((((CamelObjectClass *)k)->lock)))
158 #define CLASS_UNLOCK(k) (g_mutex_unlock((((CamelObjectClass *)k)->lock)))
159 #define REF_LOCK() (g_mutex_lock(ref_lock))
160 #define REF_UNLOCK() (g_mutex_unlock(ref_lock))
161 #define TYPE_LOCK() (g_static_rec_mutex_lock(&type_lock))
162 #define TYPE_UNLOCK() (g_static_rec_mutex_unlock(&type_lock))
163
164 static struct _CamelHookPair *
165 pair_alloc(void)
166 {
167         CamelHookPair *pair;
168
169         P_LOCK(chunks_lock);
170         pair = e_memchunk_alloc(pair_chunks);
171         pair->id = pair_id++;
172         if (pair_id == 0)
173                 pair_id = 1;
174         P_UNLOCK(chunks_lock);
175
176         return pair;
177 }
178
179 static void
180 pair_free(CamelHookPair *pair)
181 {
182         g_assert(pair_chunks != NULL);
183
184         P_LOCK(chunks_lock);
185         e_memchunk_free(pair_chunks, pair);
186         P_UNLOCK(chunks_lock);
187 }
188
189 static struct _CamelHookList *
190 hooks_alloc(void)
191 {
192         CamelHookList *hooks;
193
194         P_LOCK(chunks_lock);
195         hooks = e_memchunk_alloc(hook_chunks);
196         P_UNLOCK(chunks_lock);
197
198         return hooks;
199 }
200
201 static void
202 hooks_free(CamelHookList *hooks)
203 {
204         g_assert(hook_chunks != NULL);
205
206         P_LOCK(chunks_lock);
207         e_memchunk_free(hook_chunks, hooks);
208         P_UNLOCK(chunks_lock);
209 }
210
211 /* not checked locked, who cares, only required for people that want to redefine root objects */
212 void
213 camel_type_init(void)
214 {
215         static int init = FALSE;
216
217         if (init)
218                 return;
219
220         init = TRUE;
221         pair_chunks = e_memchunk_new(16, sizeof(CamelHookPair));
222         hook_chunks = e_memchunk_new(16, sizeof(CamelHookList));
223         type_chunks = e_memchunk_new(32, sizeof(CamelType));
224         type_table = g_hash_table_new(NULL, NULL);
225         ref_lock = g_mutex_new();
226 }
227
228 /* ************************************************************************ */
229
230 /* CamelObject base methods */
231
232 /* Should this return the object to the caller? */
233 static void
234 cobject_init(CamelObject *o, CamelObjectClass *klass)
235 {
236         o->klass = klass;
237         o->magic = CAMEL_OBJECT_MAGIC;
238         o->ref_count = 1;
239         o->flags = 0;
240 }
241
242 static void
243 cobject_finalise(CamelObject *o)
244 {
245         /*printf("%p: finalise %s\n", o, o->klass->name);*/
246
247         if (o->ref_count == 0)
248                 return;
249
250         camel_object_free_hooks(o);
251
252         o->magic = CAMEL_OBJECT_FINALISED_MAGIC;
253         o->klass = NULL;
254 }
255
256 static int
257 cobject_getv(CamelObject *o, CamelException *ex, CamelArgGetV *args)
258 {
259         int i;
260         guint32 tag;
261
262         for (i=0;i<args->argc;i++) {
263                 CamelArgGet *arg = &args->argv[i];
264
265                 tag = arg->tag;
266
267                 switch (tag & CAMEL_ARG_TAG) {
268                 case CAMEL_OBJECT_ARG_DESCRIPTION:
269                         *arg->ca_str = (char *)o->klass->name;
270                         break;
271                 case CAMEL_OBJECT_ARG_METADATA:
272                         *arg->ca_ptr = co_metadata_get(o);
273                         break;
274                 case CAMEL_OBJECT_ARG_STATE_FILE: {
275                         CamelHookPair *pair = co_metadata_pair(o, FALSE);
276
277                         if (pair) {
278                                 *arg->ca_str = g_strdup(pair->func.filename);
279                                 camel_object_unget_hooks(o);
280                         }
281                         break; }
282                 }
283         }
284
285         /* could have flags or stuff here? */
286         return 0;
287 }
288
289 static int
290 cobject_setv(CamelObject *o, CamelException *ex, CamelArgV *args)
291 {
292         int i;
293         guint32 tag;
294
295         for (i=0;i<args->argc;i++) {
296                 CamelArg *arg = &args->argv[i];
297
298                 tag = arg->tag;
299
300                 switch (tag & CAMEL_ARG_TAG) {
301                 case CAMEL_OBJECT_ARG_STATE_FILE: {
302                         CamelHookPair *pair;
303
304                         /* We store the filename on the meta-data hook-pair */
305                         pair = co_metadata_pair(o, TRUE);
306                         g_free(pair->func.filename);
307                         pair->func.filename = g_strdup(arg->ca_str);
308                         camel_object_unget_hooks(o);
309                         break; }
310                 }
311         }
312
313         /* could have flags or stuff here? */
314         return 0;
315 }
316
317 static void
318 cobject_free(CamelObject *o, guint32 tag, void *value)
319 {
320         switch(tag & CAMEL_ARG_TAG) {
321         case CAMEL_OBJECT_ARG_METADATA:
322                 co_metadata_free(o, value);
323                 break;
324         case CAMEL_OBJECT_ARG_STATE_FILE:
325                 g_free(value);
326                 break;
327         case CAMEL_OBJECT_ARG_PERSISTENT_PROPERTIES:
328                 g_slist_free((GSList *)value);
329                 break;
330         }
331 }
332
333 static char *
334 cobject_meta_get(CamelObject *obj, const char * name)
335 {
336         CamelHookPair *pair;
337         CamelObjectMeta *meta;
338         char *res = NULL;
339
340         g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0);
341         g_return_val_if_fail(name != NULL, 0);
342
343         pair = co_metadata_pair(obj, FALSE);
344         if (pair) {
345                 meta = pair->data;
346                 while (meta) {
347                         if (!strcmp(meta->name, name)) {
348                                 res = g_strdup(meta->value);
349                                 break;
350                         }
351                         meta = meta->next;
352                 }
353                 camel_object_unget_hooks(obj);
354         }
355         
356         return res;
357 }
358
359 static gboolean
360 cobject_meta_set(CamelObject *obj, const char * name, const char *value)
361 {
362         CamelHookPair *pair;
363         int changed = FALSE;
364         CamelObjectMeta *meta, *metap;
365
366         g_return_val_if_fail(CAMEL_IS_OBJECT (obj), FALSE);
367         g_return_val_if_fail(name != NULL, FALSE);
368
369         if (obj->hooks == NULL && value == NULL)
370                 return FALSE;
371
372         pair = co_metadata_pair(obj, TRUE);
373         meta = pair->data;
374         metap = (CamelObjectMeta *)&pair->data;
375         while (meta) {
376                 if (!strcmp(meta->name, name))
377                         break;
378                 metap = meta;
379                 meta = meta->next;
380         }
381
382         /* TODO: The camelobjectmeta structure is identical to
383            CamelTag, they could be merged or share common code */
384         if (meta == NULL) {
385                 if (value == NULL)
386                         goto done;
387                 meta = g_malloc(sizeof(*meta) + strlen(name));
388                 meta->next = pair->data;
389                 pair->data = meta;
390                 strcpy(meta->name, name);
391                 meta->value = g_strdup(value);
392                 changed = TRUE;
393         } else if (value == NULL) {
394                 metap->next = meta->next;
395                 g_free(meta->value);
396                 g_free(meta);
397                 changed = TRUE;
398         } else if (strcmp(meta->value, value) != 0) {
399                 g_free(meta->value);
400                 meta->value = g_strdup(value);
401                 changed = TRUE;
402         }
403
404 done:
405         camel_object_unget_hooks(obj);
406
407         return changed;
408 }
409
410 /* State file for CamelObject data.  Any later versions should only append data.
411
412    version:uint32
413
414    Version 0 of the file:
415
416    version:uint32 = 0
417    count:uint32                                 -- count of meta-data items
418    ( name:string value:string ) *count          -- meta-data items
419
420    Version 1 of the file adds:
421    count:uint32                                 -- count of persistent properties
422    ( tag:uing32 value:tagtype ) *count          -- persistent properties
423
424 */
425
426 static int
427 cobject_state_read(CamelObject *obj, FILE *fp)
428 {
429         guint32 i, count, version;
430
431         /* NB: for later versions, just check the version is 1 .. known version */
432         if (camel_file_util_decode_uint32(fp, &version) == -1
433             || version > 1
434             || camel_file_util_decode_uint32(fp, &count) == -1)
435                 return -1;
436
437         for (i=0;i<count;i++) {
438                 char *name = NULL, *value = NULL;
439                         
440                 if (camel_file_util_decode_string(fp, &name) == 0
441                     && camel_file_util_decode_string(fp, &value) == 0) {
442                         camel_object_meta_set(obj, name, value);
443                         g_free(name);
444                         g_free(value);
445                 } else {
446                         g_free(name);
447                         g_free(value);
448
449                         return -1;
450                 }
451         }
452
453         if (version > 0) {
454                 CamelArgV *argv;
455
456                 if (camel_file_util_decode_uint32(fp, &count) == -1
457                         || count == 0 || count > 1024) {
458                         /* maybe it was just version 0 afterall */
459                         return 0;
460                 }
461
462                 count = MIN(count, CAMEL_ARGV_MAX);
463                 
464                 /* we batch up the properties and set them in one go */
465                 argv = g_try_malloc(sizeof(CamelArgV) -
466                         ((CAMEL_ARGV_MAX - count) * sizeof(CamelArg)));
467                 if (argv == NULL)
468                         return -1;
469                 
470                 argv->argc = 0;
471                 for (i=0;i<count;i++) {
472                         if (camel_file_util_decode_uint32(fp, &argv->argv[argv->argc].tag) == -1)
473                                 goto cleanup;
474
475                         /* so far,only do strings and ints, doubles could be added,
476                            object's would require a serialisation interface */
477
478                         switch(argv->argv[argv->argc].tag & CAMEL_ARG_TYPE) {
479                         case CAMEL_ARG_INT:
480                         case CAMEL_ARG_BOO:
481                                 if (camel_file_util_decode_uint32(fp, &argv->argv[argv->argc].ca_int) == -1)
482                                         goto cleanup;
483                                 break;
484                         case CAMEL_ARG_STR:
485                                 if (camel_file_util_decode_string(fp, &argv->argv[argv->argc].ca_str) == -1)
486                                         goto cleanup;
487                                 break;
488                         default:
489                                 goto cleanup;
490                         }
491
492                         argv->argc++;
493                 }
494
495                 camel_object_setv(obj, NULL, argv);
496         cleanup:
497                 for (i=0;i<argv->argc;i++) {
498                         if ((argv->argv[i].tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR)
499                                 g_free(argv->argv[i].ca_str);
500                 }
501                 g_free(argv);
502         }
503
504         return 0;
505 }
506
507 /* TODO: should pass exception around */
508 static int
509 cobject_state_write(CamelObject *obj, FILE *fp)
510 {
511         gint32 count, i;
512         CamelObjectMeta *meta = NULL, *scan;
513         int res = -1;
514         GSList *props = NULL, *l;
515         CamelArgGetV *arggetv = NULL;
516         CamelArgV *argv = NULL;
517
518         camel_object_get(obj, NULL, CAMEL_OBJECT_METADATA, &meta, NULL);
519
520         count = 0;
521         scan = meta;
522         while (scan) {
523                 count++;
524                 scan = scan->next;
525         }
526
527         /* current version is 1 */
528         if (camel_file_util_encode_uint32(fp, 1) == -1
529             || camel_file_util_encode_uint32(fp, count) == -1)
530                 goto abort;
531
532         scan = meta;
533         while (scan) {
534                 if (camel_file_util_encode_string(fp, scan->name) == -1
535                     || camel_file_util_encode_string(fp, scan->value) == -1)
536                         goto abort;
537                 scan = scan->next;
538         }
539
540         camel_object_get(obj, NULL, CAMEL_OBJECT_PERSISTENT_PROPERTIES, &props, NULL);
541
542         /* we build an arggetv to query the object atomically,
543            we also need an argv to store the results - bit messy */
544
545         count = g_slist_length(props);
546         count = MIN(count, CAMEL_ARGV_MAX);
547
548         arggetv = g_malloc0(sizeof(CamelArgGetV) -
549                 ((CAMEL_ARGV_MAX - count) * sizeof(CamelArgGet)));
550         argv = g_malloc0(sizeof(CamelArgV) -
551                 ((CAMEL_ARGV_MAX - count) * sizeof(CamelArg)));
552         l = props;
553         i = 0;
554         while (l) {
555                 CamelProperty *prop = l->data;
556
557                 argv->argv[i].tag = prop->tag;
558                 arggetv->argv[i].tag = prop->tag;
559                 arggetv->argv[i].ca_ptr = &argv->argv[i].ca_ptr;
560
561                 i++;
562                 l = l->next;
563         }
564         arggetv->argc = i;
565         argv->argc = i;
566
567         camel_object_getv(obj, NULL, arggetv);
568
569         if (camel_file_util_encode_uint32(fp, count) == -1)
570                 goto abort;
571
572         for (i=0;i<argv->argc;i++) {
573                 CamelArg *arg = &argv->argv[i];
574
575                 if (camel_file_util_encode_uint32(fp, arg->tag) == -1)
576                         goto abort;
577
578                 switch (arg->tag & CAMEL_ARG_TYPE) {
579                 case CAMEL_ARG_INT:
580                 case CAMEL_ARG_BOO:
581                         if (camel_file_util_encode_uint32(fp, arg->ca_int) == -1)
582                                 goto abort;
583                         break;
584                 case CAMEL_ARG_STR:
585                         if (camel_file_util_encode_string(fp, arg->ca_str) == -1)
586                                 goto abort;
587                         break;
588                 }
589         }
590
591         res = 0;
592 abort:
593         for (i=0;i<argv->argc;i++) {
594                 CamelArg *arg = &argv->argv[i];
595
596                 if ((argv->argv[i].tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR)
597                         camel_object_free(obj, arg->tag, arg->ca_str);
598         }
599
600         g_free(argv);
601         g_free(arggetv);
602
603         if (props)
604                 camel_object_free(obj, CAMEL_OBJECT_PERSISTENT_PROPERTIES, props);
605
606         if (meta)
607                 camel_object_free(obj, CAMEL_OBJECT_METADATA, meta);
608
609         return res;
610 }
611
612
613 static void
614 cobject_class_init(CamelObjectClass *klass)
615 {
616         klass->magic = CAMEL_OBJECT_CLASS_MAGIC;
617
618         klass->getv = cobject_getv;
619         klass->setv = cobject_setv;
620         klass->free = cobject_free;
621
622         klass->meta_get = cobject_meta_get;
623         klass->meta_set = cobject_meta_set;
624         klass->state_read = cobject_state_read;
625         klass->state_write = cobject_state_write;
626
627         camel_object_class_add_event(klass, "finalize", NULL);
628         camel_object_class_add_event(klass, "meta_changed", NULL);
629 }
630
631 static void
632 cobject_class_finalise(CamelObjectClass * klass)
633 {
634         klass->magic = CAMEL_OBJECT_CLASS_FINALISED_MAGIC;
635
636         g_free(klass);
637 }
638
639
640 /* CamelInterface base methods */
641
642 static void
643 cinterface_init(CamelObject *o, CamelObjectClass *klass)
644 {
645         g_error("Cannot instantiate interfaces, trying to instantiate '%s'", klass->name);
646         abort();
647 }
648
649 static int
650 cinterface_getv(CamelObject *o, CamelException *ex, CamelArgGetV *args)
651 {
652         return 0;
653 }
654
655 static int
656 cinterface_setv(CamelObject *o, CamelException *ex, CamelArgV *args)
657 {
658         return 0;
659 }
660
661 static void
662 cinterface_free(CamelObject *o, guint32 tag, void *value)
663 {
664         /* NOOP */
665 }
666
667 static void
668 cinterface_class_init(CamelObjectClass *klass)
669 {
670         klass->magic = CAMEL_INTERFACE_MAGIC;
671
672         /* just setup dummy callbacks, properties could be part of the interface but we support none */
673         klass->getv = cinterface_getv;
674         klass->setv = cinterface_setv;
675         klass->free = cinterface_free;
676
677         /* TODO: ok, these are cruft hanging around an interface, but it saves having to define two different class bases */
678         klass->meta_get = NULL;
679         klass->meta_set = NULL;
680         klass->state_read = NULL;
681         klass->state_write = NULL;
682 }
683
684 static void
685 cinterface_class_finalise(CamelObjectClass * klass)
686 {
687         klass->magic = CAMEL_INTERFACE_FINALISED_MAGIC;
688         g_free(klass);
689 }
690
691 /* this function must be called for any other in the object system */
692 CamelType
693 camel_object_get_type(void)
694 {
695         if (camel_object_type == CAMEL_INVALID_TYPE) {
696                 camel_type_init();
697
698                 camel_object_type = camel_type_register(NULL, "CamelObject", /*, 0, 0*/
699                                                         sizeof(CamelObject), sizeof(CamelObjectClass),
700                                                         cobject_class_init, cobject_class_finalise,
701                                                         cobject_init, cobject_finalise);
702
703                 camel_interface_type = camel_type_register(NULL, "CamelInterface",
704                                                            0, sizeof(CamelInterface),
705                                                            cinterface_class_init, cinterface_class_finalise,
706                                                            cinterface_init, NULL);
707                                                            
708         }
709
710         return camel_object_type;
711 }
712
713 static void
714 camel_type_class_init(CamelObjectClass *klass, CamelObjectClass *type)
715 {
716         if (type->parent)
717                 camel_type_class_init(klass, type->parent);
718
719         if (type->klass_init)
720                 type->klass_init(klass);
721 }
722
723 static CamelType
724 co_type_register(CamelType parent, const char * name,
725                  /*unsigned int ver, unsigned int rev,*/
726                  size_t object_size, size_t klass_size,
727                  CamelObjectClassInitFunc class_init,
728                  CamelObjectClassFinalizeFunc class_finalise,
729                  CamelObjectInitFunc object_init,
730                  CamelObjectFinalizeFunc object_finalise)
731 {
732         CamelObjectClass *klass;
733         /*int offset;
734           size_t size;*/
735
736         TYPE_LOCK();
737
738         /* Have to check creation, it might've happened in another thread before we got here */
739         klass = g_hash_table_lookup(type_table, name);
740         if (klass != NULL) {
741                 if (klass->klass_size != klass_size || klass->object_size != object_size
742                     || klass->klass_init != class_init || klass->klass_finalise != class_finalise
743                     || klass->init != object_init || klass->finalise != object_finalise) {
744                         g_warning("camel_type_register: Trying to re-register class '%s'", name);
745                         klass = NULL;
746                 }
747                 TYPE_UNLOCK();
748                 return klass;
749         }
750
751         /* this is for objects with no parent as part of their struct ('interfaces'?) */
752         /*offset = parent?parent->klass_size:0;
753         offset = (offset + 3) & (~3);
754
755         size = offset + klass_size;
756
757         klass = g_malloc0(size);
758
759         klass->klass_size = size;
760         klass->klass_data = offset;
761
762         offset = parent?parent->object_size:0;
763         offset = (offset + 3) & (~3);
764
765         klass->object_size = offset + object_size;
766         klass->object_data = offset;*/
767
768         if (parent
769             && klass_size < parent->klass_size) {
770                 g_warning("camel_type_register: '%s' has smaller class size than parent '%s'", name, parent->name);
771                 TYPE_UNLOCK();
772                 return NULL;
773         }
774
775         klass = g_malloc0(klass_size);
776         klass->klass_size = klass_size;
777         klass->object_size = object_size;
778         klass->lock = g_mutex_new();
779         klass->instance_chunks = e_memchunk_new(8, object_size);
780         
781         klass->parent = parent;
782         if (parent) {
783                 klass->next = parent->child;
784                 parent->child = klass;
785         }
786         klass->name = name;
787
788         /*klass->version = ver;
789           klass->revision = rev;*/
790
791         klass->klass_init = class_init;
792         klass->klass_finalise = class_finalise;
793
794         klass->init = object_init;
795         klass->finalise = object_finalise;
796
797         /* setup before class init, incase class init func uses the type or looks it up ? */
798         g_hash_table_insert(type_table, (void *)name, klass);
799
800         camel_type_class_init(klass, klass);
801
802         TYPE_UNLOCK();
803
804         return klass;
805 }
806
807 CamelType
808 camel_type_register(CamelType parent, const char * name,
809                     /*unsigned int ver, unsigned int rev,*/
810                     size_t object_size, size_t klass_size,
811                     CamelObjectClassInitFunc class_init,
812                     CamelObjectClassFinalizeFunc class_finalise,
813                     CamelObjectInitFunc object_init,
814                     CamelObjectFinalizeFunc object_finalise)
815 {
816         if (parent != NULL && parent->magic != CAMEL_OBJECT_CLASS_MAGIC) {
817                 g_warning("camel_type_register: invalid junk parent class for '%s'", name);
818                 return NULL;
819         }
820
821         return co_type_register(parent, name, object_size, klass_size, class_init, class_finalise, object_init, object_finalise);
822 }
823
824 CamelType
825 camel_interface_register(CamelType parent, const char *name,
826                          size_t class_size,
827                          CamelObjectClassInitFunc class_init,
828                          CamelObjectClassFinalizeFunc class_finalise)
829 {
830         if (parent != NULL && parent->magic != CAMEL_INTERFACE_MAGIC) {
831                 g_warning("camel_interface_register: invalid junk parent class for '%s'", name);
832                 return NULL;
833         }
834
835         return camel_type_register(parent, name, 0, class_size, class_init, class_finalise, NULL, NULL);
836 }
837
838 static void
839 camel_object_init(CamelObject *o, CamelObjectClass *klass, CamelType type)
840 {
841         if (type->parent)
842                 camel_object_init(o, klass, type->parent);
843
844         if (type->init)
845                 type->init(o, klass);
846 }
847
848 CamelObject *
849 camel_object_new(CamelType type)
850 {
851         CamelObject *o;
852
853         if (type == NULL)
854                 return NULL;
855
856         if (type->magic != CAMEL_OBJECT_CLASS_MAGIC)
857                 return NULL;
858
859         CLASS_LOCK(type);
860
861         o = e_memchunk_alloc0(type->instance_chunks);
862
863 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
864         if (type->instances)
865                 type->instances->prev = o;
866         o->next = type->instances;
867         o->prev = NULL;
868         type->instances = o;
869 #endif
870
871         CLASS_UNLOCK(type);
872
873         camel_object_init(o, type, type);
874
875         d(printf("%p: new %s()\n", o, o->klass->name));
876
877         return o;
878 }
879
880 void
881 camel_object_ref(void *vo)
882 {
883         register CamelObject *o = vo;
884
885         g_return_if_fail(CAMEL_IS_OBJECT(o));
886
887         REF_LOCK();
888
889         o->ref_count++;
890         d(printf("%p: ref %s(%d)\n", o, o->klass->name, o->ref_count));
891
892         REF_UNLOCK();
893 }
894
895 void
896 camel_object_unref(void *vo)
897 {
898         register CamelObject *o = vo;
899         register CamelObjectClass *klass, *k;
900         CamelHookList *hooks = NULL;
901
902         g_return_if_fail(CAMEL_IS_OBJECT(o));
903         
904         klass = o->klass;
905
906         if (o->hooks)
907                 hooks = camel_object_get_hooks(o);
908
909         REF_LOCK();
910
911         o->ref_count--;
912
913         d(printf("%p: unref %s(%d)\n", o, o->klass->name, o->ref_count));
914
915         if (o->ref_count > 0
916             || (o->flags & CAMEL_OBJECT_DESTROY)) {
917                 REF_UNLOCK();
918                 if (hooks)
919                         camel_object_unget_hooks(o);
920                 return;
921         }
922
923         o->flags |= CAMEL_OBJECT_DESTROY;
924
925         if (hooks)
926                 camel_object_bag_remove_unlocked(NULL, o, hooks);
927
928         REF_UNLOCK();
929
930         if (hooks)
931                 camel_object_unget_hooks(o);
932
933         camel_object_trigger_event(o, "finalize", NULL);
934
935         k = klass;
936         while (k) {
937                 if (k->finalise)
938                         k->finalise(o);
939                 k = k->parent;
940         }
941
942         o->magic = CAMEL_OBJECT_FINALISED_MAGIC;
943
944         CLASS_LOCK(klass);
945 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
946         if (o->prev)
947                 o->prev->next = o->next;
948         else
949                 klass->instances = o->next;
950         if (o->next)
951                 o->next->prev = o->prev;
952 #endif
953         e_memchunk_free(klass->instance_chunks, o);
954         CLASS_UNLOCK(klass);
955 }
956
957 const char *
958 camel_type_to_name(CamelType type)
959 {
960         if (type == NULL)
961                 return "(NULL class)";
962
963         if (type->magic == CAMEL_OBJECT_CLASS_MAGIC)
964                 return type->name;
965
966         if (type->magic == CAMEL_INTERFACE_MAGIC)
967                 return type->name;
968
969         return "(Junk class)";
970 }
971
972 CamelType camel_name_to_type(const char *name)
973 {
974         /* TODO: Load a class off disk (!) */
975
976         return g_hash_table_lookup(type_table, name);
977 }
978
979 static char *
980 desc_data(CamelObject *o, guint32 ok)
981 {
982         char *what;
983
984         if (o == NULL)
985                 what = g_strdup("NULL OBJECT");
986         else if (o->magic == ok)
987                 what = NULL;
988         else if (o->magic == CAMEL_OBJECT_MAGIC)
989                 what = g_strdup_printf("CLASS '%s'", ((CamelObjectClass *)o)->name);
990         else if (o->magic == CAMEL_OBJECT_CLASS_MAGIC)
991                 what = g_strdup_printf("CLASS '%s'", ((CamelObjectClass *)o)->name);
992         else if (o->magic == CAMEL_INTERFACE_MAGIC)
993                 what = g_strdup_printf("INTERFACE '%s'", ((CamelObjectClass *)o)->name);
994         else if (o->magic == CAMEL_OBJECT_FINALISED_MAGIC)
995                 what = g_strdup_printf("finalised OBJECT");
996         else if (o->magic == CAMEL_OBJECT_CLASS_FINALISED_MAGIC)
997                 what = g_strdup_printf("finalised CLASS");
998         else if (o->magic == CAMEL_INTERFACE_FINALISED_MAGIC)
999                 what = g_strdup_printf("finalised INTERFACE");
1000         else 
1001                 what = g_strdup_printf("junk data");
1002
1003         return what;
1004 }
1005
1006 #define check_magic(o, ctype, omagic) \
1007         ( ((CamelObject *)(o))->magic == (omagic) \
1008         && (ctype)->magic == CAMEL_OBJECT_CLASS_MAGIC) \
1009         ? 1 : check_magic_fail(o, ctype, omagic)
1010
1011 static gboolean
1012 check_magic_fail(void *o, CamelType ctype, guint32 omagic)
1013 {
1014         char *what, *to;
1015
1016         what = desc_data(o, omagic);
1017         to = desc_data((CamelObject *)ctype, CAMEL_OBJECT_CLASS_MAGIC);
1018
1019         if (what || to) {
1020                 if (what == NULL) {
1021                         if (omagic == CAMEL_OBJECT_MAGIC)
1022                                 what = g_strdup_printf("OBJECT '%s'", ((CamelObject *)o)->klass->name);
1023                         else
1024                                 what = g_strdup_printf("OBJECT '%s'", ((CamelObjectClass *)o)->name);
1025                 }               
1026                 if (to == NULL)
1027                         to = g_strdup_printf("OBJECT '%s'", ctype->name);
1028                 g_warning("Trying to check %s is %s", what, to);
1029                 g_free(what);
1030                 g_free(to);
1031
1032                 return FALSE;
1033         }
1034
1035         return TRUE;
1036 }
1037
1038 gboolean
1039 camel_object_is(CamelObject *o, CamelType ctype)
1040 {
1041         CamelObjectClass *k;
1042
1043         g_return_val_if_fail(o != NULL, FALSE);
1044         g_return_val_if_fail(check_magic(o, ctype, CAMEL_OBJECT_MAGIC), FALSE);
1045
1046         k = o->klass;
1047         while (k) {
1048                 if (k == ctype)
1049                         return TRUE;
1050                 k = k->parent;
1051         }
1052
1053         return FALSE;
1054 }
1055
1056 gboolean
1057 camel_object_class_is(CamelObjectClass *k, CamelType ctype)
1058 {
1059         g_return_val_if_fail(k != NULL, FALSE);
1060         g_return_val_if_fail(check_magic(k, ctype, CAMEL_OBJECT_CLASS_MAGIC), FALSE);
1061
1062         while (k) {
1063                 if (k == ctype)
1064                         return TRUE;
1065                 k = k->parent;
1066         }
1067
1068         return FALSE;
1069 }
1070
1071 gboolean
1072 camel_interface_is(CamelObjectClass *k, CamelType ctype)
1073 {
1074         g_return_val_if_fail(k != NULL, FALSE);
1075         g_return_val_if_fail(check_magic(k, ctype, CAMEL_INTERFACE_MAGIC), FALSE);
1076
1077         while (k) {
1078                 if (k == ctype)
1079                         return TRUE;
1080                 k = k->parent;
1081         }
1082
1083         return FALSE;
1084 }
1085
1086 CamelObject *
1087 camel_object_cast(CamelObject *o, CamelType ctype)
1088 {
1089         CamelObjectClass *k;
1090
1091         g_return_val_if_fail(check_magic(o, ctype, CAMEL_OBJECT_MAGIC), NULL);
1092
1093         k = o->klass;
1094         while (k) {
1095                 if (k == ctype)
1096                         return o;
1097                 k = k->parent;
1098         }
1099
1100         g_warning("Object %p (class '%s') doesn't have '%s' in its hierarchy", o, o->klass->name, ctype->name);
1101
1102         return NULL;
1103 }
1104
1105 CamelObjectClass *
1106 camel_object_class_cast(CamelObjectClass *k, CamelType ctype)
1107 {
1108         CamelObjectClass *r = k;
1109
1110         g_return_val_if_fail(check_magic(k, ctype, CAMEL_OBJECT_CLASS_MAGIC), NULL);
1111
1112         while (k) {
1113                 if (k == ctype)
1114                         return r;
1115                 k = k->parent;
1116         }
1117
1118         g_warning("Class '%s' doesn't have '%s' in its hierarchy", r->name, ctype->name);
1119
1120         return NULL;
1121 }
1122
1123 CamelObjectClass *
1124 camel_interface_cast(CamelObjectClass *k, CamelType ctype)
1125 {
1126         CamelObjectClass *r = k;
1127
1128         g_return_val_if_fail(check_magic(k, ctype, CAMEL_INTERFACE_MAGIC), NULL);
1129
1130         while (k) {
1131                 if (k == ctype)
1132                         return r;
1133                 k = k->parent;
1134         }
1135
1136         g_warning("Interface '%s' doesn't have '%s' in its hierarchy", r->name, ctype->name);
1137
1138         return NULL;
1139 }
1140
1141 static CamelHookPair *
1142 co_find_pair(CamelObjectClass *klass, const char *name)
1143 {
1144         CamelHookPair *hook;
1145
1146         hook = klass->hooks;
1147         while (hook) {
1148                 if (strcmp(hook->name, name) == 0)
1149                         return hook;
1150                 hook = hook->next;
1151         }
1152
1153         return NULL;
1154 }
1155
1156 static CamelHookPair *
1157 co_find_pair_ptr(CamelObjectClass *klass, const char *name)
1158 {
1159         CamelHookPair *hook;
1160
1161         hook = klass->hooks;
1162         while (hook) {
1163                 if (hook->name == name)
1164                         return hook;
1165                 hook = hook->next;
1166         }
1167
1168         return NULL;
1169 }
1170
1171 /* class functions */
1172 void
1173 camel_object_class_add_event(CamelObjectClass *klass, const char *name, CamelObjectEventPrepFunc prep)
1174 {
1175         CamelHookPair *pair;
1176
1177         g_return_if_fail (name);
1178
1179         pair = co_find_pair(klass, name);
1180         if (pair) {
1181                 g_warning("camel_object_class_add_event: `%s' is already declared for '%s'",
1182                           name, klass->name);
1183                 return;
1184         }
1185
1186         if (klass->magic == CAMEL_INTERFACE_MAGIC && prep != NULL) {
1187                 g_warning("camel_object_class_add_event: `%s', CamelInterface '%s' may not have an event prep function - ignored",
1188                           name, klass->name);
1189                 prep = NULL;
1190         }
1191
1192         pair = pair_alloc();
1193         pair->name = name;
1194         pair->func.prep = prep;
1195         pair->flags = 0;
1196
1197         pair->next = klass->hooks;
1198         klass->hooks = pair;
1199 }
1200
1201 void
1202 camel_object_class_add_interface(CamelObjectClass *klass, CamelType itype)
1203 {
1204         CamelHookPair *pair;
1205         CamelType iscan;
1206         GPtrArray *interfaces;
1207         int i;
1208
1209         if (!camel_interface_is(itype, camel_interface_type)) {
1210                 g_warning("Cannot add an interface not derived from CamelInterface on class '%s'", klass->name);
1211                 return;
1212         }
1213
1214         if (camel_object_class_is(klass, camel_interface_type)) {
1215                 g_warning("Cannot add an interface onto a class derived from CamelInterface");
1216                 return;
1217         }
1218
1219         /* we store it on the class hooks so we don't have to add any extra space to the class */
1220         pair = co_find_pair_ptr(klass, interface_name);
1221         if (pair == NULL) {
1222                 pair = pair_alloc();
1223                 pair->data = g_ptr_array_new();
1224                 pair->next = klass->hooks;
1225                 klass->hooks = pair;
1226         }
1227
1228         /* We just check that this type isn't added/derived anywhere else */
1229         interfaces = pair->data;
1230         iscan = itype;
1231         while (iscan && iscan != camel_interface_type) {
1232                 for (i=0;i<interfaces->len;i++) {
1233                         if (camel_interface_is((CamelType)interfaces->pdata[i], iscan)) {
1234                                 g_warning("Cannot add an interface twice '%s' on class '%s'\n", itype->name, klass->name);
1235                                 return;
1236                         }
1237                 }
1238                 iscan = iscan->parent;
1239         }
1240
1241         if (iscan == camel_interface_type)
1242                 g_ptr_array_add(interfaces, itype);
1243 }
1244
1245 /* free hook data */
1246 static void
1247 camel_object_free_hooks(CamelObject *o)
1248 {
1249         CamelHookPair *pair, *next;
1250
1251         if (o->hooks) {
1252                 g_assert(o->hooks->depth == 0);
1253                 g_assert((o->hooks->flags & CAMEL_HOOK_PAIR_REMOVED) == 0);
1254
1255                 pair = o->hooks->list;
1256                 while (pair) {
1257                         next = pair->next;
1258
1259                         if (pair->name == meta_name) {
1260                                 co_metadata_free(o, pair->data);
1261                                 g_free(pair->func.filename);
1262                         }
1263
1264                         pair_free(pair);
1265                         pair = next;
1266                 }
1267                 g_static_rec_mutex_free(&o->hooks->lock);
1268                 hooks_free(o->hooks);
1269                 o->hooks = NULL;
1270         }
1271 }
1272
1273 /* return (allocate if required) the object's hook list, locking at the same time */
1274 static CamelHookList *
1275 camel_object_get_hooks(CamelObject *o)
1276 {
1277         static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
1278         CamelHookList *hooks;
1279
1280         /* if we have it, we dont have to do any other locking,
1281            otherwise use a global lock to setup the object's hook data */
1282         if (o->hooks == NULL) {
1283                 pthread_mutex_lock(&lock);
1284                 if (o->hooks == NULL) {
1285                         hooks = hooks_alloc();
1286                         g_static_rec_mutex_init(&hooks->lock);
1287                         hooks->flags = 0;
1288                         hooks->depth = 0;
1289                         hooks->list_length = 0;
1290                         hooks->list = NULL;
1291                         o->hooks = hooks;
1292                 }
1293                 pthread_mutex_unlock(&lock);
1294         }
1295         
1296         g_static_rec_mutex_lock(&o->hooks->lock);
1297         
1298         return o->hooks;        
1299 }
1300
1301 unsigned int
1302 camel_object_hook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data)
1303 {
1304         CamelObject *obj = vo;
1305         CamelHookPair *pair, *hook;
1306         CamelHookList *hooks;
1307         int id;
1308
1309         g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0);
1310         g_return_val_if_fail(name != NULL, 0);
1311         g_return_val_if_fail(func != NULL, 0);
1312
1313         hook = co_find_pair(obj->klass, name);
1314
1315         /* Check all interfaces on this object for events defined on them */
1316         if (hook == NULL) {
1317                 pair = co_find_pair_ptr(obj->klass, interface_name);
1318                 if (pair) {
1319                         GPtrArray *interfaces = pair->data;
1320                         int i;
1321
1322                         for (i=0;i<interfaces->len;i++) {
1323                                 hook = co_find_pair(interfaces->pdata[i], name);
1324                                 if (hook)
1325                                         goto setup;
1326                         }
1327                 }
1328
1329                 g_warning("camel_object_hook_event: trying to hook event `%s' in class `%s' with no defined events.",
1330                           name, obj->klass->name);
1331
1332                 return 0;
1333         }
1334 setup:
1335         /* setup hook pair */
1336         pair = pair_alloc();
1337         pair->name = hook->name;        /* effectively static! */
1338         pair->func.event = func;
1339         pair->data = data;
1340         pair->flags = 0;
1341         id = pair->id;
1342
1343         /* get the hook list object, locked, link in new event hook, unlock */
1344         hooks = camel_object_get_hooks(obj);
1345         pair->next = hooks->list;
1346         hooks->list = pair;
1347         hooks->list_length++;
1348         camel_object_unget_hooks(obj);
1349
1350         h(printf("%p hook event '%s' %p %p = %d\n", vo, name, func, data, id));
1351
1352         return id;
1353 }
1354
1355 void
1356 camel_object_remove_event(void *vo, unsigned int id)
1357 {
1358         CamelObject *obj = vo;
1359         CamelHookList *hooks;
1360         CamelHookPair *pair, *parent;
1361
1362         g_return_if_fail (CAMEL_IS_OBJECT (obj));
1363         g_return_if_fail (id != 0);
1364
1365         if (obj->hooks == NULL) {
1366                 g_warning("camel_object_unhook_event: trying to unhook `%u` from an instance of `%s' with no hooks",
1367                           id, obj->klass->name);
1368                 return;
1369         }
1370
1371         h(printf("%p remove event %d\n", vo, id));
1372
1373         /* scan hooks for this event, remove it, or flag it if we're busy */
1374         hooks = camel_object_get_hooks(obj);
1375         parent = (CamelHookPair *)&hooks->list;
1376         pair = parent->next;
1377         while (pair) {
1378                 if (pair->id == id
1379                     && (pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) {
1380                         if (hooks->depth > 0) {
1381                                 pair->flags |= CAMEL_HOOK_PAIR_REMOVED;
1382                                 hooks->flags |= CAMEL_HOOK_PAIR_REMOVED;
1383                         } else {
1384                                 parent->next = pair->next;
1385                                 pair_free(pair);
1386                                 hooks->list_length--;
1387                         }
1388                         camel_object_unget_hooks(obj);
1389                         return;
1390                 }
1391                 parent = pair;
1392                 pair = pair->next;
1393         }
1394         camel_object_unget_hooks(obj);
1395
1396         g_warning("camel_object_unhook_event: cannot find hook id %u in instance of `%s'",
1397                   id, obj->klass->name);
1398 }
1399
1400 void
1401 camel_object_unhook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data)
1402 {
1403         CamelObject *obj = vo;
1404         CamelHookList *hooks;
1405         CamelHookPair *pair, *parent;
1406
1407         g_return_if_fail (CAMEL_IS_OBJECT (obj));
1408         g_return_if_fail (name != NULL);
1409         g_return_if_fail (func != NULL);
1410
1411         if (obj->hooks == NULL) {
1412                 g_warning("camel_object_unhook_event: trying to unhook `%s` from an instance of `%s' with no hooks",
1413                           name, obj->klass->name);
1414                 return;
1415         }
1416
1417         h(printf("%p unhook event '%s' %p %p\n", vo, name, func, data));
1418
1419         /* scan hooks for this event, remove it, or flag it if we're busy */
1420         hooks = camel_object_get_hooks(obj);
1421         parent = (CamelHookPair *)&hooks->list;
1422         pair = parent->next;
1423         while (pair) {
1424                 if (pair->func.event == func
1425                     && pair->data == data
1426                     && strcmp(pair->name, name) == 0
1427                     && (pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) {
1428                         if (hooks->depth > 0) {
1429                                 pair->flags |= CAMEL_HOOK_PAIR_REMOVED;
1430                                 hooks->flags |= CAMEL_HOOK_PAIR_REMOVED;
1431                         } else {
1432                                 parent->next = pair->next;
1433                                 pair_free(pair);
1434                                 hooks->list_length--;
1435                         }
1436                         camel_object_unget_hooks(obj);
1437                         return;
1438                 }
1439                 parent = pair;
1440                 pair = pair->next;
1441         }
1442         camel_object_unget_hooks(obj);
1443
1444         g_warning("camel_object_unhook_event: cannot find hook/data pair %p/%p in an instance of `%s' attached to `%s'",
1445                   func, data, obj->klass->name, name);
1446 }
1447
1448 void
1449 camel_object_trigger_event(void *vo, const char * name, void *event_data)
1450 {
1451         CamelObject *obj = vo;
1452         CamelHookList *hooks;
1453         CamelHookPair *pair, **pairs, *parent, *hook;
1454         int i, size;
1455         const char *prepname;
1456
1457         g_return_if_fail (CAMEL_IS_OBJECT (obj));
1458         g_return_if_fail (name);
1459
1460         hook = co_find_pair(obj->klass, name);
1461         if (hook)
1462                 goto trigger;
1463
1464         if (obj->hooks == NULL)
1465                 return;
1466
1467         /* interface events can't have prep functions */
1468         pair = co_find_pair_ptr(obj->klass, interface_name);
1469         if (pair) {
1470                 GPtrArray *interfaces = pair->data;
1471                 
1472                 for (i=0;i<interfaces->len;i++) {
1473                         hook = co_find_pair(interfaces->pdata[i], name);
1474                         if (hook)
1475                                 goto trigger_interface;
1476                 }
1477         }
1478
1479         g_warning("camel_object_trigger_event: trying to trigger unknown event `%s' in class `%s'",
1480                   name, obj->klass->name);
1481
1482         return;
1483
1484 trigger:
1485         /* try prep function, if false, then quit */
1486         if (hook->func.prep != NULL && !hook->func.prep(obj, event_data))
1487                 return;
1488
1489         /* also, no hooks, dont bother going further */
1490         if (obj->hooks == NULL)
1491                 return;
1492 trigger_interface:
1493         /* lock the object for hook emission */
1494         camel_object_ref(obj);
1495         hooks = camel_object_get_hooks(obj);
1496         
1497         if (hooks->list) {
1498                 /* first, copy the items in the list, and say we're in an event */
1499                 hooks->depth++;
1500                 pair = hooks->list;
1501                 size = 0;
1502                 pairs = alloca(sizeof(pairs[0]) * hooks->list_length);
1503                 prepname = hook->name;
1504                 while (pair) {
1505                         if (pair->name == prepname)
1506                                 pairs[size++] = pair;
1507                         pair = pair->next;
1508                 }
1509
1510                 /* now execute the events we have, if they haven't been removed during our calls */
1511                 for (i=size-1;i>=0;i--) {
1512                         pair = pairs[i];
1513                         if ((pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0)
1514                                 (pair->func.event) (obj, event_data, pair->data);
1515                 }
1516                 hooks->depth--;
1517
1518                 /* and if we're out of any events, then clean up any pending removes */
1519                 if (hooks->depth == 0 && (hooks->flags & CAMEL_HOOK_PAIR_REMOVED)) {
1520                         parent = (CamelHookPair *)&hooks->list;
1521                         pair = parent->next;
1522                         while (pair) {
1523                                 if (pair->flags & CAMEL_HOOK_PAIR_REMOVED) {
1524                                         parent->next = pair->next;
1525                                         pair_free(pair);
1526                                         hooks->list_length--;
1527                                 } else {
1528                                         parent = pair;
1529                                 }
1530                                 pair = parent->next;
1531                         }
1532                         hooks->flags &= ~CAMEL_HOOK_PAIR_REMOVED;
1533                 }
1534         }
1535
1536         camel_object_unget_hooks(obj);
1537         camel_object_unref(obj);
1538 }
1539
1540 void *
1541 camel_object_get_interface(void *vo, CamelType itype)
1542 {
1543         CamelObject *obj = vo;
1544         CamelHookPair *pair;
1545
1546         g_return_val_if_fail(CAMEL_IS_OBJECT (obj), NULL);
1547         g_return_val_if_fail(camel_interface_is(itype, camel_interface_type), NULL);
1548
1549         pair = co_find_pair_ptr(obj->klass, interface_name);
1550         if (pair) {
1551                 GPtrArray *interfaces = pair->data;
1552                 int i;
1553
1554                 for (i=0;i<interfaces->len;i++) {
1555                         if (camel_interface_is((CamelType)interfaces->pdata[i], itype))
1556                                 return (CamelType)interfaces->pdata[i];
1557                 }
1558         }
1559
1560         g_warning("Object %p class %s doesn't contain interface %s\n", vo, obj->klass->name, itype->name);
1561
1562         return NULL;
1563 }
1564
1565 /* get/set arg methods */
1566 int camel_object_set(void *vo, CamelException *ex, ...)
1567 {
1568         CamelArgV args;
1569         CamelObject *o = vo;
1570         CamelObjectClass *klass = o->klass;
1571         int ret = 0;
1572
1573         g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1);
1574
1575         camel_argv_start(&args, ex);
1576
1577         while (camel_argv_build(&args) && ret == 0)
1578                 ret = klass->setv(o, ex, &args);
1579         if (ret == 0)
1580                 ret = klass->setv(o, ex, &args);
1581
1582         camel_argv_end(&args);
1583
1584         return ret;
1585 }
1586
1587 int camel_object_setv(void *vo, CamelException *ex, CamelArgV *args)
1588 {
1589         g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1);
1590
1591         return ((CamelObject *)vo)->klass->setv(vo, ex, args);
1592 }
1593
1594 int camel_object_get(void *vo, CamelException *ex, ...)
1595 {
1596         CamelObject *o = vo;
1597         CamelArgGetV args;
1598         CamelObjectClass *klass = o->klass;
1599         int ret = 0;
1600
1601         g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1);
1602
1603         camel_argv_start(&args, ex);
1604
1605         while (camel_arggetv_build(&args) && ret == 0)
1606                 ret = klass->getv(o, ex, &args);
1607         if (ret == 0)
1608                 ret = klass->getv(o, ex, &args);
1609
1610         camel_argv_end(&args);
1611
1612         return ret;
1613 }
1614
1615 void *camel_object_get_ptr(void *vo, CamelException *ex, int tag)
1616 {
1617         CamelObject *o = vo;
1618         CamelArgGetV args;
1619         CamelObjectClass *klass = o->klass;
1620         int ret = 0;
1621         void *val = NULL;
1622
1623         g_return_val_if_fail(CAMEL_IS_OBJECT(o), NULL);
1624         g_return_val_if_fail((tag & CAMEL_ARG_TYPE) == CAMEL_ARG_OBJ
1625                              || (tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR
1626                              || (tag & CAMEL_ARG_TYPE) == CAMEL_ARG_PTR, 0);
1627
1628         /* woefully inefficient, *shrug */
1629         args.argc = 1;
1630         args.argv[0].tag = tag;
1631         args.argv[0].ca_ptr = &val;
1632
1633         ret = klass->getv(o, ex, &args);
1634         if (ret != 0)
1635                 return NULL;
1636         else
1637                 return val;
1638 }
1639
1640 int camel_object_get_int(void *vo, CamelException *ex, int tag)
1641 {
1642         CamelObject *o = vo;
1643         CamelArgGetV args;
1644         CamelObjectClass *klass = o->klass;
1645         int ret = 0;
1646         int val = 0;
1647
1648         g_return_val_if_fail(CAMEL_IS_OBJECT(o), 0);
1649         g_return_val_if_fail((tag & CAMEL_ARG_TYPE) == CAMEL_ARG_INT
1650                              || (tag & CAMEL_ARG_TYPE) == CAMEL_ARG_BOO, 0);
1651
1652         /* woefully inefficient, *shrug */
1653         args.argc = 1;
1654         args.argv[0].tag = tag;
1655         args.argv[0].ca_int = &val;
1656
1657         ret = klass->getv(o, ex, &args);
1658         if (ret != 0)
1659                 return 0;
1660         else
1661                 return val;
1662 }
1663
1664 int camel_object_getv(void *vo, CamelException *ex, CamelArgGetV *args)
1665 {
1666         g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1);
1667
1668         return ((CamelObject *)vo)->klass->getv(vo, ex, args);
1669 }
1670
1671 /* NB: If this doesn't return NULL, then you must unget_hooks when done */
1672 static CamelHookPair *
1673 co_metadata_pair(CamelObject *obj, int create)
1674 {
1675         CamelHookPair *pair;
1676         CamelHookList *hooks;
1677
1678         if (obj->hooks == NULL && !create)
1679                 return NULL;
1680
1681         hooks = camel_object_get_hooks(obj);
1682         pair = hooks->list;
1683         while (pair) {
1684                 if (pair->name == meta_name)
1685                         return pair;
1686
1687                 pair = pair->next;
1688         }
1689
1690         if (create) {
1691                 pair = pair_alloc();
1692                 pair->name = meta_name;
1693                 pair->data = NULL;
1694                 pair->flags = 0;
1695                 pair->func.filename = NULL;
1696                 pair->next = hooks->list;
1697                 hooks->list = pair;
1698                 hooks->list_length++;
1699         } else {
1700                 camel_object_unget_hooks(obj);
1701         }
1702
1703         return pair;
1704 }
1705
1706 static CamelObjectMeta *
1707 co_metadata_get(CamelObject *obj)
1708 {
1709         CamelHookPair *pair;
1710         CamelObjectMeta *meta = NULL, *metaout = NULL, *metalast = NULL;
1711
1712         pair = co_metadata_pair(obj, FALSE);
1713         if (pair) {
1714                 meta = pair->data;
1715
1716                 while (meta) {
1717                         CamelObjectMeta *m;
1718
1719                         m = g_malloc(sizeof(*m) + strlen(meta->name));
1720                         m->next = NULL;
1721                         strcpy(m->name, meta->name);
1722                         m->value = g_strdup(meta->value);
1723                         if (metaout == NULL)
1724                                 metalast = metaout = m;
1725                         else {
1726                                 metalast->next = m;
1727                                 metalast = m;
1728                         }
1729                         meta = meta->next;
1730                 }
1731
1732                 camel_object_unget_hooks(obj);
1733         }
1734
1735         return metaout;
1736 }
1737
1738 static void
1739 co_metadata_free(CamelObject *obj, CamelObjectMeta *meta)
1740 {
1741         while (meta) {
1742                 CamelObjectMeta *metan = meta->next;
1743
1744                 g_free(meta->value);
1745                 g_free(meta);
1746                 meta = metan;
1747         }
1748 }
1749
1750 /**
1751  * camel_object_meta_get:
1752  * @vo: 
1753  * @name: 
1754  * 
1755  * Get a meta-data on an object.
1756  * 
1757  * Return value: NULL if the meta-data is not set.
1758  **/
1759 char *
1760 camel_object_meta_get(void *vo, const char * name)
1761 {
1762         CamelObject *obj = vo;
1763
1764         g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0);
1765         g_return_val_if_fail(name != NULL, 0);
1766
1767         return obj->klass->meta_get(obj, name);
1768 }
1769
1770 /**
1771  * camel_object_meta_set:
1772  * @vo: 
1773  * @name: Name of meta-data.  Should be prefixed with class of setter.
1774  * @value: Value to set.  If NULL, then the meta-data is removed.
1775  * 
1776  * Set a meta-data item on an object.  If the object supports persistent
1777  * data, then the meta-data will be persistent across sessions.
1778  *
1779  * If the meta-data changes, is added, or removed, then a
1780  * "meta_changed" event will be triggered with the name of the changed
1781  * data.
1782  *
1783  * Return Value: TRUE if the setting caused a change to the object's
1784  * metadata.
1785  **/
1786 gboolean
1787 camel_object_meta_set(void *vo, const char * name, const char *value)
1788 {
1789         CamelObject *obj = vo;
1790
1791         g_return_val_if_fail(CAMEL_IS_OBJECT (obj), FALSE);
1792         g_return_val_if_fail(name != NULL, FALSE);
1793
1794         if (obj->klass->meta_set(obj, name, value)) {
1795                 camel_object_trigger_event(obj, "meta_changed", (void *)name);
1796                 return TRUE;
1797         }
1798
1799         return FALSE;
1800 }
1801
1802 /**
1803  * camel_object_state_read:
1804  * @vo: 
1805  * 
1806  * Read persistent object state from object_set(CAMEL_OBJECT_STATE_FILE).
1807  * 
1808  * Return value: -1 on error.
1809  **/
1810 int camel_object_state_read(void *vo)
1811 {
1812         CamelObject *obj = vo;
1813         int res = -1;
1814         char *file;
1815         FILE *fp;
1816         char magic[4];
1817
1818         camel_object_get(vo, NULL, CAMEL_OBJECT_STATE_FILE, &file, NULL);
1819         if (file == NULL)
1820                 return 0;
1821
1822         fp = g_fopen(file, "rb");
1823         if (fp != NULL) {
1824                 if (fread(magic, 4, 1, fp) == 1
1825                     && memcmp(magic, CAMEL_OBJECT_STATE_FILE_MAGIC, 4) == 0)
1826                         res = obj->klass->state_read(obj, fp);
1827                 else
1828                         res = -1;
1829                 fclose(fp);
1830         }
1831
1832         camel_object_free(vo, CAMEL_OBJECT_STATE_FILE, file);
1833
1834         return res;
1835 }
1836
1837 /**
1838  * camel_object_state_write:
1839  * @vo: 
1840  * 
1841  * Write persistent state to the file as set by object_set(CAMEL_OBJECT_STATE_FILE).
1842  * 
1843  * Return value: -1 on error.
1844  **/
1845 int camel_object_state_write(void *vo)
1846 {
1847         CamelObject *obj = vo;
1848         int res = -1;
1849         char *file, *savename, *dirname;
1850         FILE *fp;
1851
1852         camel_object_get(vo, NULL, CAMEL_OBJECT_STATE_FILE, &file, NULL);
1853         if (file == NULL)
1854                 return 0;
1855
1856         savename = camel_file_util_savename(file);
1857         dirname = g_path_get_dirname(savename);
1858         g_mkdir_with_parents(dirname, 0777);
1859         g_free(dirname);
1860         fp = g_fopen(savename, "wb");
1861         if (fp != NULL) {
1862                 if (fwrite(CAMEL_OBJECT_STATE_FILE_MAGIC, 4, 1, fp) == 1
1863                     && obj->klass->state_write(obj, fp) == 0) {
1864                         if (fclose(fp) == 0) {
1865                                 res = 0;
1866                                 g_rename(savename, file);
1867                         }
1868                 } else {
1869                         fclose(fp);
1870                 }
1871         } else {
1872                 g_warning("Could not save object state file to '%s': %s", savename, g_strerror(errno));
1873         }
1874
1875         g_free(savename);
1876         camel_object_free(vo, CAMEL_OBJECT_STATE_FILE, file);
1877
1878         return res;
1879 }
1880
1881 /* free an arg object, you can only free objects 1 at a time */
1882 void camel_object_free(void *vo, guint32 tag, void *value)
1883 {
1884         g_return_if_fail(CAMEL_IS_OBJECT(vo));
1885
1886         /* We could also handle freeing of args differently
1887
1888            Add a 'const' bit to the arg type field,
1889            specifying that the object should not be freed.
1890            
1891            And, add free handlers for any pointer objects which are
1892            not const.  The free handlers would be hookpairs off of the
1893            class.
1894
1895            Then we handle the basic types OBJ,STR here, and pass PTR
1896            types to their appropriate handler, without having to pass
1897            through the invocation heirarchy of the free method.
1898
1899            This would also let us copy and do other things with args
1900            we can't do, but i can't see a use for that yet ...  */
1901
1902         ((CamelObject *)vo)->klass->free(vo, tag, value);
1903 }
1904
1905 static void
1906 object_class_dump_tree_rec(CamelType root, int depth)
1907 {
1908         char *p;
1909 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
1910         struct _CamelObject *o;
1911 #endif
1912
1913         p = alloca(depth*2+1);
1914         memset(p, ' ', depth*2);
1915         p[depth*2] = 0;
1916
1917         while (root) {
1918                 CLASS_LOCK(root);
1919                 printf("%sClass: %s\n", p, root->name);
1920                 /*printf("%sVersion: %u.%u\n", p, root->version, root->revision);*/
1921                 if (root->hooks) {
1922                         CamelHookPair *pair = root->hooks;
1923
1924                         while (pair) {
1925                                 printf("%s  event '%s' prep %p\n", p, pair->name, pair->func.prep);
1926                                 pair = pair->next;
1927                         }
1928                 }
1929 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
1930                 o = root->instances;
1931                 while (o) {
1932                         printf("%s instance %p [%d]\n", p, o, o->ref_count);
1933                         /* todo: should lock hooks while it scans them */
1934                         if (o->hooks) {
1935                                 CamelHookPair *pair = o->hooks->list;
1936
1937                                 while (pair) {
1938                                         printf("%s  hook '%s' func %p data %p\n", p, pair->name, pair->func.event, pair->data);
1939                                         pair = pair->next;
1940                                 }
1941                         }
1942                         o = o->next;
1943                 }
1944 #endif
1945                 CLASS_UNLOCK(root);
1946
1947                 if (root->child)
1948                         object_class_dump_tree_rec(root->child, depth+1);
1949
1950                 root = root->next;
1951         }
1952 }
1953
1954 void
1955 camel_object_class_dump_tree(CamelType root)
1956 {
1957         object_class_dump_tree_rec(root, 0);
1958 }
1959
1960 /**
1961  * camel_object_bag_new:
1962  * @hash: 
1963  * @equal: 
1964  * @keycopy: 
1965  * @keyfree: 
1966  * 
1967  * Allocate a new object bag.  Object bag's are key'd hash tables of
1968  * camel-objects which can be updated atomically using transaction
1969  * semantics.
1970  * 
1971  * Return value: 
1972  **/
1973 CamelObjectBag *
1974 camel_object_bag_new(GHashFunc hash, GEqualFunc equal, CamelCopyFunc keycopy, GFreeFunc keyfree)
1975 {
1976         CamelObjectBag *bag;
1977
1978         bag = g_malloc(sizeof(*bag));
1979         bag->object_table = g_hash_table_new(hash, equal);
1980         bag->equal_key = equal;
1981         bag->copy_key = keycopy;
1982         bag->free_key = keyfree;
1983         bag->key_table = g_hash_table_new(NULL, NULL);
1984         bag->reserved = NULL;
1985
1986         return bag;
1987 }
1988
1989 static void
1990 save_object(void *key, CamelObject *o, GPtrArray *objects)
1991 {
1992         g_ptr_array_add(objects, o);
1993 }
1994
1995 void
1996 camel_object_bag_destroy(CamelObjectBag *bag)
1997 {
1998         GPtrArray *objects = g_ptr_array_new();
1999         int i;
2000
2001         g_assert(bag->reserved == NULL);
2002
2003         g_hash_table_foreach(bag->object_table, (GHFunc)save_object, objects);
2004         for (i=0;i<objects->len;i++)
2005                 camel_object_bag_remove(bag, objects->pdata[i]);
2006         
2007         g_ptr_array_free(objects, TRUE);
2008         g_hash_table_destroy(bag->object_table);
2009         g_hash_table_destroy(bag->key_table);
2010         g_free(bag);
2011 }
2012
2013 /* must be called with ref_lock held */
2014 static void
2015 co_bag_unreserve(CamelObjectBag *bag, const void *key)
2016 {
2017         struct _CamelObjectBagKey *res, *resp;
2018
2019         resp = (struct _CamelObjectBagKey *)&bag->reserved;
2020         res = resp->next;
2021         while (res) {
2022                 if (bag->equal_key(res->key, key))
2023                         break;
2024                 resp = res;
2025                 res = res->next;
2026         }
2027
2028         g_assert(res != NULL);
2029         g_assert(res->have_owner && pthread_equal(res->owner, pthread_self()));
2030
2031         if (res->waiters > 0) {
2032                 b(printf("unreserve bag '%s', waking waiters\n", (char *)key));
2033                 res->have_owner = FALSE;
2034                 g_cond_signal(res->cond);
2035         } else {
2036                 b(printf("unreserve bag '%s', no waiters, freeing reservation\n", (char *)key));
2037                 resp->next = res->next;
2038                 bag->free_key(res->key);
2039                 g_cond_free(res->cond);
2040                 g_free(res);
2041         }
2042 }
2043
2044 /**
2045  * camel_object_bag_add:
2046  * @bag: 
2047  * @key: 
2048  * @vo: 
2049  * 
2050  * Add an object @vo to the object bag @bag.  The @key MUST have
2051  * previously been reserved using camel_object_bag_reserve().
2052  **/
2053 void
2054 camel_object_bag_add(CamelObjectBag *bag, const void *key, void *vo)
2055 {
2056         CamelObject *o = vo;
2057         CamelHookList *hooks;
2058         CamelHookPair *pair;
2059         void *k;
2060
2061         hooks = camel_object_get_hooks(o);
2062         REF_LOCK();
2063
2064         pair = hooks->list;
2065         while (pair) {
2066                 if (pair->name == bag_name && pair->data == bag) {
2067                         REF_UNLOCK();
2068                         camel_object_unget_hooks(o);
2069                         return;
2070                 }
2071                 pair = pair->next;
2072         }
2073
2074         pair = pair_alloc();
2075         pair->name = bag_name;
2076         pair->data = bag;
2077         pair->flags = 0;
2078         pair->func.event = NULL;
2079
2080         pair->next = hooks->list;
2081         hooks->list = pair;
2082         hooks->list_length++;
2083
2084         k = bag->copy_key(key);
2085         g_hash_table_insert(bag->object_table, k, vo);
2086         g_hash_table_insert(bag->key_table, vo, k);
2087
2088         co_bag_unreserve(bag, key);
2089         
2090         REF_UNLOCK();
2091         camel_object_unget_hooks(o);
2092 }
2093
2094 /**
2095  * camel_object_bag_get:
2096  * @bag: 
2097  * @key: 
2098  * 
2099  * Lookup an object by @key.  If the key is currently reserved, then
2100  * wait until the key has been committed before continuing.
2101  * 
2102  * Return value: NULL if the object corresponding to @key is not
2103  * in the bag.  Otherwise a ref'd object pointer which the caller owns
2104  * the ref to.
2105  **/
2106 void *
2107 camel_object_bag_get(CamelObjectBag *bag, const void *key)
2108 {
2109         CamelObject *o;
2110
2111         REF_LOCK();
2112
2113         o = g_hash_table_lookup(bag->object_table, key);
2114         if (o) {
2115                 b(printf("object bag get '%s' = %p\n", (char *)key, o));
2116
2117                 /* we use the same lock as the refcount */
2118                 o->ref_count++;
2119         } else {
2120                 struct _CamelObjectBagKey *res = bag->reserved;
2121
2122                 /* check if this name is reserved currently, if so wait till its finished */
2123                 while (res) {
2124                         if (bag->equal_key(res->key, key))
2125                                 break;
2126                         res = res->next;
2127                 }
2128
2129                 if (res) {
2130                         b(printf("object bag get '%s', reserved, waiting\n", (char *)key));
2131
2132                         res->waiters++;
2133                         g_assert(!res->have_owner || !pthread_equal(res->owner, pthread_self()));
2134                         g_cond_wait(res->cond, ref_lock);
2135                         res->waiters--;
2136
2137                         /* re-check if it slipped in */
2138                         o = g_hash_table_lookup(bag->object_table, key);
2139                         if (o)
2140                                 o->ref_count++;
2141
2142                         b(printf("object bag get '%s', finished waiting, got %p\n", (char *)key, o));
2143
2144                         /* we don't actually reserve it */
2145                         res->owner = pthread_self();
2146                         res->have_owner = TRUE;
2147                         co_bag_unreserve(bag, key);
2148                 }
2149         }
2150         
2151         REF_UNLOCK();
2152         
2153         return o;
2154 }
2155
2156 /**
2157  * camel_object_bag_peek:
2158  * @bag: 
2159  * @key: 
2160  * 
2161  * Lookup the object @key in @bag, ignoring any reservations.  If it
2162  * isn't committed, then it isn't considered.  This should only be
2163  * used where reliable transactional-based state is not required.
2164  * 
2165  * Unlike other 'peek' operations, the object is still reffed if
2166  * found.
2167  *
2168  * Return value: A referenced object, or NULL if @key is not
2169  * present in the bag.
2170  **/
2171 void *
2172 camel_object_bag_peek(CamelObjectBag *bag, const void *key)
2173 {
2174         CamelObject *o;
2175
2176         REF_LOCK();
2177
2178         o = g_hash_table_lookup(bag->object_table, key);
2179         if (o) {
2180                 /* we use the same lock as the refcount */
2181                 o->ref_count++;
2182         }
2183
2184         REF_UNLOCK();
2185
2186         return o;
2187 }
2188
2189 /**
2190  * camel_object_bag_reserve:
2191  * @bag: 
2192  * @key: 
2193  * 
2194  * Reserve a key in the object bag.  If the key is already reserved in
2195  * another thread, then wait until the reservation has been committed.
2196  *
2197  * After reserving a key, you either get a reffed pointer to the
2198  * object corresponding to the key, similar to object_bag_get, or you
2199  * get NULL, signifying that you then MIST call either object_bag_add
2200  * or object_bag_abort.
2201  *
2202  * You may reserve multiple keys from the same thread, but they should
2203  * always be reserved in the same order, to avoid deadlocks.
2204  * 
2205  * Return value: 
2206  **/
2207 void *
2208 camel_object_bag_reserve(CamelObjectBag *bag, const void *key)
2209 {
2210         CamelObject *o;
2211
2212         REF_LOCK();
2213
2214         o = g_hash_table_lookup(bag->object_table, key);
2215         if (o) {
2216                 o->ref_count++;
2217         } else {
2218                 struct _CamelObjectBagKey *res = bag->reserved;
2219
2220                 while (res) {
2221                         if (bag->equal_key(res->key, key))
2222                                 break;
2223                         res = res->next;
2224                 }
2225
2226                 if (res) {
2227                         b(printf("bag reserve %s, already reserved, waiting\n", (char *)key));
2228                         g_assert(!res->have_owner || !pthread_equal(res->owner, pthread_self()));
2229                         res->waiters++;
2230                         g_cond_wait(res->cond, ref_lock);
2231                         res->waiters--;
2232                         /* incase its slipped in while we were waiting */
2233                         o = g_hash_table_lookup(bag->object_table, key);
2234                         if (o) {
2235                                 b(printf("finished wait, someone else created '%s' = %p\n", (char *)key, o));
2236                                 o->ref_count++;
2237                                 /* in which case we dont need to reserve the bag either */
2238                                 res->owner = pthread_self();
2239                                 res->have_owner = TRUE;
2240                                 co_bag_unreserve(bag, key);
2241                         } else {
2242                                 b(printf("finished wait, now owner of '%s'\n", (char *)key));
2243                                 res->owner = pthread_self();
2244                                 res->have_owner = TRUE;
2245                         }
2246                 } else {
2247                         b(printf("bag reserve %s, no key, reserving\n", (char *)key));
2248                         res = g_malloc(sizeof(*res));
2249                         res->waiters = 0;
2250                         res->key = bag->copy_key(key);
2251                         res->cond = g_cond_new();
2252                         res->owner = pthread_self();
2253                         res->have_owner = TRUE;
2254                         res->next = bag->reserved;
2255                         bag->reserved = res;
2256                 }
2257         }
2258         
2259         REF_UNLOCK();
2260
2261         return o;
2262 }
2263
2264 /**
2265  * camel_object_bag_abort:
2266  * @bag: 
2267  * @key: 
2268  * 
2269  * Abort a key reservation.
2270  **/
2271 void
2272 camel_object_bag_abort(CamelObjectBag *bag, const void *key)
2273 {
2274         REF_LOCK();
2275
2276         co_bag_unreserve(bag, key);
2277
2278         REF_UNLOCK();
2279 }
2280
2281 /**
2282  * camel_object_bag_rekey:
2283  * @bag: 
2284  * @o: 
2285  * @newkey: 
2286  * 
2287  * Re-key an object, atomically.  The key for object @o is set to
2288  * @newkey, in an atomic manner.
2289  *
2290  * It is an api (fatal) error if @o is not currently in the bag.
2291  **/
2292 void
2293 camel_object_bag_rekey(CamelObjectBag *bag, void *o, const void *newkey)
2294 {
2295         void *oldkey;
2296
2297         REF_LOCK();
2298
2299         if (g_hash_table_lookup_extended(bag->key_table, o, NULL, &oldkey)) {
2300                 g_hash_table_remove(bag->object_table, oldkey);
2301                 g_hash_table_remove(bag->key_table, o);
2302                 bag->free_key(oldkey);
2303                 oldkey = bag->copy_key(newkey);
2304                 g_hash_table_insert(bag->object_table, oldkey, o);
2305                 g_hash_table_insert(bag->key_table, o, oldkey);
2306         } else {
2307                 abort();
2308         }
2309
2310         REF_UNLOCK();
2311 }
2312
2313 static void
2314 save_bag(void *key, CamelObject *o, GPtrArray *list)
2315 {
2316         /* we have the refcount lock already */
2317         o->ref_count++;
2318         g_ptr_array_add(list, o);
2319 }
2320
2321 /* get a list of all objects in the bag, ref'd
2322    ignores any reserved keys */
2323 GPtrArray *
2324 camel_object_bag_list(CamelObjectBag *bag)
2325 {
2326         GPtrArray *list;
2327
2328         list = g_ptr_array_new();
2329
2330         REF_LOCK();
2331         g_hash_table_foreach(bag->object_table, (GHFunc)save_bag, list);
2332         REF_UNLOCK();
2333
2334         return list;
2335 }
2336
2337 /* if bag is NULL, remove all bags from object */
2338 static void
2339 camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks)
2340 {
2341         CamelHookPair *pair, *parent;
2342         void *oldkey;
2343         CamelObjectBag *bag;
2344
2345         parent = (CamelHookPair *)&hooks->list;
2346         pair = parent->next;
2347         while (pair) {
2348                 if (pair->name == bag_name
2349                     && (inbag == NULL || inbag == pair->data)) {
2350                         bag = pair->data;
2351                         /* lookup object in table? */
2352                         oldkey = g_hash_table_lookup(bag->key_table, o);
2353                         if (oldkey) {
2354                                 g_hash_table_remove(bag->key_table, o);
2355                                 g_hash_table_remove(bag->object_table, oldkey);
2356                                 bag->free_key(oldkey);
2357                         }
2358                         parent->next = pair->next;
2359                         pair_free(pair);
2360                         hooks->list_length--;
2361                 } else {
2362                         parent = pair;
2363                 }
2364                 pair = parent->next;
2365         }
2366 }
2367
2368 void
2369 camel_object_bag_remove(CamelObjectBag *inbag, void *vo)
2370 {
2371         CamelObject *o = vo;
2372         CamelHookList *hooks;
2373
2374         if (o->hooks == NULL)
2375                 return;
2376
2377         hooks = camel_object_get_hooks(o);
2378         REF_LOCK();
2379
2380         camel_object_bag_remove_unlocked(inbag, o, hooks);
2381                 
2382         REF_UNLOCK();
2383         camel_object_unget_hooks(o);
2384 }
2385
2386 /* ********************************************************************** */
2387
2388 void *camel_iterator_new(CamelIteratorVTable *klass, size_t size)
2389 {
2390         CamelIterator *it;
2391
2392         g_assert(size >= sizeof(CamelIterator));
2393
2394         it = g_malloc0(size);
2395         it->klass = klass;
2396
2397         return it;
2398 }
2399
2400 const void *camel_iterator_next(void *it, CamelException *ex)
2401 {
2402         g_assert(it);
2403         return ((CamelIterator *)it)->klass->next(it, ex);
2404 }
2405
2406 void camel_iterator_reset(void *it)
2407 {
2408         g_assert(it);
2409         ((CamelIterator *)it)->klass->reset(it);
2410 }
2411
2412 int camel_iterator_length(void *it)
2413 {
2414         g_assert(it);
2415         g_return_val_if_fail(((CamelIterator *)it)->klass->length != NULL, 0);
2416         
2417         return ((CamelIterator *)it)->klass->length(it);
2418 }
2419
2420 void camel_iterator_free(void *it)
2421 {
2422         if (it) {
2423                 if (((CamelIterator *)it)->klass->free)
2424                         ((CamelIterator *)it)->klass->free(it);
2425                 g_free(it);
2426         }
2427 }