aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-object.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/camel-object.c')
-rw-r--r--camel/camel-object.c307
1 files changed, 295 insertions, 12 deletions
diff --git a/camel/camel-object.c b/camel/camel-object.c
index 6e3b5ef54b..7214518839 100644
--- a/camel/camel-object.c
+++ b/camel/camel-object.c
@@ -35,6 +35,8 @@
#include <e-util/e-msgport.h>
#endif
+#define d(x)
+
/* I just mashed the keyboard for these... */
#define CAMEL_OBJECT_MAGIC 0x77A344ED
#define CAMEL_OBJECT_CLASS_MAGIC 0xEE26A997
@@ -77,9 +79,32 @@ typedef struct _CamelHookPair
void *data;
} CamelHookPair;
+struct _CamelObjectBag {
+ GHashTable *object_table; /* object by key */
+ GHashTable *key_table; /* key by object */
+ guint32 flags;
+#ifdef ENABLE_THREADS
+ pthread_t owner;
+ pthread_cond_t cond;
+#endif
+};
+
+#define CAMEL_OBJECT_BAG_RESERVED (1)
+
+/* used to tag a bag hookpair */
+static const char *bag_name = "object:bag";
+
/* ********************************************************************** */
+static CamelHookList *camel_object_get_hooks(CamelObject *o);
static void camel_object_free_hooks(CamelObject *o);
+static void camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks);
+
+#ifdef ENABLE_THREADS
+#define camel_object_unget_hooks(o) (e_mutex_unlock((CAMEL_OBJECT(o)->hooks->lock)))
+#else
+#define camel_object_unget_hooks(o)
+#endif
/* ********************************************************************** */
@@ -191,6 +216,8 @@ cobject_init (CamelObject *o, CamelObjectClass *klass)
static void
cobject_finalise(CamelObject *o)
{
+ /*printf("%p: finalise %s\n", o, o->klass->name);*/
+
g_assert(o->ref_count == 0);
camel_object_free_hooks(o);
@@ -392,6 +419,7 @@ camel_object_new(CamelType type)
return NULL;
CLASS_LOCK(type);
+
o = e_memchunk_alloc0(type->instance_chunks);
#ifdef CAMEL_OBJECT_TRACK_INSTANCES
@@ -403,8 +431,11 @@ camel_object_new(CamelType type)
#endif
CLASS_UNLOCK(type);
+
camel_object_init(o, type, type);
+ d(printf("%p: new %s()\n", o, o->klass->name));
+
return o;
}
@@ -415,9 +446,12 @@ camel_object_ref(void *vo)
g_return_if_fail(CAMEL_IS_OBJECT(o));
- CLASS_LOCK(o->klass);
+ E_LOCK(type_lock);
+
o->ref_count++;
- CLASS_UNLOCK(o->klass);
+ d(printf("%p: ref %s(%d)\n", o, o->klass->name, o->ref_count));
+
+ E_UNLOCK(type_lock);
}
void
@@ -425,22 +459,38 @@ camel_object_unref(void *vo)
{
register CamelObject *o = vo;
register CamelObjectClass *klass, *k;
+ CamelHookList *hooks = NULL;
g_return_if_fail(CAMEL_IS_OBJECT(o));
klass = o->klass;
- CLASS_LOCK(klass);
+ if (o->hooks)
+ hooks = camel_object_get_hooks(o);
+
+ E_LOCK(type_lock);
+
o->ref_count--;
+
+ d(printf("%p: unref %s(%d)\n", o, o->klass->name, o->ref_count));
+
if (o->ref_count > 0
|| (o->flags & CAMEL_OBJECT_DESTROY)) {
- CLASS_UNLOCK(klass);
+ E_UNLOCK(type_lock);
+ if (hooks)
+ camel_object_unget_hooks(o);
return;
}
o->flags |= CAMEL_OBJECT_DESTROY;
- CLASS_UNLOCK(klass);
+ if (hooks)
+ camel_object_bag_remove_unlocked(NULL, o, hooks);
+
+ E_UNLOCK(type_lock);
+
+ if (hooks)
+ camel_object_unget_hooks(o);
camel_object_trigger_event(o, "finalize", NULL);
@@ -686,13 +736,6 @@ static CamelHookList *camel_object_get_hooks(CamelObject *o)
return o->hooks;
}
-/* unlock object hooks' list */
-#ifdef ENABLE_THREADS
-#define camel_object_unget_hooks(o) (e_mutex_unlock((CAMEL_OBJECT(o)->hooks->lock)))
-#else
-#define camel_object_unget_hooks(o)
-#endif
-
unsigned int
camel_object_hook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data)
{
@@ -1040,3 +1083,243 @@ camel_object_class_dump_tree(CamelType root)
{
object_class_dump_tree_rec(root, 0);
}
+
+CamelObjectBag *camel_object_bag_new(GHashFunc hash, GEqualFunc equal)
+{
+ CamelObjectBag *bag;
+
+ bag = g_malloc(sizeof(*bag));
+ bag->object_table = g_hash_table_new(hash, equal);
+ bag->key_table = g_hash_table_new(NULL, NULL);
+ bag->flags = 0;
+#ifdef ENABLE_THREADS
+ pthread_cond_init(&bag->cond, NULL);
+#endif
+ return bag;
+}
+
+static void
+remove_bag(char *key, CamelObject *o, CamelObjectBag *bag)
+{
+ camel_object_bag_remove(bag, o);
+ g_free(key);
+}
+
+void camel_object_bag_destroy(CamelObjectBag *bag)
+{
+ g_assert(bag->flags == 0);
+
+ g_hash_table_foreach(bag->object_table, (GHFunc)remove_bag, bag);
+ g_hash_table_destroy(bag->object_table);
+ g_hash_table_destroy(bag->key_table);
+ g_free(bag);
+}
+
+void camel_object_bag_add(CamelObjectBag *bag, const char *key, void *vo)
+{
+ CamelObject *o = vo;
+ CamelHookList *hooks;
+ CamelHookPair *pair;
+ char *k;
+
+ hooks = camel_object_get_hooks(o);
+ E_LOCK(type_lock);
+
+ pair = hooks->list;
+ while (pair) {
+ if (pair->name == bag_name && pair->data == bag) {
+ E_UNLOCK(type_lock);
+ camel_object_unget_hooks(o);
+ return;
+ }
+ pair = pair->next;
+ }
+
+ pair = pair_alloc();
+ pair->name = bag_name;
+ pair->data = bag;
+ pair->flags = 0;
+
+ pair->next = hooks->list;
+ hooks->list = pair;
+ hooks->list_length++;
+
+ k = g_strdup(key);
+ g_hash_table_insert(bag->object_table, k, vo);
+ g_hash_table_insert(bag->key_table, vo, k);
+
+#ifdef ENABLE_THREADS
+ if (bag->flags & CAMEL_OBJECT_BAG_RESERVED
+ && bag->owner == pthread_self()) {
+ bag->flags &= ~CAMEL_OBJECT_BAG_RESERVED;
+ pthread_cond_broadcast(&bag->cond);
+ }
+#endif
+
+ E_UNLOCK(type_lock);
+ camel_object_unget_hooks(o);
+}
+
+void *camel_object_bag_get(CamelObjectBag *bag, const char *key)
+{
+ CamelObject *o;
+ int retry;
+
+ E_LOCK(type_lock);
+
+ do {
+ retry = FALSE;
+ o = g_hash_table_lookup(bag->object_table, key);
+ if (o) {
+ /* we use the same lock as the refcount */
+ o->ref_count++;
+ }
+#ifdef ENABLE_THREADS
+ else {
+ if (bag->flags & CAMEL_OBJECT_BAG_RESERVED) {
+ e_mutex_cond_wait(&bag->cond, type_lock);
+ retry = TRUE;
+ }
+ }
+#endif
+ } while (retry);
+
+ E_UNLOCK(type_lock);
+
+ return o;
+}
+
+/* like get, but also reserve a spot for key if it isn't there */
+/* After calling reserve, you MUST call bag_abort or bag_add */
+/* Also note that currently you can only reserve a single key
+ at any one time in a given thread */
+void *camel_object_bag_reserve(CamelObjectBag *bag, const char *key)
+{
+ CamelObject *o;
+ int retry;
+
+ E_LOCK(type_lock);
+
+ do {
+ retry = FALSE;
+ o = g_hash_table_lookup(bag->object_table, key);
+ if (o) {
+ /* we use the same lock as the refcount */
+ o->ref_count++;
+ }
+#ifdef ENABLE_THREADS
+ else {
+ /* NOTE: We dont actually reserve the key, we just reserve
+ the whole bag. We could do either but this is easier */
+ if (bag->flags & CAMEL_OBJECT_BAG_RESERVED) {
+ e_mutex_cond_wait(&bag->cond, type_lock);
+ retry = TRUE;
+ } else {
+ bag->flags |= CAMEL_OBJECT_BAG_RESERVED;
+ bag->owner = pthread_self();
+ }
+ }
+#endif
+ } while (retry);
+
+ E_UNLOCK(type_lock);
+
+ return o;
+}
+
+/* abort a reserved key */
+void camel_object_bag_abort(CamelObjectBag *bag, const char *key)
+{
+ CamelObject *o;
+ int retry;
+
+ E_LOCK(type_lock);
+
+ /* NOTE: We dont actually reserve the key, we just reserve
+ the whole bag. We could do either but this is easier */
+
+ g_assert(bag->owner == pthread_self());
+ g_assert(bag->flags & CAMEL_OBJECT_BAG_RESERVED);
+
+ bag->owner = 0;
+ bag->flags &= ~CAMEL_OBJECT_BAG_RESERVED;
+
+ pthread_cond_broadcast(&bag->cond);
+
+ E_UNLOCK(type_lock);
+}
+
+static void
+save_bag(char *key, CamelObject *o, GPtrArray *list)
+{
+ /* we have the refcount lock already */
+ o->ref_count++;
+ g_ptr_array_add(list, o);
+}
+
+/* get a list of all objects in the bag, ref'd */
+GPtrArray *camel_object_bag_list(CamelObjectBag *bag)
+{
+ GPtrArray *list;
+
+ list = g_ptr_array_new();
+ E_LOCK(type_lock);
+
+ while (bag->flags & CAMEL_OBJECT_BAG_RESERVED)
+ e_mutex_cond_wait(&bag->cond, type_lock);
+
+ g_hash_table_foreach(bag->object_table, (GHFunc)save_bag, list);
+ E_UNLOCK(type_lock);
+
+ return list;
+}
+
+/* if bag is NULL, remove all bags from object */
+static void camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks)
+{
+ CamelHookPair *pair, *parent;
+ char *oldkey;
+ CamelObjectBag *bag;
+
+ parent = (CamelHookPair *)&hooks->list;
+ pair = parent->next;
+ while (pair) {
+ if (pair->name == bag_name
+ && (inbag == NULL || inbag == pair->data)) {
+ bag = pair->data;
+ /* lookup object in table? */
+ oldkey = g_hash_table_lookup(bag->key_table, o);
+ if (oldkey) {
+ g_hash_table_remove(bag->key_table, o);
+ g_hash_table_remove(bag->object_table, oldkey);
+ g_free(oldkey);
+ }
+ parent->next = pair->next;
+ pair_free(pair);
+ hooks->list_length--;
+ } else {
+ parent = pair;
+ }
+ pair = parent->next;
+ }
+}
+
+void camel_object_bag_remove(CamelObjectBag *inbag, void *vo)
+{
+ CamelObject *o = vo;
+ CamelHookPair *pair, *parent;
+ CamelHookList *hooks;
+ char *oldkey;
+ CamelObjectBag *bag;
+
+ if (o->hooks == NULL)
+ return;
+
+ hooks = camel_object_get_hooks(o);
+ E_LOCK(type_lock);
+
+ camel_object_bag_remove_unlocked(inbag, o, hooks);
+
+ E_UNLOCK(type_lock);
+ camel_object_unget_hooks(o);
+}