Index: src/agents/store/stored.c
===================================================================
--- src/agents/store/stored.c	(revision 1222)
+++ src/agents/store/stored.c	(working copy)
@@ -318,6 +318,8 @@
     XplStartMainThread(AGENT_NAME, &id, StoreServer, 8192, NULL, ccode);
     Ringlog("Stopping main thread");
     
+    LogicalLockDestroy();
+
     XplUnloadApp(XplGetThreadID());
     MsgClearRecoveryFlag("store");
     
Index: src/agents/store/stored.h
===================================================================
--- src/agents/store/stored.h	(revision 1222)
+++ src/agents/store/stored.h	(working copy)
@@ -426,8 +426,9 @@
 } LogicalLockType;
 
 void LogicalLockInit();
-int LogicalLockGain(StoreClient *client, StoreObject *object, LogicalLockType type);
-void LogicalLockRelease(StoreClient *client, StoreObject *object);
+int LogicalLockGain(StoreClient *client, LogicalLockType type, const char *location);
+void LogicalLockRelease(StoreClient *client, LogicalLockType type, const char *location);
+void LogicalLockDestroy();
 
 /* hardcoded guids: */
 
Index: src/agents/store/command.c
===================================================================
--- src/agents/store/command.c	(revision 1222)
+++ src/agents/store/command.c	(working copy)
@@ -1481,9 +1481,9 @@
 	ccode = StoreObjectCheckAuthorization(client, container, STORE_PRIV_LIST);
 	if (ccode) return ConnWriteStr(client->conn, MSG4240NOPERMISSION);
 	
-	if (LogicalLockGain(client, container, LLOCK_READONLY)) {
+	if (LogicalLockGain(client, LLOCK_READONLY, "StoreCommandCOLLECTIONS")) {
 		ccode = StoreObjectIterSubcollections(client, container);
-		LogicalLockRelease(client, container);
+		LogicalLockRelease(client, LLOCK_READONLY, "StoreCommandCOLLECTIONS");
 		return ccode;
 	} else {
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
@@ -1548,6 +1548,7 @@
 	char srcpath[XPL_MAX_PATH + 1];
 	char dstpath[XPL_MAX_PATH + 1];
 	StoreObject newobject;
+	LogicalLockType lock=LLOCK_NONE;
 	
 	CHECK_NOT_READONLY(client)
 	
@@ -1555,13 +1556,14 @@
 	if (STORE_IS_FOLDER(object->type)) return ConnWriteStr(client->conn, MSG3015BADDOCTYPE);
 	
 	// take a RO lock on the source to ensure it doesn't change while we copy it
-	if (! LogicalLockGain(client, object, LLOCK_READONLY))
+	if (! LogicalLockGain(client, LLOCK_READONLY, "StoreCommandCopy"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
+	lock=LLOCK_READONLY;
 	
 	// check authorization on the parent collection
 	ccode = StoreObjectCheckAuthorization(client, collection, STORE_PRIV_BIND | STORE_PRIV_READ);
 	if (ccode) {
-		LogicalLockRelease(client, object);
+		LogicalLockRelease(client, lock, "StoreCommandCopy");
 		return ConnWriteStr(client->conn, MSG4240NOPERMISSION);
 	}
 	
@@ -1587,8 +1589,10 @@
 	newobject.guid = 0;
 	newobject.filename[0] = '\0';
 		
-	if (StoreObjectCreate(client, &newobject))
-		return ConnWriteStr(client->conn, MSG5005DBLIBERR);
+	if (StoreObjectCreate(client, &newobject)) {
+		ccode = ConnWriteStr(client->conn, MSG5005DBLIBERR);
+		goto finish;
+	}
 	
 	StoreObjectCopyInfo(client, object, &newobject);
 	
@@ -1605,11 +1609,16 @@
 	}
 	unlink(srcpath);
 	
+	/* i've got a readonly lock.  i need to release it so that i can get a rw lock */
+	LogicalLockRelease(client, lock, "StoreCommandCopy");
+	lock=LLOCK_NONE;
+
 	// update metadata, at this point we need our RW-lock
-	if (! LogicalLockGain(client, collection, LLOCK_READWRITE)) {
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandCopy")) {
 		ccode = ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 		goto finish;
 	}
+	lock=LLOCK_READWRITE;
 	newobject.collection_guid = collection->guid;
 	newobject.time_created = newobject.time_modified = time(NULL);
 	
@@ -1619,8 +1628,8 @@
 	
 	StoreObjectUpdateImapUID(client, &newobject);
 	
-	LogicalLockRelease(client, collection);
-	LogicalLockRelease(client, object);
+	LogicalLockRelease(client, lock, "StoreCommandCopy");
+	lock=LLOCK_NONE;
 	
 	++client->stats.insertions;
 	StoreWatcherEvent(client, &newobject, STORE_WATCH_EVENT_NEW);
@@ -1629,7 +1638,9 @@
 		newobject.guid, newobject.version);
 
 finish:
-	LogicalLockRelease(client, object);
+	if (lock!=LLOCK_NONE) {
+		LogicalLockRelease(client, lock, "StoreCommandCopy");
+	}
 	return ccode;
 }
 
@@ -1644,6 +1655,7 @@
 	char container_path[MAX_FILE_NAME+1];
 	char *container_final_sep = NULL;
 	char *ptr;
+	LogicalLockType lock=LLOCK_NONE;
 	
 	CHECK_NOT_READONLY(client)
 	
@@ -1739,11 +1751,12 @@
 	}
 	
 	// grab the lock on the container now we're modifying it directly
