diff options
-rw-r--r-- | camel/ChangeLog | 15 | ||||
-rw-r--r-- | camel/camel-object.c | 154 | ||||
-rw-r--r-- | camel/camel-vee-store.c | 114 |
3 files changed, 218 insertions, 65 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index ac3c4b6115..4548e560a6 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,18 @@ +2004-01-15 Not Zed <NotZed@Ximian.com> + + ** See bug #52881. + + * camel-object.c (camel_object_bag*): Support reserving different + keys from the same thread. Oh the pain. + + * camel-vee-store.c (vee_get_folder_info): implement child flags + properly. Changed to build tree itself rather than calling + camel_folder_info_build. + (vee_get_folder): if we're adding a folder with dummy parents, + create and add the dummy parent folders too (as real folder + objects). We are the only owner of the ref, so this sort of leaks + the folder, but they're small. + 2004-01-14 Rodrigo Moya <rodrigo@ximian.com> * providers/groupwise/camel-groupwise-provider.c diff --git a/camel/camel-object.c b/camel/camel-object.c index 744e9f7566..3d7c90baa6 100644 --- a/camel/camel-object.c +++ b/camel/camel-object.c @@ -36,6 +36,7 @@ #include <e-util/e-msgport.h> #define d(x) +#define b(x) /* object bag */ /* I just mashed the keyboard for these... */ #define CAMEL_OBJECT_MAGIC 0x77A344ED @@ -80,13 +81,23 @@ typedef struct _CamelHookPair void *data; } CamelHookPair; +struct _CamelObjectBagKey { + struct _CamelObjectBagKey *next; + + void *key; /* the key reserved */ + int waiters; /* count of threads waiting for key */ + pthread_t owner; /* the thread that has reserved the bag for a new entry */ + sem_t reserve_sem; /* used to track ownership */ +}; + struct _CamelObjectBag { GHashTable *object_table; /* object by key */ GHashTable *key_table; /* key by object */ + GEqualFunc equal_key; CamelCopyFunc copy_key; GFreeFunc free_key; - pthread_t owner; /* the thread that has reserved the bag for a new entry */ - sem_t reserve_sem; /* used to track ownership */ + + struct _CamelObjectBagKey *reserved; }; /* used to tag a bag hookpair */ @@ -1607,14 +1618,12 @@ camel_object_bag_new (GHashFunc hash, GEqualFunc equal, CamelCopyFunc keycopy, G bag = g_malloc(sizeof(*bag)); bag->object_table = g_hash_table_new(hash, equal); + bag->equal_key = equal; bag->copy_key = keycopy; bag->free_key = keyfree; bag->key_table = g_hash_table_new(NULL, NULL); - bag->owner = 0; - - /* init the semaphore to 1 owner, this is who has reserved the bag for adding */ - sem_init(&bag->reserve_sem, 0, 1); - + bag->reserved = NULL; + return bag; } @@ -1630,8 +1639,7 @@ camel_object_bag_destroy (CamelObjectBag *bag) GPtrArray *objects = g_ptr_array_new(); int i; - sem_getvalue(&bag->reserve_sem, &i); - g_assert(i == 1); + g_assert(bag->reserved == NULL); g_hash_table_foreach(bag->object_table, (GHFunc)save_object, objects); for (i=0;i<objects->len;i++) @@ -1640,10 +1648,40 @@ camel_object_bag_destroy (CamelObjectBag *bag) g_ptr_array_free(objects, TRUE); g_hash_table_destroy(bag->object_table); g_hash_table_destroy(bag->key_table); - sem_destroy(&bag->reserve_sem); g_free(bag); } +/* must be called with type_lock held */ +static void +co_bag_unreserve(CamelObjectBag *bag, const void *key) +{ + struct _CamelObjectBagKey *res, *resp; + + resp = (struct _CamelObjectBagKey *)&bag->reserved; + res = resp->next; + while (res) { + if (bag->equal_key(res->key, key)) + break; + resp = res; + res = res->next; + } + + g_assert(res != NULL); + g_assert(res->owner == pthread_self()); + + if (res->waiters > 0) { + b(printf("unreserve bag, waking waiters\n")); + res->owner = 0; + sem_post(&res->reserve_sem); + } else { + b(printf("unreserve bag, no waiters, freeing reservation\n")); + resp->next = res->next; + bag->free_key(res->key); + sem_destroy(&res->reserve_sem); + g_free(res); + } +} + void camel_object_bag_add (CamelObjectBag *bag, const void *key, void *vo) { @@ -1677,11 +1715,8 @@ camel_object_bag_add (CamelObjectBag *bag, const void *key, void *vo) k = bag->copy_key(key); g_hash_table_insert(bag->object_table, k, vo); g_hash_table_insert(bag->key_table, vo, k); - - if (bag->owner == pthread_self()) { - bag->owner = 0; - sem_post(&bag->reserve_sem); - } + + co_bag_unreserve(bag, key); E_UNLOCK(type_lock); camel_object_unget_hooks(o); @@ -1698,16 +1733,33 @@ camel_object_bag_get (CamelObjectBag *bag, const void *key) if (o) { /* we use the same lock as the refcount */ o->ref_count++; - } else if (bag->owner != pthread_self()) { - E_UNLOCK(type_lock); - sem_wait(&bag->reserve_sem); - E_LOCK(type_lock); - /* re-check if it slipped in */ - o = g_hash_table_lookup(bag->object_table, key); - if (o) - o->ref_count++; - /* we dont want to reserve the bag */ - sem_post(&bag->reserve_sem); + } else { + struct _CamelObjectBagKey *res = bag->reserved; + + /* check if this name is reserved currently, if so wait till its finished */ + while (res) { + if (bag->equal_key(res->key, key)) + break; + res = res->next; + } + + if (res) { + res->waiters++; + g_assert(res->owner != pthread_self()); + E_UNLOCK(type_lock); + sem_wait(&res->reserve_sem); + E_LOCK(type_lock); + res->waiters--; + + /* re-check if it slipped in */ + o = g_hash_table_lookup(bag->object_table, key); + if (o) + o->ref_count++; + + /* we don't actually reserve it */ + res->owner = pthread_self(); + co_bag_unreserve(bag, key); + } } E_UNLOCK(type_lock); @@ -1730,18 +1782,41 @@ camel_object_bag_reserve (CamelObjectBag *bag, const void *key) if (o) { o->ref_count++; } else { - g_assert(bag->owner != pthread_self()); - E_UNLOCK(type_lock); - sem_wait(&bag->reserve_sem); - E_LOCK(type_lock); - /* incase its slipped in while we were waiting */ - o = g_hash_table_lookup(bag->object_table, key); - if (o) { - o->ref_count++; - /* in which case we dont need to reserve the bag either */ - sem_post(&bag->reserve_sem); + struct _CamelObjectBagKey *res = bag->reserved; + + while (res) { + if (bag->equal_key(res->key, key)) + break; + res = res->next; + } + + if (res) { + b(printf("bag reserve, already reserved, waiting\n")); + g_assert(res->owner != pthread_self()); + res->waiters++; + E_UNLOCK(type_lock); + sem_wait(&res->reserve_sem); + E_LOCK(type_lock); + res->waiters--; + /* incase its slipped in while we were waiting */ + o = g_hash_table_lookup(bag->object_table, key); + if (o) { + o->ref_count++; + /* in which case we dont need to reserve the bag either */ + res->owner = pthread_self(); + co_bag_unreserve(bag, key); + } else { + res->owner = pthread_self(); + } } else { - bag->owner = pthread_self(); + b(printf("bag reserve, no key, reserving\n")); + res = g_malloc(sizeof(*res)); + res->waiters = 0; + res->key = bag->copy_key(key); + sem_init(&res->reserve_sem, 0, 0); + res->owner = pthread_self(); + res->next = bag->reserved; + bag->reserved = res; } } @@ -1754,10 +1829,11 @@ camel_object_bag_reserve (CamelObjectBag *bag, const void *key) void camel_object_bag_abort (CamelObjectBag *bag, const void *key) { - g_assert(bag->owner == pthread_self()); + E_LOCK(type_lock); - bag->owner = 0; - sem_post(&bag->reserve_sem); + co_bag_unreserve(bag, key); + + E_UNLOCK(type_lock); } static void diff --git a/camel/camel-vee-store.c b/camel/camel-vee-store.c index b13522b8de..2d15bac7c3 100644 --- a/camel/camel-vee-store.c +++ b/camel/camel-vee-store.c @@ -160,11 +160,16 @@ vee_get_folder (CamelStore *store, const char *folder_name, guint32 flags, Camel while ( (p = strchr(p, '/'))) { *p = 0; - folder = camel_object_bag_get(store->folders, name); - if (folder == NULL) - change_folder(store, name, CHANGE_ADD|CHANGE_NOSELECT, -1); - else + folder = camel_object_bag_reserve(store->folders, name); + if (folder == NULL) { + /* create a dummy vFolder for this, makes get_folder_info simpler */ + folder = camel_vee_folder_new(store, name, flags); + camel_object_bag_add(store->folders, name, folder); + change_folder(store, name, CHANGE_ADD|CHANGE_NOSELECT, 0); + /* FIXME: this sort of leaks folder, nobody owns a ref to it but us */ + } else { camel_object_unref(folder); + } *p++='/'; } @@ -200,39 +205,52 @@ vee_get_junk (CamelStore *store, CamelException *ex) return NULL; } +static int +vee_folder_cmp(const void *ap, const void *bp) +{ + return strcmp(((CamelFolder **)ap)[0]->full_name, ((CamelFolder **)bp)[0]->full_name); +} + static CamelFolderInfo * vee_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex) { - CamelFolderInfo *info; + CamelFolderInfo *info, *res = NULL, *tail; GPtrArray *folders, *infos; + GHashTable *infos_hash; int i; - infos = g_ptr_array_new(); + printf("Get folder info '%s'\n", top?top:"<null>"); + + infos_hash = g_hash_table_new(g_str_hash, g_str_equal); folders = camel_object_bag_list(store->folders); + qsort(folders->pdata, folders->len, sizeof(folders->pdata[0]), vee_folder_cmp); for (i=0;i<folders->len;i++) { CamelVeeFolder *folder = folders->pdata[i]; int add = FALSE; - char *name = ((CamelFolder *)folder)->full_name; + char *name = ((CamelFolder *)folder)->full_name, *pname, *tmp; + CamelFolderInfo *pinfo; + + printf("folder '%s'\n", name); /* check we have to include this one */ if (top) { - if (flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) { - int namelen = strlen(name); - int toplen = strlen(top); - - add = ((namelen == toplen && - strcmp(name, top) == 0) - || ((namelen > toplen) - && strncmp(name, top, toplen) == 0 - && name[toplen] == '/')); - } else { - add = strcmp(name, top) == 0; - } + int namelen = strlen(name); + int toplen = strlen(top); + + add = ((namelen == toplen + && strcmp(name, top) == 0) + || ((namelen > toplen) + && strncmp(name, top, toplen) == 0 + && name[toplen] == '/' + && ((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) + || strchr(name+toplen+1, '/') == NULL))); } else { if ((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) == 0) add = strchr(name, '/') == NULL; } + printf("%sadding '%s'\n", add?"":"not ", name); + if (add) { /* ensures unread is correct */ if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0) @@ -244,11 +262,50 @@ vee_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelExce info->full_name = g_strdup(((CamelFolder *)folder)->full_name); info->name = g_strdup(((CamelFolder *)folder)->name); info->unread_message_count = camel_folder_get_unread_message_count((CamelFolder *)folder); - g_ptr_array_add(infos, info); + info->flags = CAMEL_FOLDER_NOCHILDREN; + camel_folder_info_build_path(info, '/'); + g_hash_table_insert(infos_hash, info->full_name, info); + + if (res == NULL) + res = info; + } else { + info = NULL; + } + + /* check for parent, if present, update flags and if adding, update parent linkage */ + pname = g_strdup(((CamelFolder *)folder)->full_name); + printf("looking up parent of '%s'\n", pname); + tmp = strrchr(pname, '/'); + if (tmp) { + *tmp = 0; + pinfo = g_hash_table_lookup(infos_hash, pname); + } else + pinfo = NULL; + + if (pinfo) { + pinfo->flags = (pinfo->flags & ~(CAMEL_FOLDER_CHILDREN|CAMEL_FOLDER_NOCHILDREN))|CAMEL_FOLDER_CHILDREN; + printf("updating parent flags for children '%s' %08x\n", pinfo->full_name, pinfo->flags); + tail = pinfo->child; + if (tail == NULL) + pinfo->child = info; + } else if (info != res) { + tail = res; + } else { + tail = NULL; } + + if (info && tail) { + while (tail->sibling) + tail = tail->sibling; + tail->sibling = info; + info->parent = pinfo; + } + + g_free(pname); camel_object_unref(folder); } g_ptr_array_free(folders, TRUE); + g_hash_table_destroy(infos_hash); /* and always add UNMATCHED, if scanning from top/etc */ if (top == NULL || top[0] == 0 || strncmp(top, CAMEL_UNMATCHED_NAME, strlen(CAMEL_UNMATCHED_NAME)) == 0) { @@ -257,15 +314,20 @@ vee_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelExce info->full_name = g_strdup(CAMEL_UNMATCHED_NAME); info->name = g_strdup(CAMEL_UNMATCHED_NAME); info->unread_message_count = -1; + info->flags = CAMEL_FOLDER_NOCHILDREN|CAMEL_FOLDER_NOINFERIORS; camel_folder_info_build_path(info, '/'); - g_ptr_array_add(infos, info); + + if (res == NULL) + res = info; + else { + tail = res; + while (tail->sibling) + tail = tail->sibling; + tail->sibling = info; + } } - - /* convert it into a tree */ - info = camel_folder_info_build(infos, (top&&top[0])?top:"", '/', TRUE); - g_ptr_array_free(infos, TRUE); - return info; + return res; } static void |