Index: /branches/eam_branches/ipp-20110505/psLib/src/sys/psMemory.c
===================================================================
--- /branches/eam_branches/ipp-20110505/psLib/src/sys/psMemory.c	(revision 31568)
+++ /branches/eam_branches/ipp-20110505/psLib/src/sys/psMemory.c	(revision 31569)
@@ -35,4 +35,5 @@
 #include "psError.h"    // for psErrorStackPrint() only
 #include "psMemory.h"
+#include "psAbort.h"
 
 // Magic number in psMemBlock header
@@ -325,8 +326,158 @@
 }
 
+/** BLOCK_LOCK, BLOCK_UNLOCK, BLOCKSET_LOCK, BLOCKSET_UNLOCK are used to set per-memBlock locks
+ **     to allow psRealloc to release the global lock before performing the expensive system
+ **     call 'realloc'
+ **/
+
+// if HARDLOCKS is set, revert to global locking for realloc
+# define HARDLOCKS 1
+
+// memBlock->inFlight is a 'soft' lock.  if true, the lock is set
+void BLOCK_LOCK (psMemBlock *memBlock, bool keepMutex) {
+
+# if (HARDLOCKS) 
+    MUTEX_LOCK(&memBlockListMutex);
+# else
+
+    psAssert (memBlock || keepMutex, "trying to set a soft lock on a non-existent memBlock");
+
+    // if memBlock is not defined, we are just asking for a m
+    if (!memBlock) {
+	MUTEX_LOCK(&memBlockListMutex);
+	return;
+    }
+	
+    while (true) {
+	// we cannot set the lock on the marker while the lock is held
+	while (memBlock->inFlight) { usleep (10000); }
+
+	// set a lock, then lock this memblock and its neighbors
+	MUTEX_LOCK(&memBlockListMutex);
+    
+	// did we beat the race condition?  is the lock still clear?
+	if (memBlock->inFlight) {
+	    // nope, we need to try again
+	    MUTEX_UNLOCK(&memBlockListMutex);
+	    continue;
+	}
+
+	// the marker is ours!
+	memBlock->inFlight = true;
+	if (keepMutex) return;
+
+	MUTEX_UNLOCK(&memBlockListMutex);
+	return;
+    }
+# endif
+}
+
+void BLOCK_UNLOCK (psMemBlock *memBlock, bool haveMutex) {
+    
+# if (HARDLOCKS)
+    MUTEX_UNLOCK(&memBlockListMutex);
+# else
+
+    psAssert (memBlock || haveMutex, "trying to clear a soft lock on a non-existent memBlock");
+
+    // if memBlock is not defined, we are just asking for a regular lock
+    if (!memBlock) {
+	MUTEX_UNLOCK(&memBlockListMutex);
+	return;
+    }
+
+    // need to lock before clearing the marker
+    if (!haveMutex) {
+	MUTEX_LOCK(&memBlockListMutex);
+    }
+    memBlock->inFlight = false;
+    MUTEX_UNLOCK(&memBlockListMutex);
+    return;
+# endif    
+}
+
+// attempt to grab the markers on the given memBlock and its two neighbors.  the only times the
+// two neighbors do not exist is if we are at the beginning or end of the list (or the list is
+// new).
+void BLOCKSET_LOCK (psMemBlock *memBlock, bool keepMutex) {
+
+# if (HARDLOCKS)
+    MUTEX_LOCK(&memBlockListMutex);
+# else
+
+    psAssert (memBlock, "trying to set a soft lock on a non-existent memBlock");
+
+    while (true) {
+	// we cannot set the lock on the marker while the lock is held
+	// wait until all three markers are clear:
+	while (memBlock->inFlight) { usleep (10000); }
+	if (memBlock->nextBlock) {
+	    while (memBlock->nextBlock->inFlight) { usleep (10000); }
+	}
+	if (memBlock->previousBlock) {
+	    while (memBlock->previousBlock->inFlight) { usleep (10000); }
+	}
+
+	// set a lock, then lock this memblock and its neighbors
+	MUTEX_LOCK(&memBlockListMutex);
+    
+	// did we beat the race condition?  are all three locks still clear?
+	if (memBlock->inFlight) {
+	    // nope, we need to try again
+	    MUTEX_UNLOCK(&memBlockListMutex);
+	    continue;
+	}
+	if (memBlock->nextBlock && memBlock->nextBlock->inFlight) {
+	    // nope, we need to try again
+	    MUTEX_UNLOCK(&memBlockListMutex);
+	    continue;
+	}
+	if (memBlock->previousBlock && memBlock->previousBlock->inFlight) {
+	    // nope, we need to try again
+	    MUTEX_UNLOCK(&memBlockListMutex);
+	    continue;
+	}
+
+	// the markers are ours!
+	memBlock->inFlight = true;
+	if (memBlock->nextBlock) {
+	    memBlock->nextBlock->inFlight = true;
+	}
+	if (memBlock->previousBlock) {
+	    memBlock->previousBlock->inFlight = true;
+	}
+	if (keepMutex) return;
+
+	MUTEX_UNLOCK(&memBlockListMutex);
+	return;
+    }
+# endif
+}
+
+void BLOCKSET_UNLOCK (psMemBlock *memBlock, bool haveMutex) {
+    
+# if (HARDLOCKS) 
+    MUTEX_UNLOCK(&memBlockListMutex);
+# else
+
+    // need to lock before clearing the marker
+    if (!haveMutex) {
+	MUTEX_LOCK(&memBlockListMutex);
+    }
+    memBlock->inFlight = false;
+    if (memBlock->nextBlock) {
+	memBlock->nextBlock->inFlight = false;
+    }
+    if (memBlock->previousBlock) {
+	memBlock->previousBlock->inFlight = false;
+    }
+    MUTEX_UNLOCK(&memBlockListMutex);
+    return;
+# endif
+}
+
 // pointer to the last mem block that was allocated.
 // This is the root of the entire memory list
 static psMemBlock *lastMemBlockAllocated = NULL;