-	if (! LogicalLockGain(client, &container, LLOCK_READWRITE)) {
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandCREATE")) {
 		// FIXME: here and below, what if we created other collections?
 		StoreObjectRemove(client, &object);
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	}
+	lock=LLOCK_READWRITE;
 	
 	// check we can save this new object and if not, error out
 	object.collection_guid = container.guid;
@@ -1751,12 +1764,13 @@
 	
 	if (StoreObjectSave(client, &object)) {
 		StoreObjectRemove(client, &object);
-		LogicalLockRelease(client, &container);
+		LogicalLockRelease(client, lock, "StoreCommandCREATE");
 		return ConnWriteStr(client->conn, MSG4228CANTWRITEMBOX);
 	}
 	
 	// release any lock we hold
-	LogicalLockRelease(client, &container);
+	LogicalLockRelease(client, lock, "StoreCommandCREATE");
+	lock=LLOCK_NONE;
 	
 	// announce creation on parent container
 	++client->stats.insertions;
@@ -1774,6 +1788,7 @@
 	StoreObject collection;
 	char path[XPL_MAX_PATH]; 
 	int ccode;
+	LogicalLockType lock=LLOCK_NONE;
 	
 	// FIXME: do we want some kind of delete-only mode too?
 	CHECK_NOT_READONLY(client)
@@ -1790,9 +1805,10 @@
 	if (ccode) return ConnWriteStr(client->conn, MSG5005DBLIBERR); // TODO : less generic errors
 	
 	// grab a read/write lock on the containing collection
-	if (! LogicalLockGain(client, &collection, LLOCK_READWRITE)) {
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandDELETE")) {
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	}
+	lock=LLOCK_READWRITE;
 	
 	switch(object->type) {
 	case STORE_DOCTYPE_MAIL:
@@ -1815,7 +1831,7 @@
 	
 	// Release our RW lock now - we can still fail, but that doesn't bring
 	// back the store object we just nuked :)
-	LogicalLockRelease(client, &collection);
+	LogicalLockRelease(client, lock, "StoreCommandDELETE");
 	
 	// Remove the file content from the filesystem
 	FindPathToDocument(client, object->collection_guid, object->guid, path, sizeof(path));
@@ -2129,12 +2145,12 @@
 	}
 	
 	// save the changes
