aboutsummaryrefslogtreecommitdiffstats
path: root/camel
diff options
context:
space:
mode:
Diffstat (limited to 'camel')
-rw-r--r--camel/ChangeLog15
-rw-r--r--camel/camel-object.c154
-rw-r--r--camel/camel-vee-store.c114
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