1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
4 * Michael Zucchi <notzed@ximian.com>
6 * Copyright 2000-2003 Ximian, Inc. (www.ximian.com)
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.
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.
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
33 #include <glib/gstdio.h>
35 #include <libedataserver/e-data-server-util.h>
36 #include <libedataserver/e-memory.h>
37 #include <libedataserver/e-msgport.h>
39 #include "camel-file-utils.h"
40 #include "camel-object.h"
43 #define b(x) /* object bag */
44 #define h(x) /* hooks */
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
54 /* ** Quickie type system ************************************************* */
56 /* A 'locked' hooklist, that is only allocated on demand */
57 typedef struct _CamelHookList {
60 unsigned int depth:30; /* recursive event depth */
61 unsigned int flags:2; /* flags, see below */
63 unsigned int list_length;
64 struct _CamelHookPair *list;
67 #define CAMEL_HOOK_PAIR_REMOVED (1<<0)
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
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
75 typedef struct _CamelHookPair
77 struct _CamelHookPair *next; /* next MUST be the first member */
80 unsigned int flags:2; /* removed, etc */
82 const char *name; /* points to the key field in the classes preplist, static memory */
84 CamelObjectEventHookFunc event;
85 CamelObjectEventPrepFunc prep;
91 struct _CamelObjectBagKey {
92 struct _CamelObjectBagKey *next;
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 */
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;
108 struct _CamelObjectBagKey *reserved;
111 /* used to tag a bag hookpair */
112 static const char bag_name[] = "object:bag";
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);
119 static const char meta_name[] = "object:meta";
120 #define CAMEL_OBJECT_STATE_FILE_MAGIC "CLMD"
122 /* interface stuff */
123 static const char interface_name[] = "object:interface";
125 /* ********************************************************************** */
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);
131 #define camel_object_unget_hooks(o) \
132 (g_static_rec_mutex_unlock(&CAMEL_OBJECT(o)->hooks->lock))
135 /* ********************************************************************** */
137 static pthread_mutex_t chunks_lock = PTHREAD_MUTEX_INITIALIZER;
139 static EMemChunk *pair_chunks;
140 static EMemChunk *hook_chunks;
141 static unsigned int pair_id = 1;
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;
148 static GHashTable *type_table;
149 static EMemChunk *type_chunks;
151 /* fundamental types are accessed via global */
152 CamelType camel_object_type;
153 CamelType camel_interface_type;
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))
164 static struct _CamelHookPair *
170 pair = e_memchunk_alloc(pair_chunks);
171 pair->id = pair_id++;
174 P_UNLOCK(chunks_lock);
180 pair_free(CamelHookPair *pair)
182 g_assert(pair_chunks != NULL);
185 e_memchunk_free(pair_chunks, pair);
186 P_UNLOCK(chunks_lock);
189 static struct _CamelHookList *
192 CamelHookList *hooks;
195 hooks = e_memchunk_alloc(hook_chunks);
196 P_UNLOCK(chunks_lock);
202 hooks_free(CamelHookList *hooks)
204 g_assert(hook_chunks != NULL);
207 e_memchunk_free(hook_chunks, hooks);
208 P_UNLOCK(chunks_lock);
211 /* not checked locked, who cares, only required for people that want to redefine root objects */
213 camel_type_init(void)
215 static int init = FALSE;
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();
228 /* ************************************************************************ */
230 /* CamelObject base methods */
232 /* Should this return the object to the caller? */
234 cobject_init(CamelObject *o, CamelObjectClass *klass)
237 o->magic = CAMEL_OBJECT_MAGIC;
243 cobject_finalise(CamelObject *o)
245 /*printf("%p: finalise %s\n", o, o->klass->name);*/
247 if (o->ref_count == 0)
250 camel_object_free_hooks(o);
252 o->magic = CAMEL_OBJECT_FINALISED_MAGIC;
257 cobject_getv(CamelObject *o, CamelException *ex, CamelArgGetV *args)
262 for (i=0;i<args->argc;i++) {
263 CamelArgGet *arg = &args->argv[i];
267 switch (tag & CAMEL_ARG_TAG) {
268 case CAMEL_OBJECT_ARG_DESCRIPTION:
269 *arg->ca_str = (char *)o->klass->name;
271 case CAMEL_OBJECT_ARG_METADATA:
272 *arg->ca_ptr = co_metadata_get(o);
274 case CAMEL_OBJECT_ARG_STATE_FILE: {
275 CamelHookPair *pair = co_metadata_pair(o, FALSE);
278 *arg->ca_str = g_strdup(pair->func.filename);
279 camel_object_unget_hooks(o);
285 /* could have flags or stuff here? */
290 cobject_setv(CamelObject *o, CamelException *ex, CamelArgV *args)
295 for (i=0;i<args->argc;i++) {
296 CamelArg *arg = &args->argv[i];
300 switch (tag & CAMEL_ARG_TAG) {
301 case CAMEL_OBJECT_ARG_STATE_FILE: {
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);
313 /* could have flags or stuff here? */
318 cobject_free(CamelObject *o, guint32 tag, void *value)
320 switch(tag & CAMEL_ARG_TAG) {
321 case CAMEL_OBJECT_ARG_METADATA:
322 co_metadata_free(o, value);
324 case CAMEL_OBJECT_ARG_STATE_FILE:
327 case CAMEL_OBJECT_ARG_PERSISTENT_PROPERTIES:
328 g_slist_free((GSList *)value);
334 cobject_meta_get(CamelObject *obj, const char * name)
337 CamelObjectMeta *meta;
340 g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0);
341 g_return_val_if_fail(name != NULL, 0);
343 pair = co_metadata_pair(obj, FALSE);
347 if (!strcmp(meta->name, name)) {
348 res = g_strdup(meta->value);
353 camel_object_unget_hooks(obj);
360 cobject_meta_set(CamelObject *obj, const char * name, const char *value)
364 CamelObjectMeta *meta, *metap;
366 g_return_val_if_fail(CAMEL_IS_OBJECT (obj), FALSE);
367 g_return_val_if_fail(name != NULL, FALSE);
369 if (obj->hooks == NULL && value == NULL)
372 pair = co_metadata_pair(obj, TRUE);
374 metap = (CamelObjectMeta *)&pair->data;
376 if (!strcmp(meta->name, name))
382 /* TODO: The camelobjectmeta structure is identical to
383 CamelTag, they could be merged or share common code */
387 meta = g_malloc(sizeof(*meta) + strlen(name));
388 meta->next = pair->data;
390 strcpy(meta->name, name);
391 meta->value = g_strdup(value);
393 } else if (value == NULL) {
394 metap->next = meta->next;
398 } else if (strcmp(meta->value, value) != 0) {
400 meta->value = g_strdup(value);
405 camel_object_unget_hooks(obj);
410 /* State file for CamelObject data. Any later versions should only append data.
414 Version 0 of the file:
417 count:uint32 -- count of meta-data items
418 ( name:string value:string ) *count -- meta-data items
420 Version 1 of the file adds:
421 count:uint32 -- count of persistent properties
422 ( tag:uing32 value:tagtype ) *count -- persistent properties
427 cobject_state_read(CamelObject *obj, FILE *fp)
429 guint32 i, count, version;
431 /* NB: for later versions, just check the version is 1 .. known version */
432 if (camel_file_util_decode_uint32(fp, &version) == -1
434 || camel_file_util_decode_uint32(fp, &count) == -1)
437 for (i=0;i<count;i++) {
438 char *name = NULL, *value = NULL;
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);
456 if (camel_file_util_decode_uint32(fp, &count) == -1
457 || count == 0 || count > 1024) {
458 /* maybe it was just version 0 afterall */
462 count = MIN(count, CAMEL_ARGV_MAX);
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)));
471 for (i=0;i<count;i++) {
472 if (camel_file_util_decode_uint32(fp, &argv->argv[argv->argc].tag) == -1)
475 /* so far,only do strings and ints, doubles could be added,
476 object's would require a serialisation interface */
478 switch(argv->argv[argv->argc].tag & CAMEL_ARG_TYPE) {
481 if (camel_file_util_decode_uint32(fp, &argv->argv[argv->argc].ca_int) == -1)
485 if (camel_file_util_decode_string(fp, &argv->argv[argv->argc].ca_str) == -1)
495 camel_object_setv(obj, NULL, argv);
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);
507 /* TODO: should pass exception around */
509 cobject_state_write(CamelObject *obj, FILE *fp)
512 CamelObjectMeta *meta = NULL, *scan;
514 GSList *props = NULL, *l;
515 CamelArgGetV *arggetv = NULL;
516 CamelArgV *argv = NULL;
518 camel_object_get(obj, NULL, CAMEL_OBJECT_METADATA, &meta, NULL);
527 /* current version is 1 */
528 if (camel_file_util_encode_uint32(fp, 1) == -1
529 || camel_file_util_encode_uint32(fp, count) == -1)
534 if (camel_file_util_encode_string(fp, scan->name) == -1
535 || camel_file_util_encode_string(fp, scan->value) == -1)
540 camel_object_get(obj, NULL, CAMEL_OBJECT_PERSISTENT_PROPERTIES, &props, NULL);
542 /* we build an arggetv to query the object atomically,
543 we also need an argv to store the results - bit messy */
545 count = g_slist_length(props);
546 count = MIN(count, CAMEL_ARGV_MAX);
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)));
555 CamelProperty *prop = l->data;
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;
567 camel_object_getv(obj, NULL, arggetv);
569 if (camel_file_util_encode_uint32(fp, count) == -1)
572 for (i=0;i<argv->argc;i++) {
573 CamelArg *arg = &argv->argv[i];
575 if (camel_file_util_encode_uint32(fp, arg->tag) == -1)
578 switch (arg->tag & CAMEL_ARG_TYPE) {
581 if (camel_file_util_encode_uint32(fp, arg->ca_int) == -1)
585 if (camel_file_util_encode_string(fp, arg->ca_str) == -1)
593 for (i=0;i<argv->argc;i++) {
594 CamelArg *arg = &argv->argv[i];
596 if ((argv->argv[i].tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR)
597 camel_object_free(obj, arg->tag, arg->ca_str);
604 camel_object_free(obj, CAMEL_OBJECT_PERSISTENT_PROPERTIES, props);
607 camel_object_free(obj, CAMEL_OBJECT_METADATA, meta);
614 cobject_class_init(CamelObjectClass *klass)
616 klass->magic = CAMEL_OBJECT_CLASS_MAGIC;
618 klass->getv = cobject_getv;
619 klass->setv = cobject_setv;
620 klass->free = cobject_free;
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;
627 camel_object_class_add_event(klass, "finalize", NULL);
628 camel_object_class_add_event(klass, "meta_changed", NULL);
632 cobject_class_finalise(CamelObjectClass * klass)
634 klass->magic = CAMEL_OBJECT_CLASS_FINALISED_MAGIC;
640 /* CamelInterface base methods */
643 cinterface_init(CamelObject *o, CamelObjectClass *klass)
645 g_error("Cannot instantiate interfaces, trying to instantiate '%s'", klass->name);
650 cinterface_getv(CamelObject *o, CamelException *ex, CamelArgGetV *args)
656 cinterface_setv(CamelObject *o, CamelException *ex, CamelArgV *args)
662 cinterface_free(CamelObject *o, guint32 tag, void *value)
668 cinterface_class_init(CamelObjectClass *klass)
670 klass->magic = CAMEL_INTERFACE_MAGIC;
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;
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;
685 cinterface_class_finalise(CamelObjectClass * klass)
687 klass->magic = CAMEL_INTERFACE_FINALISED_MAGIC;
691 /* this function must be called for any other in the object system */
693 camel_object_get_type(void)
695 if (camel_object_type == CAMEL_INVALID_TYPE) {
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);
703 camel_interface_type = camel_type_register(NULL, "CamelInterface",
704 0, sizeof(CamelInterface),
705 cinterface_class_init, cinterface_class_finalise,
706 cinterface_init, NULL);
710 return camel_object_type;
714 camel_type_class_init(CamelObjectClass *klass, CamelObjectClass *type)
717 camel_type_class_init(klass, type->parent);
719 if (type->klass_init)
720 type->klass_init(klass);
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)
732 CamelObjectClass *klass;
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);
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);
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);
755 size = offset + klass_size;
757 klass = g_malloc0(size);
759 klass->klass_size = size;
760 klass->klass_data = offset;
762 offset = parent?parent->object_size:0;
763 offset = (offset + 3) & (~3);
765 klass->object_size = offset + object_size;
766 klass->object_data = offset;*/
769 && klass_size < parent->klass_size) {
770 g_warning("camel_type_register: '%s' has smaller class size than parent '%s'", name, parent->name);
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);
781 klass->parent = parent;
783 klass->next = parent->child;
784 parent->child = klass;
788 /*klass->version = ver;
789 klass->revision = rev;*/
791 klass->klass_init = class_init;
792 klass->klass_finalise = class_finalise;
794 klass->init = object_init;
795 klass->finalise = object_finalise;
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);
800 camel_type_class_init(klass, klass);
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)
816 if (parent != NULL && parent->magic != CAMEL_OBJECT_CLASS_MAGIC) {
817 g_warning("camel_type_register: invalid junk parent class for '%s'", name);
821 return co_type_register(parent, name, object_size, klass_size, class_init, class_finalise, object_init, object_finalise);
825 camel_interface_register(CamelType parent, const char *name,
827 CamelObjectClassInitFunc class_init,
828 CamelObjectClassFinalizeFunc class_finalise)
830 if (parent != NULL && parent->magic != CAMEL_INTERFACE_MAGIC) {
831 g_warning("camel_interface_register: invalid junk parent class for '%s'", name);
835 return camel_type_register(parent, name, 0, class_size, class_init, class_finalise, NULL, NULL);
839 camel_object_init(CamelObject *o, CamelObjectClass *klass, CamelType type)
842 camel_object_init(o, klass, type->parent);
845 type->init(o, klass);
849 camel_object_new(CamelType type)
856 if (type->magic != CAMEL_OBJECT_CLASS_MAGIC)
861 o = e_memchunk_alloc0(type->instance_chunks);
863 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
865 type->instances->prev = o;
866 o->next = type->instances;
873 camel_object_init(o, type, type);
875 d(printf("%p: new %s()\n", o, o->klass->name));
881 camel_object_ref(void *vo)
883 register CamelObject *o = vo;
885 g_return_if_fail(CAMEL_IS_OBJECT(o));
890 d(printf("%p: ref %s(%d)\n", o, o->klass->name, o->ref_count));
896 camel_object_unref(void *vo)
898 register CamelObject *o = vo;
899 register CamelObjectClass *klass, *k;
900 CamelHookList *hooks = NULL;
902 g_return_if_fail(CAMEL_IS_OBJECT(o));
907 hooks = camel_object_get_hooks(o);
913 d(printf("%p: unref %s(%d)\n", o, o->klass->name, o->ref_count));
916 || (o->flags & CAMEL_OBJECT_DESTROY)) {
919 camel_object_unget_hooks(o);
923 o->flags |= CAMEL_OBJECT_DESTROY;
926 camel_object_bag_remove_unlocked(NULL, o, hooks);
931 camel_object_unget_hooks(o);
933 camel_object_trigger_event(o, "finalize", NULL);
942 o->magic = CAMEL_OBJECT_FINALISED_MAGIC;
945 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
947 o->prev->next = o->next;
949 klass->instances = o->next;
951 o->next->prev = o->prev;
953 e_memchunk_free(klass->instance_chunks, o);
958 camel_type_to_name(CamelType type)
961 return "(NULL class)";
963 if (type->magic == CAMEL_OBJECT_CLASS_MAGIC)
966 if (type->magic == CAMEL_INTERFACE_MAGIC)
969 return "(Junk class)";
972 CamelType camel_name_to_type(const char *name)
974 /* TODO: Load a class off disk (!) */
976 return g_hash_table_lookup(type_table, name);
980 desc_data(CamelObject *o, guint32 ok)
985 what = g_strdup("NULL OBJECT");
986 else if (o->magic == ok)
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");
1001 what = g_strdup_printf("junk data");
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)
1012 check_magic_fail(void *o, CamelType ctype, guint32 omagic)
1016 what = desc_data(o, omagic);
1017 to = desc_data((CamelObject *)ctype, CAMEL_OBJECT_CLASS_MAGIC);
1021 if (omagic == CAMEL_OBJECT_MAGIC)
1022 what = g_strdup_printf("OBJECT '%s'", ((CamelObject *)o)->klass->name);
1024 what = g_strdup_printf("OBJECT '%s'", ((CamelObjectClass *)o)->name);
1027 to = g_strdup_printf("OBJECT '%s'", ctype->name);
1028 g_warning("Trying to check %s is %s", what, to);
1039 camel_object_is(CamelObject *o, CamelType ctype)
1041 CamelObjectClass *k;
1043 g_return_val_if_fail(o != NULL, FALSE);
1044 g_return_val_if_fail(check_magic(o, ctype, CAMEL_OBJECT_MAGIC), FALSE);
1057 camel_object_class_is(CamelObjectClass *k, CamelType ctype)
1059 g_return_val_if_fail(k != NULL, FALSE);
1060 g_return_val_if_fail(check_magic(k, ctype, CAMEL_OBJECT_CLASS_MAGIC), FALSE);
1072 camel_interface_is(CamelObjectClass *k, CamelType ctype)
1074 g_return_val_if_fail(k != NULL, FALSE);
1075 g_return_val_if_fail(check_magic(k, ctype, CAMEL_INTERFACE_MAGIC), FALSE);
1087 camel_object_cast(CamelObject *o, CamelType ctype)
1089 CamelObjectClass *k;
1091 g_return_val_if_fail(check_magic(o, ctype, CAMEL_OBJECT_MAGIC), NULL);
1100 g_warning("Object %p (class '%s') doesn't have '%s' in its hierarchy", o, o->klass->name, ctype->name);
1106 camel_object_class_cast(CamelObjectClass *k, CamelType ctype)
1108 CamelObjectClass *r = k;
1110 g_return_val_if_fail(check_magic(k, ctype, CAMEL_OBJECT_CLASS_MAGIC), NULL);
1118 g_warning("Class '%s' doesn't have '%s' in its hierarchy", r->name, ctype->name);
1124 camel_interface_cast(CamelObjectClass *k, CamelType ctype)
1126 CamelObjectClass *r = k;
1128 g_return_val_if_fail(check_magic(k, ctype, CAMEL_INTERFACE_MAGIC), NULL);
1136 g_warning("Interface '%s' doesn't have '%s' in its hierarchy", r->name, ctype->name);
1141 static CamelHookPair *
1142 co_find_pair(CamelObjectClass *klass, const char *name)
1144 CamelHookPair *hook;
1146 hook = klass->hooks;
1148 if (strcmp(hook->name, name) == 0)
1156 static CamelHookPair *
1157 co_find_pair_ptr(CamelObjectClass *klass, const char *name)
1159 CamelHookPair *hook;
1161 hook = klass->hooks;
1163 if (hook->name == name)
1171 /* class functions */
1173 camel_object_class_add_event(CamelObjectClass *klass, const char *name, CamelObjectEventPrepFunc prep)
1175 CamelHookPair *pair;
1177 g_return_if_fail (name);
1179 pair = co_find_pair(klass, name);
1181 g_warning("camel_object_class_add_event: `%s' is already declared for '%s'",
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",
1192 pair = pair_alloc();
1194 pair->func.prep = prep;
1197 pair->next = klass->hooks;
1198 klass->hooks = pair;
1202 camel_object_class_add_interface(CamelObjectClass *klass, CamelType itype)
1204 CamelHookPair *pair;
1206 GPtrArray *interfaces;
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);
1214 if (camel_object_class_is(klass, camel_interface_type)) {
1215 g_warning("Cannot add an interface onto a class derived from CamelInterface");
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);
1222 pair = pair_alloc();
1223 pair->data = g_ptr_array_new();
1224 pair->next = klass->hooks;
1225 klass->hooks = pair;
1228 /* We just check that this type isn't added/derived anywhere else */
1229 interfaces = pair->data;
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);
1238 iscan = iscan->parent;
1241 if (iscan == camel_interface_type)
1242 g_ptr_array_add(interfaces, itype);
1245 /* free hook data */
1247 camel_object_free_hooks(CamelObject *o)
1249 CamelHookPair *pair, *next;
1252 g_assert(o->hooks->depth == 0);
1253 g_assert((o->hooks->flags & CAMEL_HOOK_PAIR_REMOVED) == 0);
1255 pair = o->hooks->list;
1259 if (pair->name == meta_name) {
1260 co_metadata_free(o, pair->data);
1261 g_free(pair->func.filename);
1267 g_static_rec_mutex_free(&o->hooks->lock);
1268 hooks_free(o->hooks);
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)
1277 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
1278 CamelHookList *hooks;
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);
1289 hooks->list_length = 0;
1293 pthread_mutex_unlock(&lock);
1296 g_static_rec_mutex_lock(&o->hooks->lock);
1302 camel_object_hook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data)
1304 CamelObject *obj = vo;
1305 CamelHookPair *pair, *hook;
1306 CamelHookList *hooks;
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);
1313 hook = co_find_pair(obj->klass, name);
1315 /* Check all interfaces on this object for events defined on them */
1317 pair = co_find_pair_ptr(obj->klass, interface_name);
1319 GPtrArray *interfaces = pair->data;
1322 for (i=0;i<interfaces->len;i++) {
1323 hook = co_find_pair(interfaces->pdata[i], name);
1329 g_warning("camel_object_hook_event: trying to hook event `%s' in class `%s' with no defined events.",
1330 name, obj->klass->name);
1335 /* setup hook pair */
1336 pair = pair_alloc();
1337 pair->name = hook->name; /* effectively static! */
1338 pair->func.event = func;
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;
1347 hooks->list_length++;
1348 camel_object_unget_hooks(obj);
1350 h(printf("%p hook event '%s' %p %p = %d\n", vo, name, func, data, id));
1356 camel_object_remove_event(void *vo, unsigned int id)
1358 CamelObject *obj = vo;
1359 CamelHookList *hooks;
1360 CamelHookPair *pair, *parent;
1362 g_return_if_fail (CAMEL_IS_OBJECT (obj));
1363 g_return_if_fail (id != 0);
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);
1371 h(printf("%p remove event %d\n", vo, id));
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;
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;
1384 parent->next = pair->next;
1386 hooks->list_length--;
1388 camel_object_unget_hooks(obj);
1394 camel_object_unget_hooks(obj);
1396 g_warning("camel_object_unhook_event: cannot find hook id %u in instance of `%s'",
1397 id, obj->klass->name);
1401 camel_object_unhook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data)
1403 CamelObject *obj = vo;
1404 CamelHookList *hooks;
1405 CamelHookPair *pair, *parent;
1407 g_return_if_fail (CAMEL_IS_OBJECT (obj));
1408 g_return_if_fail (name != NULL);
1409 g_return_if_fail (func != NULL);
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);
1417 h(printf("%p unhook event '%s' %p %p\n", vo, name, func, data));
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;
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;
1432 parent->next = pair->next;
1434 hooks->list_length--;
1436 camel_object_unget_hooks(obj);
1442 camel_object_unget_hooks(obj);
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);
1449 camel_object_trigger_event(void *vo, const char * name, void *event_data)
1451 CamelObject *obj = vo;
1452 CamelHookList *hooks;
1453 CamelHookPair *pair, **pairs, *parent, *hook;
1455 const char *prepname;
1457 g_return_if_fail (CAMEL_IS_OBJECT (obj));
1458 g_return_if_fail (name);
1460 hook = co_find_pair(obj->klass, name);
1464 if (obj->hooks == NULL)
1467 /* interface events can't have prep functions */
1468 pair = co_find_pair_ptr(obj->klass, interface_name);
1470 GPtrArray *interfaces = pair->data;
1472 for (i=0;i<interfaces->len;i++) {
1473 hook = co_find_pair(interfaces->pdata[i], name);
1475 goto trigger_interface;
1479 g_warning("camel_object_trigger_event: trying to trigger unknown event `%s' in class `%s'",
1480 name, obj->klass->name);
1485 /* try prep function, if false, then quit */
1486 if (hook->func.prep != NULL && !hook->func.prep(obj, event_data))
1489 /* also, no hooks, dont bother going further */
1490 if (obj->hooks == NULL)
1493 /* lock the object for hook emission */
1494 camel_object_ref(obj);
1495 hooks = camel_object_get_hooks(obj);
1498 /* first, copy the items in the list, and say we're in an event */
1502 pairs = alloca(sizeof(pairs[0]) * hooks->list_length);
1503 prepname = hook->name;
1505 if (pair->name == prepname)
1506 pairs[size++] = pair;
1510 /* now execute the events we have, if they haven't been removed during our calls */
1511 for (i=size-1;i>=0;i--) {
1513 if ((pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0)
1514 (pair->func.event) (obj, event_data, pair->data);
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;
1523 if (pair->flags & CAMEL_HOOK_PAIR_REMOVED) {
1524 parent->next = pair->next;
1526 hooks->list_length--;
1530 pair = parent->next;
1532 hooks->flags &= ~CAMEL_HOOK_PAIR_REMOVED;
1536 camel_object_unget_hooks(obj);
1537 camel_object_unref(obj);
1541 camel_object_get_interface(void *vo, CamelType itype)
1543 CamelObject *obj = vo;
1544 CamelHookPair *pair;
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);
1549 pair = co_find_pair_ptr(obj->klass, interface_name);
1551 GPtrArray *interfaces = pair->data;
1554 for (i=0;i<interfaces->len;i++) {
1555 if (camel_interface_is((CamelType)interfaces->pdata[i], itype))
1556 return (CamelType)interfaces->pdata[i];
1560 g_warning("Object %p class %s doesn't contain interface %s\n", vo, obj->klass->name, itype->name);
1565 /* get/set arg methods */
1566 int camel_object_set(void *vo, CamelException *ex, ...)
1569 CamelObject *o = vo;
1570 CamelObjectClass *klass = o->klass;
1573 g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1);
1575 camel_argv_start(&args, ex);
1577 while (camel_argv_build(&args) && ret == 0)
1578 ret = klass->setv(o, ex, &args);
1580 ret = klass->setv(o, ex, &args);
1582 camel_argv_end(&args);
1587 int camel_object_setv(void *vo, CamelException *ex, CamelArgV *args)
1589 g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1);
1591 return ((CamelObject *)vo)->klass->setv(vo, ex, args);
1594 int camel_object_get(void *vo, CamelException *ex, ...)
1596 CamelObject *o = vo;
1598 CamelObjectClass *klass = o->klass;
1601 g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1);
1603 camel_argv_start(&args, ex);
1605 while (camel_arggetv_build(&args) && ret == 0)
1606 ret = klass->getv(o, ex, &args);
1608 ret = klass->getv(o, ex, &args);
1610 camel_argv_end(&args);
1615 void *camel_object_get_ptr(void *vo, CamelException *ex, int tag)
1617 CamelObject *o = vo;
1619 CamelObjectClass *klass = o->klass;
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);
1628 /* woefully inefficient, *shrug */
1630 args.argv[0].tag = tag;
1631 args.argv[0].ca_ptr = &val;
1633 ret = klass->getv(o, ex, &args);
1640 int camel_object_get_int(void *vo, CamelException *ex, int tag)
1642 CamelObject *o = vo;
1644 CamelObjectClass *klass = o->klass;
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);
1652 /* woefully inefficient, *shrug */
1654 args.argv[0].tag = tag;
1655 args.argv[0].ca_int = &val;
1657 ret = klass->getv(o, ex, &args);
1664 int camel_object_getv(void *vo, CamelException *ex, CamelArgGetV *args)
1666 g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1);
1668 return ((CamelObject *)vo)->klass->getv(vo, ex, args);
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)
1675 CamelHookPair *pair;
1676 CamelHookList *hooks;
1678 if (obj->hooks == NULL && !create)
1681 hooks = camel_object_get_hooks(obj);
1684 if (pair->name == meta_name)
1691 pair = pair_alloc();
1692 pair->name = meta_name;
1695 pair->func.filename = NULL;
1696 pair->next = hooks->list;
1698 hooks->list_length++;
1700 camel_object_unget_hooks(obj);
1706 static CamelObjectMeta *
1707 co_metadata_get(CamelObject *obj)
1709 CamelHookPair *pair;
1710 CamelObjectMeta *meta = NULL, *metaout = NULL, *metalast = NULL;
1712 pair = co_metadata_pair(obj, FALSE);
1719 m = g_malloc(sizeof(*m) + strlen(meta->name));
1721 strcpy(m->name, meta->name);
1722 m->value = g_strdup(meta->value);
1723 if (metaout == NULL)
1724 metalast = metaout = m;
1732 camel_object_unget_hooks(obj);
1739 co_metadata_free(CamelObject *obj, CamelObjectMeta *meta)
1742 CamelObjectMeta *metan = meta->next;
1744 g_free(meta->value);
1751 * camel_object_meta_get:
1755 * Get a meta-data on an object.
1757 * Return value: NULL if the meta-data is not set.
1760 camel_object_meta_get(void *vo, const char * name)
1762 CamelObject *obj = vo;
1764 g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0);
1765 g_return_val_if_fail(name != NULL, 0);
1767 return obj->klass->meta_get(obj, name);
1771 * camel_object_meta_set:
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.
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.
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
1783 * Return Value: TRUE if the setting caused a change to the object's
1787 camel_object_meta_set(void *vo, const char * name, const char *value)
1789 CamelObject *obj = vo;
1791 g_return_val_if_fail(CAMEL_IS_OBJECT (obj), FALSE);
1792 g_return_val_if_fail(name != NULL, FALSE);
1794 if (obj->klass->meta_set(obj, name, value)) {
1795 camel_object_trigger_event(obj, "meta_changed", (void *)name);
1803 * camel_object_state_read:
1806 * Read persistent object state from object_set(CAMEL_OBJECT_STATE_FILE).
1808 * Return value: -1 on error.
1810 int camel_object_state_read(void *vo)
1812 CamelObject *obj = vo;
1818 camel_object_get(vo, NULL, CAMEL_OBJECT_STATE_FILE, &file, NULL);
1822 fp = g_fopen(file, "rb");
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);
1832 camel_object_free(vo, CAMEL_OBJECT_STATE_FILE, file);
1838 * camel_object_state_write:
1841 * Write persistent state to the file as set by object_set(CAMEL_OBJECT_STATE_FILE).
1843 * Return value: -1 on error.
1845 int camel_object_state_write(void *vo)
1847 CamelObject *obj = vo;
1849 char *file, *savename, *dirname;
1852 camel_object_get(vo, NULL, CAMEL_OBJECT_STATE_FILE, &file, NULL);
1856 savename = camel_file_util_savename(file);
1857 dirname = g_path_get_dirname(savename);
1858 g_mkdir_with_parents(dirname, 0777);
1860 fp = g_fopen(savename, "wb");
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) {
1866 g_rename(savename, file);
1872 g_warning("Could not save object state file to '%s': %s", savename, g_strerror(errno));
1876 camel_object_free(vo, CAMEL_OBJECT_STATE_FILE, file);
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)
1884 g_return_if_fail(CAMEL_IS_OBJECT(vo));
1886 /* We could also handle freeing of args differently
1888 Add a 'const' bit to the arg type field,
1889 specifying that the object should not be freed.
1891 And, add free handlers for any pointer objects which are
1892 not const. The free handlers would be hookpairs off of the
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.
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 ... */
1902 ((CamelObject *)vo)->klass->free(vo, tag, value);
1906 object_class_dump_tree_rec(CamelType root, int depth)
1909 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
1910 struct _CamelObject *o;
1913 p = alloca(depth*2+1);
1914 memset(p, ' ', depth*2);
1919 printf("%sClass: %s\n", p, root->name);
1920 /*printf("%sVersion: %u.%u\n", p, root->version, root->revision);*/
1922 CamelHookPair *pair = root->hooks;
1925 printf("%s event '%s' prep %p\n", p, pair->name, pair->func.prep);
1929 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
1930 o = root->instances;
1932 printf("%s instance %p [%d]\n", p, o, o->ref_count);
1933 /* todo: should lock hooks while it scans them */
1935 CamelHookPair *pair = o->hooks->list;
1938 printf("%s hook '%s' func %p data %p\n", p, pair->name, pair->func.event, pair->data);
1948 object_class_dump_tree_rec(root->child, depth+1);
1955 camel_object_class_dump_tree(CamelType root)
1957 object_class_dump_tree_rec(root, 0);
1961 * camel_object_bag_new:
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
1974 camel_object_bag_new(GHashFunc hash, GEqualFunc equal, CamelCopyFunc keycopy, GFreeFunc keyfree)
1976 CamelObjectBag *bag;
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;
1990 save_object(void *key, CamelObject *o, GPtrArray *objects)
1992 g_ptr_array_add(objects, o);
1996 camel_object_bag_destroy(CamelObjectBag *bag)
1998 GPtrArray *objects = g_ptr_array_new();
2001 g_assert(bag->reserved == NULL);
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]);
2007 g_ptr_array_free(objects, TRUE);
2008 g_hash_table_destroy(bag->object_table);
2009 g_hash_table_destroy(bag->key_table);
2013 /* must be called with ref_lock held */
2015 co_bag_unreserve(CamelObjectBag *bag, const void *key)
2017 struct _CamelObjectBagKey *res, *resp;
2019 resp = (struct _CamelObjectBagKey *)&bag->reserved;
2022 if (bag->equal_key(res->key, key))
2028 g_assert(res != NULL);
2029 g_assert(res->have_owner && pthread_equal(res->owner, pthread_self()));
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);
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);
2045 * camel_object_bag_add:
2050 * Add an object @vo to the object bag @bag. The @key MUST have
2051 * previously been reserved using camel_object_bag_reserve().
2054 camel_object_bag_add(CamelObjectBag *bag, const void *key, void *vo)
2056 CamelObject *o = vo;
2057 CamelHookList *hooks;
2058 CamelHookPair *pair;
2061 hooks = camel_object_get_hooks(o);
2066 if (pair->name == bag_name && pair->data == bag) {
2068 camel_object_unget_hooks(o);
2074 pair = pair_alloc();
2075 pair->name = bag_name;
2078 pair->func.event = NULL;
2080 pair->next = hooks->list;
2082 hooks->list_length++;
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);
2088 co_bag_unreserve(bag, key);
2091 camel_object_unget_hooks(o);
2095 * camel_object_bag_get:
2099 * Lookup an object by @key. If the key is currently reserved, then
2100 * wait until the key has been committed before continuing.
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
2107 camel_object_bag_get(CamelObjectBag *bag, const void *key)
2113 o = g_hash_table_lookup(bag->object_table, key);
2115 b(printf("object bag get '%s' = %p\n", (char *)key, o));
2117 /* we use the same lock as the refcount */
2120 struct _CamelObjectBagKey *res = bag->reserved;
2122 /* check if this name is reserved currently, if so wait till its finished */
2124 if (bag->equal_key(res->key, key))
2130 b(printf("object bag get '%s', reserved, waiting\n", (char *)key));
2133 g_assert(!res->have_owner || !pthread_equal(res->owner, pthread_self()));
2134 g_cond_wait(res->cond, ref_lock);
2137 /* re-check if it slipped in */
2138 o = g_hash_table_lookup(bag->object_table, key);
2142 b(printf("object bag get '%s', finished waiting, got %p\n", (char *)key, o));
2144 /* we don't actually reserve it */
2145 res->owner = pthread_self();
2146 res->have_owner = TRUE;
2147 co_bag_unreserve(bag, key);
2157 * camel_object_bag_peek:
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.
2165 * Unlike other 'peek' operations, the object is still reffed if
2168 * Return value: A referenced object, or NULL if @key is not
2169 * present in the bag.
2172 camel_object_bag_peek(CamelObjectBag *bag, const void *key)
2178 o = g_hash_table_lookup(bag->object_table, key);
2180 /* we use the same lock as the refcount */
2190 * camel_object_bag_reserve:
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.
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.
2202 * You may reserve multiple keys from the same thread, but they should
2203 * always be reserved in the same order, to avoid deadlocks.
2208 camel_object_bag_reserve(CamelObjectBag *bag, const void *key)
2214 o = g_hash_table_lookup(bag->object_table, key);
2218 struct _CamelObjectBagKey *res = bag->reserved;
2221 if (bag->equal_key(res->key, key))
2227 b(printf("bag reserve %s, already reserved, waiting\n", (char *)key));
2228 g_assert(!res->have_owner || !pthread_equal(res->owner, pthread_self()));
2230 g_cond_wait(res->cond, ref_lock);
2232 /* incase its slipped in while we were waiting */
2233 o = g_hash_table_lookup(bag->object_table, key);
2235 b(printf("finished wait, someone else created '%s' = %p\n", (char *)key, o));
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);
2242 b(printf("finished wait, now owner of '%s'\n", (char *)key));
2243 res->owner = pthread_self();
2244 res->have_owner = TRUE;
2247 b(printf("bag reserve %s, no key, reserving\n", (char *)key));
2248 res = g_malloc(sizeof(*res));
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;
2265 * camel_object_bag_abort:
2269 * Abort a key reservation.
2272 camel_object_bag_abort(CamelObjectBag *bag, const void *key)
2276 co_bag_unreserve(bag, key);
2282 * camel_object_bag_rekey:
2287 * Re-key an object, atomically. The key for object @o is set to
2288 * @newkey, in an atomic manner.
2290 * It is an api (fatal) error if @o is not currently in the bag.
2293 camel_object_bag_rekey(CamelObjectBag *bag, void *o, const void *newkey)
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);
2314 save_bag(void *key, CamelObject *o, GPtrArray *list)
2316 /* we have the refcount lock already */
2318 g_ptr_array_add(list, o);
2321 /* get a list of all objects in the bag, ref'd
2322 ignores any reserved keys */
2324 camel_object_bag_list(CamelObjectBag *bag)
2328 list = g_ptr_array_new();
2331 g_hash_table_foreach(bag->object_table, (GHFunc)save_bag, list);
2337 /* if bag is NULL, remove all bags from object */
2339 camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks)
2341 CamelHookPair *pair, *parent;
2343 CamelObjectBag *bag;
2345 parent = (CamelHookPair *)&hooks->list;
2346 pair = parent->next;
2348 if (pair->name == bag_name
2349 && (inbag == NULL || inbag == pair->data)) {
2351 /* lookup object in table? */
2352 oldkey = g_hash_table_lookup(bag->key_table, o);
2354 g_hash_table_remove(bag->key_table, o);
2355 g_hash_table_remove(bag->object_table, oldkey);
2356 bag->free_key(oldkey);
2358 parent->next = pair->next;
2360 hooks->list_length--;
2364 pair = parent->next;
2369 camel_object_bag_remove(CamelObjectBag *inbag, void *vo)
2371 CamelObject *o = vo;
2372 CamelHookList *hooks;
2374 if (o->hooks == NULL)
2377 hooks = camel_object_get_hooks(o);
2380 camel_object_bag_remove_unlocked(inbag, o, hooks);
2383 camel_object_unget_hooks(o);
2386 /* ********************************************************************** */
2388 void *camel_iterator_new(CamelIteratorVTable *klass, size_t size)
2392 g_assert(size >= sizeof(CamelIterator));
2394 it = g_malloc0(size);
2400 const void *camel_iterator_next(void *it, CamelException *ex)
2403 return ((CamelIterator *)it)->klass->next(it, ex);
2406 void camel_iterator_reset(void *it)
2409 ((CamelIterator *)it)->klass->reset(it);
2412 int camel_iterator_length(void *it)
2415 g_return_val_if_fail(((CamelIterator *)it)->klass->length != NULL, 0);
2417 return ((CamelIterator *)it)->klass->length(it);
2420 void camel_iterator_free(void *it)
2423 if (((CamelIterator *)it)->klass->free)
2424 ((CamelIterator *)it)->klass->free(it);