-	LogicalLockGain(client, object, LLOCK_READWRITE);
+	LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandFLAG");
 	if (StoreObjectSave(client, object) != 0) {
-		LogicalLockRelease(client, object);
+		LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandFLAG");
 		return ConnWriteStr(client->conn, MSG5005DBLIBERR);
 	}
-	LogicalLockRelease(client, object);
+	LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandFLAG");
 	
 	++client->stats.updates;
 	StoreWatcherEvent(client, object, STORE_WATCH_EVENT_FLAGS);
@@ -2148,12 +2164,12 @@
                  StorePropInfo *props, int propcount)
 {
 	CCode ret;
-	if (! LogicalLockGain(client, object, LLOCK_READONLY))
+	if (! LogicalLockGain(client, LLOCK_READONLY, "StoreCommandINFO"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	ret = StoreObjectIterDocinfo(client, object, props, propcount, FALSE);
 	
-	LogicalLockRelease(client, object);
+	LogicalLockRelease(client, LLOCK_READONLY, "StoreCommandINFO");
 	return ret;
 }
 
@@ -2222,14 +2238,14 @@
 	if (ccode) return ConnWriteStr(client->conn, MSG4240NOPERMISSION);
 	
 	// grab a read-only lock to ensure consistency
-	if (! LogicalLockGain(client, collection, LLOCK_READONLY)) 
+	if (! LogicalLockGain(client, LLOCK_READONLY, "StoreCommandLIST")) 
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	ccode = StoreObjectIterCollectionContents(client, collection, start, 
 		end, flagsmask, flags, props, propcount, NULL, NULL, FALSE);
 	
 	// release the lock
-	LogicalLockRelease(client, collection);
+	LogicalLockRelease(client, LLOCK_READONLY, "StoreCommandLIST");
 	return ccode;
 }
 
@@ -2243,19 +2259,15 @@
 	
 	// grab the pair of locks we need
 	// FIXME: we need to check first this will not deadlock?
-	if (! LogicalLockGain(client, related, LLOCK_READONLY))
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandLINK")) {
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
-	if (! LogicalLockGain(client, document, LLOCK_READWRITE)) {
-		LogicalLockRelease(client, related);
-		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	}
 	
 	// link the documents together
 	ret = StoreObjectLink(client, document, related);
 	
-	// release our locks
-	LogicalLockRelease(client, document);
-	LogicalLockRelease(client, related);
+	// release our lock
+	LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandLINK");
 	
 	if (ret == 0) {
 		return ConnWriteStr(client->conn, MSG1000OK);
@@ -2274,12 +2286,12 @@
 	ccode = StoreObjectCheckAuthorization(client, document, STORE_PRIV_READ);
 	if (ccode) return ccode;
 	
-	if (! LogicalLockGain(client, document, LLOCK_READONLY))
+	if (! LogicalLockGain(client, LLOCK_READONLY, "StoreCommandLINKS"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	ccode = StoreObjectIterLinks(client, document, reverse);
 	
-	LogicalLockRelease(client, document);
+	LogicalLockRelease(client, LLOCK_READONLY, "StoreCommandLINKS");
 	
 	return ccode;
 }
@@ -2292,12 +2304,12 @@
 	CCode ret;
 	CHECK_NOT_READONLY(client)
 	
-	if (! LogicalLockGain(client, document, LLOCK_READWRITE))
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandUNLINK"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	ret = StoreObjectUnlink(client, document, related);
 	
-	LogicalLockRelease(client, document);
+	LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandUNLINK");
 	
 	if (ret == 0) {
 		return ConnWriteStr(client->conn, MSG1000OK);
@@ -2355,7 +2367,7 @@
 	
 	if (STORE_IS_FOLDER(document->type)) return ConnWriteStr(client->conn, MSG3015BADDOCTYPE);
 
-	if (! LogicalLockGain(client, document, LLOCK_READONLY))
+	if (! LogicalLockGain(client, LLOCK_READONLY, "StoreCommandMIME"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 
 	// FIXME: need to re-do the MimeGetInfo() API for store objects
@@ -2386,7 +2398,7 @@
 			break;
 	}
 	
-	LogicalLockRelease(client, document);
+	LogicalLockRelease(client, LLOCK_READONLY, "StoreCommandMIME");
 	
 	return ccode;
 }
@@ -2402,6 +2414,7 @@
 	char dst_path[XPL_MAX_PATH];
 	StoreObject source_collection;
 	StoreObject original;
+	LogicalLockType lock=LLOCK_NONE;
 	
 	CHECK_NOT_READONLY(client)
 	
@@ -2422,13 +2435,10 @@
 	
 	// grab the locks that we need to do the move
 	// FIXME: both RW locks, real chance of deadlock here.
-	if (! LogicalLockGain(client, &source_collection, LLOCK_READWRITE)) {
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandMOVE")) {
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	}
-	if (! LogicalLockGain(client, destination_collection, LLOCK_READWRITE)) {
-		LogicalLockRelease(client, &source_collection);
-		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
-	}
+	lock=LLOCK_READWRITE;
 	
 	// copy the original object so we can fire events on it later
 	memcpy(&original, object, sizeof(StoreObject));
@@ -2478,8 +2488,8 @@
 	StoreObjectSave(client, object);
 	
 	// unlock the collections
-	LogicalLockRelease(client, destination_collection);
-	LogicalLockRelease(client, &source_collection);
+	LogicalLockRelease(client, lock, "StoreCommandMOVE");
+	lock=LLOCK_NONE;
 	
 	// fire off any events
 	StoreWatcherEvent(client, &original, STORE_WATCH_EVENT_DELETED);
@@ -2488,9 +2498,9 @@
 	return ConnWriteStr(client->conn, MSG1000OK);
 	
 finish:
-	// unlock the collections
-	LogicalLockRelease(client, destination_collection);
-	LogicalLockRelease(client, &source_collection);
+	// unlock the collection
+	if (lock!=LLOCK_NONE) 
+		LogicalLockRelease(client, lock, "StoreCommandMOVE");
 
 	return ccode;
 }
@@ -2503,7 +2513,7 @@
 {
 	CCode ret;
 	
-	LogicalLockGain(client, object, LLOCK_READONLY);
+	LogicalLockGain(client, LLOCK_READONLY, "StoreCommandPROPGET");
 	
 	if (propcount == 0) {
 		ret = StoreObjectIterProperties(client, object);
@@ -2512,7 +2522,7 @@
 		ret = StoreObjectIterDocinfo(client, object, props, propcount, TRUE);
 	}
 	
-	LogicalLockRelease(client, object);
+	LogicalLockRelease(client, LLOCK_READONLY, "StoreCommandPROPGET");
 	
 	return ret;
 }
@@ -2552,13 +2562,13 @@
 		// this is how we used to set ACLs, deprecated
 		return ConnWriteStr(client->conn, MSG5005DBLIBERR);
 	
-	if (! LogicalLockGain(client, object, LLOCK_READWRITE))
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandPROPSET"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	// this is a plain external property
 	ccode = StoreObjectSetProperty(client, object, prop);
 	
-	LogicalLockRelease(client, object);
+	LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandPROPSET");
 	
 	if (ccode < -1) {
 		return ConnWriteStr(client->conn, MSG5005DBLIBERR);
@@ -2596,7 +2606,7 @@
 	ccode = StoreObjectCheckAuthorization(client, object, STORE_PRIV_READ);
 	if (ccode) return ccode;
 	
-	if (! LogicalLockGain(client, object, LLOCK_READONLY))
+	if (! LogicalLockGain(client, LLOCK_READONLY, "StoreCommandREAD"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	if (STORE_IS_FOLDER(object->type)) {
@@ -2609,7 +2619,7 @@
 		ccode = ShowDocumentBody(client, object, requestStart, requestLength);
 	}
 	
-	LogicalLockRelease(client, object);
+	LogicalLockRelease(client, LLOCK_READONLY, "StoreCommandREAD");
 	
 	return ccode;
 }
@@ -2648,7 +2658,7 @@
 	ccode = StoreObjectFind(client, collection->collection_guid, &parent_collection);
 	if (ccode != 0) return ConnWriteStr(client->conn, MSG5005DBLIBERR);
 	
-	if (! LogicalLockGain(client, &parent_collection, LLOCK_READWRITE))
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandRENAME"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	if (StoreObjectRenameSubobjects(client, collection, newfilename))
@@ -2658,7 +2668,7 @@
 	if (StoreObjectSave(client, collection)) 
 		goto dberror;
 	
-	LogicalLockRelease(client, &parent_collection);
+	LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandRENAME");
 	
 	// FIXME - need to fire an event for each collection renamed
 	StoreWatcherEvent(client, collection, STORE_WATCH_EVENT_COLL_RENAMED);
@@ -2666,7 +2676,7 @@
 	return ConnWriteStr(client->conn, MSG1000OK);
 
 dberror:
-	LogicalLockRelease(client, &parent_collection);
+	LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandRENAME");
 	return ConnWriteStr(client->conn, MSG5005DBLIBERR);
 }
 
@@ -2733,6 +2743,7 @@
 	char path[XPL_MAX_PATH + 1];
 	char tmppath[XPL_MAX_PATH + 1];
 	uint64_t tmpsize;
+	LogicalLockType lock=LLOCK_NONE;
 
 	CHECK_NOT_READONLY(client)
 
@@ -2766,10 +2777,11 @@
 	
 	// we gain the lock quite late - we have the new file, but haven't updated
 	// anything yet. Potential waste of effort.
-	if (! LogicalLockGain(client, object, LLOCK_READWRITE)) {
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandREPLACE")) {
 		unlink(tmppath);
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	}
+	lock=LLOCK_READWRITE;
 	
 	StoreObjectUpdateImapUID(client, object);
 	StoreProcessDocument(client, object, tmppath);
@@ -2799,7 +2811,8 @@
 		goto finish;
 	}
 	
-	LogicalLockRelease(client, object);
+	LogicalLockRelease(client, lock, "StoreCommandREPLACE");
+	lock=LLOCK_NONE;
 	
 	// now we're done, do watcher events
 	++client->stats.updates;
@@ -2809,7 +2822,8 @@
                        object->guid, object->version);
 
 finish:
-	LogicalLockRelease(client, object);
+	if (lock!=LLOCK_NONE)
+		LogicalLockRelease(client, lock, "StoreCommandREPLACE");
 	return ccode;
 }
 
@@ -2888,12 +2902,12 @@
 	ccode = StoreObjectFind(client, collection->collection_guid, &parent_collection);
 	if (ccode != 0) return ConnWriteStr(client->conn, MSG5005DBLIBERR);
 	
-	if (! LogicalLockGain(client, &parent_collection, LLOCK_READWRITE))
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandREMOVE"))
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	
 	ccode = StoreObjectRemoveCollection(client, collection);
 	
-	LogicalLockRelease(client, &parent_collection);
+	LogicalLockRelease(client, LLOCK_READWRITE, "StoreCommandREMOVE");
 	
 	if (ccode == 0) {
 		// finished with success!
@@ -3082,6 +3096,7 @@
 	uint64_t tmpsize;
 	char path[XPL_MAX_PATH+1];
 	char tmppath[XPL_MAX_PATH+1];
+	LogicalLockType lock=LLOCK_NONE;
 	
 	UNUSED_PARAMETER_REFACTOR(guid_link);
 
@@ -3117,10 +3132,11 @@
 	
 	StoreObjectFixUpFilename(collection, &newdocument);
 	
-	if (! LogicalLockGain(client, collection, LLOCK_READWRITE)) {
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "StoreCommandWRITE")) {
 		unlink(tmppath);
 		return ConnWriteStr(client->conn, MSG4120BOXLOCKED);
 	}
+	lock=LLOCK_READWRITE;
 	
 	StoreObjectUpdateImapUID(client, &newdocument);
 	if (no_process == 0) {
@@ -3136,10 +3152,10 @@
 	// save new object
 	if (StoreObjectSave(client, &newdocument)) {
 		StoreObjectRemove(client, &newdocument);
-		LogicalLockRelease(client, collection);
+		LogicalLockRelease(client, lock, "StoreCommandWRITE");
 		return ConnWriteStr(client->conn, MSG5005DBLIBERR);
 	}
-	LogicalLockRelease(client, collection);
+	LogicalLockRelease(client, lock, "StoreCommandWRITE");
 	
 	// announce its creation
 	++client->stats.insertions;
@@ -3148,5 +3164,7 @@
 	return ConnWriteF(client->conn, "1000 " GUID_FMT " %d\r\n", newdocument.guid, newdocument.version);
 
 finish:
+	if (lock!=LLOCK_NONE)
+		LogicalLockRelease(client, lock, "StoreCommandWRITE");
 	return ccode;
 }
Index: src/agents/store/locking.c
===================================================================
--- src/agents/store/locking.c	(revision 1222)
+++ src/agents/store/locking.c	(working copy)
@@ -19,12 +19,31 @@
 #include "stored.h"
 
 static XplMutex logicallock_global;
+static GHashTable *storelocks_global;
 
+typedef struct {
+	char *		store;
+	XplMutex	lock;
+	XplThreadID	thread;
+} StoreLock;
 
+/* FIXME: do i need to worry about the mutex being locked? */
 void
+pvt_MemFree(void *data) {
+	StoreLock *sl=data;
+
+	XplMutexDestroy(sl->lock);
+	MemFree(sl->store);
+	MemFree(data);
+}
+
+void
 LogicalLockInit()
 {
 	XplMutexInit(logicallock_global);
+
+	/* go ahead and initialize the hash table now */
+	storelocks_global = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, pvt_MemFree);
 }
 
 /* Acquire the desired logical lock. 
@@ -32,26 +51,92 @@
  * Return 0 on failure
  */
 int
