1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
4 * Michael Zucchi <notzed@ximian.com>
6 * Copyright 2000-2002 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 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 General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #include "camel-object.h"
31 #include <e-util/e-memory.h>
35 #include <semaphore.h>
36 #include <e-util/e-msgport.h>
41 /* I just mashed the keyboard for these... */
42 #define CAMEL_OBJECT_MAGIC 0x77A344ED
43 #define CAMEL_OBJECT_CLASS_MAGIC 0xEE26A997
44 #define CAMEL_OBJECT_FINALISED_MAGIC 0x84AC365F
45 #define CAMEL_OBJECT_CLASS_FINALISED_MAGIC 0x7621ABCD
47 /* ** Quickie type system ************************************************* */
49 /* A 'locked' hooklist, that is only allocated on demand */
50 typedef struct _CamelHookList {
53 unsigned int depth:30; /* recursive event depth */
54 unsigned int flags:2; /* flags, see below */
56 unsigned int list_length;
57 struct _CamelHookPair *list;
60 #define CAMEL_HOOK_PAIR_REMOVED (1<<0)
62 /* a 'hook pair', actually a hook tuple, we just store all hooked events in the same list,
63 and just comapre as we go, rather than storing separate lists for each hook type
65 the name field just points directly to the key field in the class's preplist hashtable.
66 This way we can just use a direct pointer compare when scanning it, and also saves
68 typedef struct _CamelHookPair
70 struct _CamelHookPair *next; /* next MUST be the first member */
73 unsigned int flags:2; /* removed, etc */
75 const char *name; /* points to the key field in the classes preplist, static memory */
77 CamelObjectEventHookFunc event;
78 CamelObjectEventPrepFunc prep;
83 struct _CamelObjectBag {
84 GHashTable *object_table; /* object by key */
85 GHashTable *key_table; /* key by object */
86 CamelCopyFunc copy_key;
89 pthread_t owner; /* the thread that has reserved the bag for a new entry */
90 sem_t reserve_sem; /* used to track ownership */
94 /* used to tag a bag hookpair */
95 static const char *bag_name = "object:bag";
97 /* ********************************************************************** */
99 static CamelHookList *camel_object_get_hooks(CamelObject *o);
100 static void camel_object_free_hooks(CamelObject *o);
101 static void camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks);
103 #ifdef ENABLE_THREADS
104 #define camel_object_unget_hooks(o) (e_mutex_unlock((CAMEL_OBJECT(o)->hooks->lock)))
106 #define camel_object_unget_hooks(o)
109 /* ********************************************************************** */
111 static pthread_mutex_t chunks_lock = PTHREAD_MUTEX_INITIALIZER;
113 static EMemChunk *pair_chunks;
114 static EMemChunk *hook_chunks;
115 static unsigned int pair_id = 1;
117 static EMutex *type_lock;
119 static GHashTable *type_table;
120 static EMemChunk *type_chunks;
122 CamelType camel_object_type;
124 #ifdef ENABLE_THREADS
125 #define P_LOCK(l) (pthread_mutex_lock(&l))
126 #define P_UNLOCK(l) (pthread_mutex_unlock(&l))
127 #define E_LOCK(l) (e_mutex_lock(l))
128 #define E_UNLOCK(l) (e_mutex_unlock(l))
129 #define CLASS_LOCK(k) (g_mutex_lock((((CamelObjectClass *)k)->lock)))
130 #define CLASS_UNLOCK(k) (g_mutex_unlock((((CamelObjectClass *)k)->lock)))
136 #define CLASS_LOCK(k)
137 #define CLASS_UNLOCK(k)
140 static struct _CamelHookPair *
146 pair = e_memchunk_alloc(pair_chunks);
147 pair->id = pair_id++;
150 P_UNLOCK(chunks_lock);
156 pair_free(CamelHookPair *pair)
158 g_assert(pair_chunks != NULL);
161 e_memchunk_free(pair_chunks, pair);
162 P_UNLOCK(chunks_lock);
165 static struct _CamelHookList *
168 CamelHookList *hooks;
171 hooks = e_memchunk_alloc(hook_chunks);
172 P_UNLOCK(chunks_lock);
178 hooks_free(CamelHookList *hooks)
180 g_assert(hook_chunks != NULL);
183 e_memchunk_free(hook_chunks, hooks);
184 P_UNLOCK(chunks_lock);
187 /* not checked locked, who cares, only required for people that want to redefine root objects */
189 camel_type_init(void)
191 static int init = FALSE;
197 pair_chunks = e_memchunk_new(16, sizeof(CamelHookPair));
198 hook_chunks = e_memchunk_new(16, sizeof(CamelHookList));
199 type_lock = e_mutex_new(E_MUTEX_REC);
200 type_chunks = e_memchunk_new(32, sizeof(CamelType));
201 type_table = g_hash_table_new(NULL, NULL);
204 /* ************************************************************************ */
206 /* Should this return the object to the caller? */
208 cobject_init (CamelObject *o, CamelObjectClass *klass)
211 o->magic = CAMEL_OBJECT_MAGIC;
217 cobject_finalise(CamelObject *o)
219 /*printf("%p: finalise %s\n", o, o->klass->name);*/
221 g_assert(o->ref_count == 0);
223 camel_object_free_hooks(o);
225 o->magic = CAMEL_OBJECT_FINALISED_MAGIC;
230 cobject_getv(CamelObject *o, CamelException *ex, CamelArgGetV *args)
235 for (i=0;i<args->argc;i++) {
236 CamelArgGet *arg = &args->argv[i];
240 switch (tag & CAMEL_ARG_TAG) {
241 case CAMEL_OBJECT_ARG_DESCRIPTION:
242 *arg->ca_str = (char *)o->klass->name;
247 /* could have flags or stuff here? */
252 cobject_setv(CamelObject *o, CamelException *ex, CamelArgV *args)
254 /* could have flags or stuff here? */
259 cobject_free(CamelObject *o, guint32 tag, void *value)
265 cobject_class_init(CamelObjectClass *klass)
267 klass->magic = CAMEL_OBJECT_CLASS_MAGIC;
269 klass->getv = cobject_getv;
270 klass->setv = cobject_setv;
271 klass->free = cobject_free;
273 camel_object_class_add_event(klass, "finalize", NULL);
277 cobject_class_finalise(CamelObjectClass * klass)
279 klass->magic = CAMEL_OBJECT_CLASS_FINALISED_MAGIC;
285 camel_object_get_type (void)
287 if (camel_object_type == CAMEL_INVALID_TYPE) {
290 camel_object_type = camel_type_register(NULL, "CamelObject", /*, 0, 0*/
291 sizeof(CamelObject), sizeof(CamelObjectClass),
292 cobject_class_init, cobject_class_finalise,
293 cobject_init, cobject_finalise);
296 return camel_object_type;
300 camel_type_class_init(CamelObjectClass *klass, CamelObjectClass *type)
303 camel_type_class_init(klass, type->parent);
305 if (type->klass_init)
306 type->klass_init(klass);
310 camel_type_register (CamelType parent, const char * name,
311 /*unsigned int ver, unsigned int rev,*/
312 size_t object_size, size_t klass_size,
313 CamelObjectClassInitFunc class_init,
314 CamelObjectClassFinalizeFunc class_finalise,
315 CamelObjectInitFunc object_init,
316 CamelObjectFinalizeFunc object_finalise)
318 CamelObjectClass *klass;
322 if (parent != NULL && parent->magic != CAMEL_OBJECT_CLASS_MAGIC) {
323 g_warning("camel_type_register: invalid junk parent class for '%s'", name);
329 /* Have to check creation, it might've happened in another thread before we got here */
330 klass = g_hash_table_lookup(type_table, name);
332 if (klass->klass_size != klass_size || klass->object_size != object_size
333 || klass->klass_init != class_init || klass->klass_finalise != class_finalise
334 || klass->init != object_init || klass->finalise != object_finalise) {
335 g_warning("camel_type_register: Trying to re-register class '%s'", name);
342 /* this is for objects with no parent as part of their struct ('interfaces'?) */
343 /*offset = parent?parent->klass_size:0;
344 offset = (offset + 3) & (~3);
346 size = offset + klass_size;
348 klass = g_malloc0(size);
350 klass->klass_size = size;
351 klass->klass_data = offset;
353 offset = parent?parent->object_size:0;
354 offset = (offset + 3) & (~3);
356 klass->object_size = offset + object_size;
357 klass->object_data = offset;*/
360 && klass_size < parent->klass_size) {
361 g_warning("camel_type_register: '%s' has smaller class size than parent '%s'", name, parent->name);
366 klass = g_malloc0(klass_size);
367 klass->klass_size = klass_size;
368 klass->object_size = object_size;
369 #ifdef ENABLE_THREADS
370 klass->lock = g_mutex_new();
372 klass->instance_chunks = e_memchunk_new(8, object_size);
374 klass->parent = parent;
376 klass->next = parent->child;
377 parent->child = klass;
381 /*klass->version = ver;
382 klass->revision = rev;*/
384 klass->klass_init = class_init;
385 klass->klass_finalise = class_finalise;
387 klass->init = object_init;
388 klass->finalise = object_finalise;
390 /* setup before class init, incase class init func uses the type or looks it up ? */
391 g_hash_table_insert(type_table, (void *)name, klass);
393 camel_type_class_init(klass, klass);
401 camel_object_init(CamelObject *o, CamelObjectClass *klass, CamelType type)
404 camel_object_init(o, klass, type->parent);
407 type->init(o, klass);
411 camel_object_new(CamelType type)
418 if (type->magic != CAMEL_OBJECT_CLASS_MAGIC)
423 o = e_memchunk_alloc0(type->instance_chunks);
425 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
427 type->instances->prev = o;
428 o->next = type->instances;
435 camel_object_init(o, type, type);
437 d(printf("%p: new %s()\n", o, o->klass->name));
443 camel_object_ref(void *vo)
445 register CamelObject *o = vo;
447 g_return_if_fail(CAMEL_IS_OBJECT(o));
452 d(printf("%p: ref %s(%d)\n", o, o->klass->name, o->ref_count));
458 camel_object_unref(void *vo)
460 register CamelObject *o = vo;
461 register CamelObjectClass *klass, *k;
462 CamelHookList *hooks = NULL;
464 g_return_if_fail(CAMEL_IS_OBJECT(o));
469 hooks = camel_object_get_hooks(o);
475 d(printf("%p: unref %s(%d)\n", o, o->klass->name, o->ref_count));
478 || (o->flags & CAMEL_OBJECT_DESTROY)) {
481 camel_object_unget_hooks(o);
485 o->flags |= CAMEL_OBJECT_DESTROY;
488 camel_object_bag_remove_unlocked(NULL, o, hooks);
493 camel_object_unget_hooks(o);
495 camel_object_trigger_event(o, "finalize", NULL);
504 o->magic = CAMEL_OBJECT_FINALISED_MAGIC;
507 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
509 o->prev->next = o->next;
511 klass->instances = o->next;
513 o->next->prev = o->prev;
515 e_memchunk_free(klass->instance_chunks, o);
520 camel_type_to_name (CamelType type)
523 return "(NULL class)";
525 if (type->magic == CAMEL_OBJECT_CLASS_MAGIC)
528 return "(Junk class)";
531 CamelType camel_name_to_type(const char *name)
533 /* TODO: Load a class off disk (!) */
535 return g_hash_table_lookup(type_table, name);
539 desc_data(CamelObject *o, int ok)
544 what = g_strdup("NULL OBJECT");
545 else if (o->magic == ok)
547 else if (o->magic == CAMEL_OBJECT_MAGIC)
548 what = g_strdup_printf("CLASS '%s'", ((CamelObjectClass *)o)->name);
549 else if (o->magic == CAMEL_OBJECT_CLASS_MAGIC)
550 what = g_strdup_printf("CLASS '%s'", ((CamelObjectClass *)o)->name);
551 else if (o->magic == CAMEL_OBJECT_FINALISED_MAGIC)
552 what = g_strdup_printf("finalised OBJECT");
553 else if (o->magic == CAMEL_OBJECT_CLASS_FINALISED_MAGIC)
554 what = g_strdup_printf("finalised CLASS");
556 what = g_strdup_printf("junk data");
562 check_magic(void *o, CamelType ctype, int isob)
566 what = desc_data(o, isob?CAMEL_OBJECT_MAGIC:CAMEL_OBJECT_CLASS_MAGIC);
567 to = desc_data((CamelObject *)ctype, CAMEL_OBJECT_CLASS_MAGIC);
572 what = g_strdup_printf("OBJECT '%s'", ((CamelObject *)o)->klass->name);
574 what = g_strdup_printf("OBJECT '%s'", ((CamelObjectClass *)o)->name);
577 to = g_strdup_printf("OBJECT '%s'", ctype->name);
578 g_warning("Trying to check %s is %s", what, to);
589 camel_object_is (CamelObject *o, CamelType ctype)
593 g_return_val_if_fail(check_magic(o, ctype, TRUE), FALSE);
606 camel_object_class_is (CamelObjectClass *k, CamelType ctype)
608 g_return_val_if_fail(check_magic(k, ctype, FALSE), FALSE);
620 camel_object_cast(CamelObject *o, CamelType ctype)
624 g_return_val_if_fail(check_magic(o, ctype, TRUE), NULL);
633 g_warning("Object %p (class '%s') doesn't have '%s' in its hierarchy", o, o->klass->name, ctype->name);
639 camel_object_class_cast(CamelObjectClass *k, CamelType ctype)
641 CamelObjectClass *r = k;
643 g_return_val_if_fail(check_magic(k, ctype, FALSE), NULL);
651 g_warning("Class '%s' doesn't have '%s' in its hierarchy", r->name, ctype->name);
657 camel_object_class_add_event(CamelObjectClass *klass, const char *name, CamelObjectEventPrepFunc prep)
661 g_return_if_fail (name);
665 if (strcmp(pair->name, name) == 0) {
666 g_warning("camel_object_class_add_event: `%s' is already declared for '%s'\n",
675 pair->func.prep = prep;
678 pair->next = klass->hooks;
683 static void camel_object_free_hooks(CamelObject *o)
685 CamelHookPair *pair, *next;
688 g_assert(o->hooks->depth == 0);
689 g_assert((o->hooks->flags & CAMEL_HOOK_PAIR_REMOVED) == 0);
691 pair = o->hooks->list;
697 e_mutex_destroy(o->hooks->lock);
698 hooks_free(o->hooks);
703 /* return (allocate if required) the object's hook list, locking at the same time */
704 static CamelHookList *camel_object_get_hooks(CamelObject *o)
706 #ifdef ENABLE_THREADS
707 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
709 CamelHookList *hooks;
711 /* if we have it, we dont have to do any other locking,
712 otherwise use a global lock to setup the object's hook data */
713 #ifdef ENABLE_THREADS
714 if (o->hooks == NULL) {
715 pthread_mutex_lock(&lock);
717 if (o->hooks == NULL) {
718 hooks = hooks_alloc();
719 #ifdef ENABLE_THREADS
720 hooks->lock = e_mutex_new(E_MUTEX_REC);
724 hooks->list_length = 0;
728 #ifdef ENABLE_THREADS
729 pthread_mutex_unlock(&lock);
733 #ifdef ENABLE_THREADS
734 e_mutex_lock(o->hooks->lock);
740 camel_object_hook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data)
742 CamelObject *obj = vo;
743 CamelHookPair *pair, *hook;
744 CamelHookList *hooks;
747 g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0);
748 g_return_val_if_fail(name != NULL, 0);
749 g_return_val_if_fail(func != NULL, 0);
751 hook = obj->klass->hooks;
753 if (strcmp(hook->name, name) == 0)
758 g_warning("camel_object_hook_event: trying to hook event `%s' in class `%s' with no defined events.",
759 name, obj->klass->name);
764 /* setup hook pair */
766 pair->name = hook->name; /* effectively static! */
767 pair->func.event = func;
772 /* get the hook list object, locked, link in new event hook, unlock */
773 hooks = camel_object_get_hooks(obj);
774 pair->next = hooks->list;
776 hooks->list_length++;
777 camel_object_unget_hooks(obj);
783 camel_object_remove_event(void *vo, unsigned int id)
785 CamelObject *obj = vo;
786 CamelHookList *hooks;
787 CamelHookPair *pair, *parent;
789 g_return_if_fail (CAMEL_IS_OBJECT (obj));
790 g_return_if_fail (id != 0);
792 if (obj->hooks == NULL) {
793 g_warning("camel_object_unhook_event: trying to unhook `%d` from an instance of `%s' with no hooks",
794 id, obj->klass->name);
798 /* scan hooks for this event, remove it, or flag it if we're busy */
799 hooks = camel_object_get_hooks(obj);
800 parent = (CamelHookPair *)&hooks->list;
804 && (pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) {
805 if (hooks->depth > 0) {
806 pair->flags |= CAMEL_HOOK_PAIR_REMOVED;
807 hooks->flags |= CAMEL_HOOK_PAIR_REMOVED;
809 parent->next = pair->next;
811 hooks->list_length--;
813 camel_object_unget_hooks(obj);
819 camel_object_unget_hooks(obj);
821 g_warning("camel_object_unhook_event: cannot find hook id %d in instance of `%s'",
822 id, obj->klass->name);
826 camel_object_unhook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data)
828 CamelObject *obj = vo;
829 CamelHookList *hooks;
830 CamelHookPair *pair, *parent;
832 g_return_if_fail (CAMEL_IS_OBJECT (obj));
833 g_return_if_fail (name != NULL);
834 g_return_if_fail (func != NULL);
836 if (obj->hooks == NULL) {
837 g_warning("camel_object_unhook_event: trying to unhook `%s` from an instance of `%s' with no hooks",
838 name, obj->klass->name);
842 /* scan hooks for this event, remove it, or flag it if we're busy */
843 hooks = camel_object_get_hooks(obj);
844 parent = (CamelHookPair *)&hooks->list;
847 if (pair->func.event == func
848 && pair->data == data
849 && strcmp(pair->name, name) == 0
850 && (pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) {
851 if (hooks->depth > 0) {
852 pair->flags |= CAMEL_HOOK_PAIR_REMOVED;
853 hooks->flags |= CAMEL_HOOK_PAIR_REMOVED;
855 parent->next = pair->next;
857 hooks->list_length--;
859 camel_object_unget_hooks(obj);
865 camel_object_unget_hooks(obj);
867 g_warning("camel_object_unhook_event: cannot find hook/data pair %p/%p in an instance of `%s' attached to `%s'",
868 func, data, obj->klass->name, name);
872 camel_object_trigger_event(void *vo, const char * name, void *event_data)
874 CamelObject *obj = vo;
875 CamelHookList *hooks;
876 CamelHookPair *pair, **pairs, *parent, *hook;
878 const char *prepname;
880 g_return_if_fail (CAMEL_IS_OBJECT (obj));
881 g_return_if_fail (name);
883 hook = obj->klass->hooks;
885 if (strcmp(hook->name, name) == 0)
890 g_warning("camel_object_trigger_event: trying to trigger unknown event `%s' in class `%s'",
891 name, obj->klass->name);
896 /* try prep function, if false, then quit */
897 if (hook->func.prep != NULL && !hook->func.prep(obj, event_data))
900 /* also, no hooks, dont bother going further */
901 if (obj->hooks == NULL)
904 /* lock the object for hook emission */
905 camel_object_ref(obj);
906 hooks = camel_object_get_hooks(obj);
909 /* first, copy the items in the list, and say we're in an event */
913 pairs = alloca(sizeof(pairs[0]) * hooks->list_length);
914 prepname = hook->name;
916 if (pair->name == prepname)
917 pairs[size++] = pair;
921 /* now execute the events we have, if they haven't been removed during our calls */
922 for (i=size-1;i>=0;i--) {
924 if ((pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0)
925 (pair->func.event) (obj, event_data, pair->data);
929 /* and if we're out of any events, then clean up any pending removes */
930 if (hooks->depth == 0 && (hooks->flags & CAMEL_HOOK_PAIR_REMOVED)) {
931 parent = (CamelHookPair *)&hooks->list;
934 if (pair->flags & CAMEL_HOOK_PAIR_REMOVED) {
935 parent->next = pair->next;
937 hooks->list_length--;
943 hooks->flags &= ~CAMEL_HOOK_PAIR_REMOVED;
947 camel_object_unget_hooks(obj);
948 camel_object_unref(obj);
951 /* get/set arg methods */
952 int camel_object_set(void *vo, CamelException *ex, ...)
956 CamelObjectClass *klass = o->klass;
959 g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1);
961 camel_argv_start(&args, ex);
963 while (camel_argv_build(&args) && ret == 0)
964 ret = klass->setv(o, ex, &args);
966 ret = klass->setv(o, ex, &args);
968 camel_argv_end(&args);
973 int camel_object_setv(void *vo, CamelException *ex, CamelArgV *args)
975 g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1);
977 return ((CamelObject *)vo)->klass->setv(vo, ex, args);
980 int camel_object_get(void *vo, CamelException *ex, ...)
984 CamelObjectClass *klass = o->klass;
987 g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1);
989 camel_argv_start(&args, ex);
991 while (camel_arggetv_build(&args) && ret == 0)
992 ret = klass->getv(o, ex, &args);
994 ret = klass->getv(o, ex, &args);
996 camel_argv_end(&args);
1001 int camel_object_getv(void *vo, CamelException *ex, CamelArgGetV *args)
1003 g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1);
1005 return ((CamelObject *)vo)->klass->getv(vo, ex, args);
1008 /* free an arg object, you can only free objects 1 at a time */
1009 void camel_object_free(void *vo, guint32 tag, void *value)
1011 g_return_if_fail(CAMEL_IS_OBJECT(vo));
1013 /* We could also handle freeing of args differently
1015 Add a 'const' bit to the arg type field,
1016 specifying that the object should not be freed.
1018 And, add free handlers for any pointer objects which are
1019 not const. The free handlers would be hookpairs off of the
1022 Then we handle the basic types OBJ,STR here, and pass PTR
1023 types to their appropriate handler, without having to pass
1024 through the invocation heirarchy of the free method.
1026 This would also let us copy and do other things with args
1027 we can't do, but i can't see a use for that yet ... */
1029 ((CamelObject *)vo)->klass->free(vo, tag, value);
1033 object_class_dump_tree_rec(CamelType root, int depth)
1036 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
1037 struct _CamelObject *o;
1040 p = alloca(depth*2+1);
1041 memset(p, ' ', depth*2);
1046 printf("%sClass: %s\n", p, root->name);
1047 /*printf("%sVersion: %u.%u\n", p, root->version, root->revision);*/
1049 CamelHookPair *pair = root->hooks;
1052 printf("%s event '%s' prep %p\n", p, pair->name, pair->func.prep);
1056 #ifdef CAMEL_OBJECT_TRACK_INSTANCES
1057 o = root->instances;
1059 printf("%s instance %p [%d]\n", p, o, o->ref_count);
1060 /* todo: should lock hooks while it scans them */
1062 CamelHookPair *pair = o->hooks->list;
1065 printf("%s hook '%s' func %p data %p\n", p, pair->name, pair->func.event, pair->data);
1075 object_class_dump_tree_rec(root->child, depth+1);
1082 camel_object_class_dump_tree(CamelType root)
1084 object_class_dump_tree_rec(root, 0);
1087 CamelObjectBag *camel_object_bag_new(GHashFunc hash, GEqualFunc equal, CamelCopyFunc keycopy, GFreeFunc keyfree)
1089 CamelObjectBag *bag;
1091 bag = g_malloc(sizeof(*bag));
1092 bag->object_table = g_hash_table_new(hash, equal);
1093 bag->copy_key = keycopy;
1094 bag->free_key = keyfree;
1095 bag->key_table = g_hash_table_new(NULL, NULL);
1097 #ifdef ENABLE_THREADS
1098 /* init the semaphore to 1 owner, this is who has reserved the bag for adding */
1099 sem_init(&bag->reserve_sem, 0, 1);
1105 save_object(void *key, CamelObject *o, GPtrArray *objects)
1107 g_ptr_array_add(objects, o);
1111 save_key(void *key, CamelObject *o, GPtrArray *keys)
1113 g_ptr_array_add (keys, key);
1116 void camel_object_bag_destroy(CamelObjectBag *bag)
1118 GPtrArray *objects = g_ptr_array_new();
1119 GPtrArray *keys = g_ptr_array_new ();
1122 sem_getvalue(&bag->reserve_sem, &i);
1125 g_hash_table_foreach(bag->object_table, (GHFunc)save_object, objects);
1126 g_hash_table_foreach(bag->object_table, (GHFunc)save_key, keys);
1127 for (i=0;i<objects->len;i++) {
1128 camel_object_bag_remove(bag, objects->pdata[i]);
1129 bag->free_key (keys->pdata[i]);
1132 g_ptr_array_free (keys, TRUE);
1133 g_ptr_array_free(objects, TRUE);
1134 g_hash_table_destroy(bag->object_table);
1135 g_hash_table_destroy(bag->key_table);
1136 #ifdef ENABLE_THREADS
1137 sem_destroy(&bag->reserve_sem);
1142 void camel_object_bag_add(CamelObjectBag *bag, const void *key, void *vo)
1144 CamelObject *o = vo;
1145 CamelHookList *hooks;
1146 CamelHookPair *pair;
1149 hooks = camel_object_get_hooks(o);
1154 if (pair->name == bag_name && pair->data == bag) {
1155 E_UNLOCK(type_lock);
1156 camel_object_unget_hooks(o);
1162 pair = pair_alloc();
1163 pair->name = bag_name;
1167 pair->next = hooks->list;
1169 hooks->list_length++;
1171 k = bag->copy_key(key);
1172 g_hash_table_insert(bag->object_table, k, vo);
1173 g_hash_table_insert(bag->key_table, vo, k);
1175 #ifdef ENABLE_THREADS
1176 if (bag->owner == pthread_self()) {
1178 sem_post(&bag->reserve_sem);
1182 E_UNLOCK(type_lock);
1183 camel_object_unget_hooks(o);
1186 void *camel_object_bag_get(CamelObjectBag *bag, const void *key)
1192 o = g_hash_table_lookup(bag->object_table, key);
1194 /* we use the same lock as the refcount */
1197 #ifdef ENABLE_THREADS
1198 else if (bag->owner != pthread_self()) {
1199 E_UNLOCK(type_lock);
1200 sem_wait(&bag->reserve_sem);
1202 /* re-check if it slipped in */
1203 o = g_hash_table_lookup(bag->object_table, key);
1206 /* we dont want to reserve the bag */
1207 sem_post(&bag->reserve_sem);
1211 E_UNLOCK(type_lock);
1216 /* like get, but also reserve a spot for key if it isn't there */
1217 /* After calling reserve, you MUST call bag_abort or bag_add */
1218 /* Also note that currently you can only reserve a single key
1219 at any one time in a given thread */
1220 void *camel_object_bag_reserve(CamelObjectBag *bag, const void *key)
1226 o = g_hash_table_lookup(bag->object_table, key);
1230 #ifdef ENABLE_THREADS
1232 g_assert(bag->owner != pthread_self());
1233 E_UNLOCK(type_lock);
1234 sem_wait(&bag->reserve_sem);
1236 /* incase its slipped in while we were waiting */
1237 o = g_hash_table_lookup(bag->object_table, key);
1240 /* in which case we dont need to reserve the bag either */
1241 sem_post(&bag->reserve_sem);
1243 bag->owner = pthread_self();
1248 E_UNLOCK(type_lock);
1253 /* abort a reserved key */
1254 void camel_object_bag_abort(CamelObjectBag *bag, const void *key)
1256 #ifdef ENABLE_THREADS
1257 g_assert(bag->owner == pthread_self());
1260 sem_post(&bag->reserve_sem);
1265 save_bag(void *key, CamelObject *o, GPtrArray *list)
1267 /* we have the refcount lock already */
1269 g_ptr_array_add(list, o);
1272 /* get a list of all objects in the bag, ref'd
1273 ignores any reserved keys */
1274 GPtrArray *camel_object_bag_list(CamelObjectBag *bag)
1278 list = g_ptr_array_new();
1281 g_hash_table_foreach(bag->object_table, (GHFunc)save_bag, list);
1282 E_UNLOCK(type_lock);
1287 /* if bag is NULL, remove all bags from object */
1288 static void camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks)
1290 CamelHookPair *pair, *parent;
1292 CamelObjectBag *bag;
1294 parent = (CamelHookPair *)&hooks->list;
1295 pair = parent->next;
1297 if (pair->name == bag_name
1298 && (inbag == NULL || inbag == pair->data)) {
1300 /* lookup object in table? */
1301 oldkey = g_hash_table_lookup(bag->key_table, o);
1303 g_hash_table_remove(bag->key_table, o);
1304 g_hash_table_remove(bag->object_table, oldkey);
1305 bag->free_key(oldkey);
1307 parent->next = pair->next;
1309 hooks->list_length--;
1313 pair = parent->next;
1317 void camel_object_bag_remove(CamelObjectBag *inbag, void *vo)
1319 CamelObject *o = vo;
1320 CamelHookList *hooks;
1322 if (o->hooks == NULL)
1325 hooks = camel_object_get_hooks(o);
1328 camel_object_bag_remove_unlocked(inbag, o, hooks);
1330 E_UNLOCK(type_lock);
1331 camel_object_unget_hooks(o);