diff options
author | Not Zed <NotZed@Ximian.com> | 2004-01-15 14:18:56 +0800 |
---|---|---|
committer | Michael Zucci <zucchi@src.gnome.org> | 2004-01-15 14:18:56 +0800 |
commit | 1f36dc67c07a017659d0fb2b543d8706b5425e0b (patch) | |
tree | c6e8d1edec579e04f651d9cda2b10687fff55439 /camel | |
parent | bbdcea50101fdf73c85a422af5e06c91d4204334 (diff) | |
download | gsoc2013-evolution-1f36dc67c07a017659d0fb2b543d8706b5425e0b.tar.gz gsoc2013-evolution-1f36dc67c07a017659d0fb2b543d8706b5425e0b.tar.zst gsoc2013-evolution-1f36dc67c07a017659d0fb2b543d8706b5425e0b.zip |
** See bug #52881.
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.
svn path=/trunk/; revision=24233
Diffstat (limited to 'camel')
-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 |