-LogicalLockGain(StoreClient *client, StoreObject *object, LogicalLockType type)
+LogicalLockGain(StoreClient *client, LogicalLockType type, const char *location)
 {
-	UNUSED_PARAMETER_REFACTOR(client);
-	UNUSED_PARAMETER_REFACTOR(object);
-	UNUSED_PARAMETER_REFACTOR(type);
-	
+	/* this has to be on the heap since it has to be valid across threads */
+	StoreLock *sl;
+	gpointer orig_key;
+	gpointer sl_tmp;
+
+	UNUSED_PARAMETER(type);
+	UNUSED_PARAMETER(location);
+
 	XplMutexLock(logicallock_global);
-	
+	if (!g_hash_table_lookup_extended(storelocks_global, client->storeName, &orig_key, &sl_tmp)) {
+		/* it must not exist, create it first.  i can lock it too since this is new. */
+		sl = MemMalloc(sizeof(StoreLock));
+		if (!sl) {
+			XplMutexUnlock(logicallock_global);
+			return 0;
+		}
+		XplMutexInit(sl->lock);
+		XplMutexLock(sl->lock);
+		sl->thread=XplGetThreadID();
+		sl->store=MemStrdup(client->storeName);
+
+		g_hash_table_insert(storelocks_global, sl->store, sl);
+
+		/* unlock the global */
+		XplMutexUnlock(logicallock_global);
+	} else {
+		sl=(StoreLock *)sl_tmp;
+
+		/* unlock the global so we don't deadlock */
+		XplMutexUnlock(logicallock_global);
+
+		XplMutexLock(sl->lock);
+		sl->thread = XplGetThreadID();
+
+		/* note: the store name is already set.  we don't need to re-set it */
+	}
+
+	Log(LOG_DEBUG, "%lu gained lock on %s in %s", XplGetThreadID(), client->storeName, location);
 	return 1;
 }
 
