From 24b06cb2a4030282763c8f60ba3b0b8b806d15d3 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Mon, 9 Dec 2002 00:28:06 +0000 Subject: Use a bag instead of a hashtable to track the cache streams. 2002-12-07 Not Zed * camel-data-cache.c (data_cache_init): Use a bag instead of a hashtable to track the cache streams. (data_cache_finalise): Same. (free_busy): No longer needed. (data_cache_expire): use bag instead of hashtable. (stream_finalised): No longer required. (camel_data_cache_add): objectbagise (camel_data_cache_get): " (camel_data_cache_remove): " (data_cache_path): Set the now expired date before running expiry, so it plays better with multiple threads. Still a couple of harmless races. 2002-12-06 Not Zed * providers/local/camel-spool-store.c (scan_dir): folders -> object bag. (get_folder_info_mbox): folders -> object bag. * providers/local/camel-mh-store.c (folder_info_new): folders -> object bag. * providers/local/camel-maildir-store.c (scan_dir): folders -> object bag. * providers/local/camel-local-store.c (rename_folder): folders -> object bag. * camel-private.h (CamelStorePrivate): Remove 'cache' lock, handled by the objectbag. * providers/imap/camel-imap-store.c (copy_folder): Removed. (imap_store_refresh_folders): folders -> object bag. (get_folder_counts): folders -> object bag. * camel-vee-store.c (vee_get_folder): changes for folders objectbag. (vee_get_folder_info): Change to use folders objectbag. Also, dont refresh the base folder if we're in FAST mode. (build_info): Removed, no longer needed. (vee_rename_folder): Fixed for folders objectbag. * camel-store.c (camel_store_init): init the folders objectbag. (camel_store_finalize): Destroy the folders object bag. (folder_matches): (folder_finalize): Removed, now handled implicitly by the objectbag. (camel_store_get_folder): object bag changes. (camel_store_delete_folder): " (get_subfolders): Removed, now handled without a callback. (camel_store_rename_folder): Changed to use object bag of folders. (trash_add_folder): Removed. (init_trash): use folders object bag. (copy_folder_cache): (sync_folder): Removed, no longer needed. Weird arsed code anyway. (store_sync): Use folder object bag instead of hashtable. (camel_store_unsubscribe_folder): " (camel_store_init): remove cache_lock init, no longer used. (camel_store_finalize): Same for cleanup. 2002-12-05 Not Zed * camel-store.h (struct _CamelStore): change folders from a hashtable into a CamelObjectBag. * camel-object.c (camel_object_ref): Use type_lock instead of class lock for ref counting. (camel_object_unref): Use type_lock instead of class lock for unref. (camel_object_unref): If the object is 'bagged', then also look hooks, and remove it from any bags. (camel_object_bag_new): (camel_object_bag_destroy): (camel_object_bag_add): (camel_object_bag_get): (camel_object_bag_remove_unlocked): (camel_object_bag_list): (camel_object_bag_abort): (camel_object_bag_remove): New functions to implement a utility object which can manage a 'bag' of weakly ref'd children in an atomic & threadsafe way. svn path=/trunk/; revision=19056 --- camel/camel-object.c | 307 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 295 insertions(+), 12 deletions(-) (limited to 'camel/camel-object.c') 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 #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); +} -- cgit v1.2.3