-static bool lastMemBlockInFlight = false;
 
 /* Actually allocate memory
@@ -366,4 +517,7 @@
     // function
     memBlock->func = func;
+
+    // per-block lock (off by default)
+    memBlock->inFlight = false;
 
     #if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
@@ -397,7 +551,9 @@
     // need exclusive access of the memory block list now...
     // this lock is very frequently called in a given program
-    MUTEX_LOCK(&memBlockListMutex);
-
-    // increment the memory id only after we've grabbed the memBlockListMutex
+    // lastMemBlockAllocated is always true except the first allocation
+    BLOCK_LOCK (lastMemBlockAllocated, true);
+
+    // increment the memory id only after we've grabbed the memBlockListMutex this value is
+    // returned by psMemGetID and psMemGetLastID, but only ever modified here
     *(psMemId* )&memBlock->id = ++memid;
 
@@ -416,5 +572,5 @@
     }
 
-    MUTEX_UNLOCK(&memBlockListMutex);
+    BLOCK_UNLOCK (memBlock->nextBlock, true);
 
     // And return the user the memory that they allocated
@@ -562,6 +718,7 @@
     */
 
-    // set a lock, then lock this memblock and its neighbors
-    MUTEX_LOCK(&memBlockListMutex);
+    // set a lock, then lock this memblock and its neighbors.  at the end of this call,
+    // the global mutex is released, but the memBlock and its neighbors are protected
+    BLOCKSET_LOCK (memBlock, false);
 
     psMemBlock *nextBlock = memBlock->nextBlock;
@@ -573,13 +730,6 @@
     bool isBlockLast = (memBlock == lastMemBlockAllocated);
 
-    memBlock->inFlight = true;
-    nextBlock->inFlight = true;
-    previousBlock->inFlight = true;
-    if (isBlockLast) lastMemBlockInFlight = true;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    // now that the per-memBlock locks are set, we can unlock the threads and realloc the data
-
+    // do the expensive system call.  other threads can continue to modify other memBlocks
+    // except this one and its two neighbors
     memBlock = (psMemBlock *)realloc(memBlock, sizeof(psMemBlock) + size + sizeof(void *));
     if (memBlock == NULL) {
@@ -616,11 +766,9 @@
 
     // all of the list modifications are done; set the lock and clear the per-memBlock locks
-    MUTEX_LOCK(&memBlockListMutex);
-    memBlock->inFlight = false;
-    nextBlock->inFlight = false;
-    previousBlock->inFlight = false;
-    if (isBlockLast) lastMemBlockInFlight = false;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
+    BLOCKSET_UNLOCK(memBlock, false);
+
+    psAssert (!memBlock->inFlight, "unreleased lock?");
+    psAssert (!nextBlock || !nextBlock->inFlight, "unreleased lock?");
+    psAssert (!previousBlock || !previousBlock->inFlight, "unreleased lock?");
 
     return memBlock + 1;                    // usr memory
@@ -848,5 +996,5 @@
     // if we have multiple references, just decrement the count and return.
     // we need a MUTEX_LOCK here: otherwise, two functions can race on refCounter--;
-    MUTEX_LOCK(&memBlockListMutex);
+    BLOCK_LOCK (memBlock, true);
     if (memBlock->refCounter > 1) {
         memBlock->refCounter--;
@@ -856,8 +1004,8 @@
             memFreeID += memFreeCallback(memBlock);
         }
-        MUTEX_UNLOCK(&memBlockListMutex);
+	BLOCK_UNLOCK(memBlock, true);
         return ptr;
     }
-    MUTEX_UNLOCK(&memBlockListMutex);
+    BLOCK_UNLOCK(memBlock, true);
 
     // we can't invoke freeFunc() while we're holding memBlockListMutex as it
@@ -868,5 +1016,5 @@
 
     // this lock is frequently set in a given program
-    MUTEX_LOCK(&memBlockListMutex);
+    BLOCKSET_LOCK (memBlock, true);
 
     // Did the user ask to be informed about this deallocation?
@@ -881,7 +1029,9 @@
     if (nextBlock != NULL) {
         nextBlock->previousBlock = previousBlock;
+	nextBlock->inFlight = false;
     }
     if (previousBlock != NULL) {
         previousBlock->nextBlock = nextBlock;
+	previousBlock->inFlight = false;
     }
     if (lastMemBlockAllocated == memBlock) {
@@ -893,5 +1043,10 @@
     memBlock->previousBlock = NULL;
 
-    MUTEX_UNLOCK(&memBlockListMutex);
+    BLOCKSET_UNLOCK (memBlock, true);
+
+    // XXX these are slightly dangerous : someone else may grab the lock in the meanwhile...
+    psAssert (!memBlock->inFlight, "unreleased lock?");
+    psAssert (!nextBlock || !nextBlock->inFlight, "unreleased lock?");
+    psAssert (!previousBlock || !previousBlock->inFlight, "unreleased lock?");
 
     // invoke free only after we've released the block list lock as free()