+void
+printfunc(gpointer key, gpointer value, gpointer user_data) {
+	StoreLock *sl=value;
+
+	Log(LOG_DEBUG, "lock table: LOCK on %s by %lu (%s)", key, sl->thread, (char *)user_data);
+}
+
 /* Free the desired logical lock.
  * One way or another, this function must succeed!
  * It's not an error to call this on an object already unlocked?
  */
 void
-LogicalLockRelease(StoreClient *client, StoreObject *object)
+LogicalLockRelease(StoreClient *client, LogicalLockType type, const char *location)
 {
-	UNUSED_PARAMETER_REFACTOR(client);
-	UNUSED_PARAMETER_REFACTOR(object);
-	
+	StoreLock *sl;
+	gpointer orig_key;
+	gpointer sl_tmp;
+
+	UNUSED_PARAMETER(type);
+
+	XplMutexLock(logicallock_global);
+
+	if (g_hash_table_lookup_extended(storelocks_global, client->storeName, &orig_key, &sl_tmp)) {
+		sl=(StoreLock *)sl_tmp;
+
+		sl->thread = 0;
+		XplMutexUnlock(sl->lock);
+		Log(LOG_DEBUG, "%lu released lock on %s in %s", XplGetThreadID(), client->storeName, location);
+	} else {
+		Log(LOG_DEBUG, "Release Failed for %s by %lu in %s.  Printing table", client->storeName, XplGetThreadID(), location);
+		g_hash_table_foreach(storelocks_global, printfunc, (char *)location);
+	}
+
 	XplMutexUnlock(logicallock_global);
 }
+
+/* Free all the store locks that are around */
+void
+LogicalLockDestroy() {
+	XplMutexLock(logicallock_global);
+
+	g_hash_table_destroy(storelocks_global);
+
+	XplMutexUnlock(logicallock_global);
+	XplMutexDestroy(logicallock_global);
+}
Index: src/agents/store/store.c
===================================================================
--- src/agents/store/store.c	(revision 1222)
+++ src/agents/store/store.c	(working copy)
@@ -229,6 +229,7 @@
 	char mailbox[XPL_MAX_PATH+1];
 	unsigned char timeBuffer[80];
 	char buffer[1024];
+	LogicalLockType lock=LLOCK_NONE;
 
 	if (scmsID || msglen) {
 		// FIXME : SCMS isn't implemented this way any more.
@@ -317,10 +318,11 @@
 	newmail.time_created = newmail.time_modified = (uint64_t) time(NULL);
 	
 	// need to acquire lock from this point
-	if (! LogicalLockGain(client, &collection, LLOCK_READWRITE)) {
+	if (! LogicalLockGain(client, LLOCK_READWRITE, "DeliverMessageToMailbox")) {
 		StoreObjectRemove(client, &newmail);
 		goto ioerror;
 	}
+	lock=LLOCK_READWRITE;
 	
 	// possibly need more locking involved in this function
 	StoreProcessDocument(client, &newmail, tmppath);
@@ -338,7 +340,8 @@
 	StoreObjectUpdateImapUID(client, &newmail); // FIXME: racy?
 	
 	// release lock here?
-	LogicalLockRelease(client, &collection);
+	LogicalLockRelease(client, lock, "DeliverMessageToMailbox");
+	lock=LLOCK_NONE;
 	
 	// announce new mail has arrived
 	++client->stats.insertions;
@@ -347,6 +350,8 @@
 	return DELIVER_SUCCESS;
 
 ioerror:
+	if (lock!=LLOCK_NONE)
+		LogicalLockRelease(client, lock, "DeliverMessageToMailbox");
 	if (tmp) {
 		fclose(tmp);
 		unlink(tmppath);
