Index: /trunk/psLib/src/sys/psMemory.c
===================================================================
--- /trunk/psLib/src/sys/psMemory.c	(revision 18825)
+++ /trunk/psLib/src/sys/psMemory.c	(revision 18826)
@@ -10,6 +10,6 @@
 *  @author Joshua Hoblitt, University of Hawaii
 *
-*  @version $Revision: 1.98 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2007-08-27 23:16:17 $
+*  @version $Revision: 1.99 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2008-07-31 23:56:10 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -73,18 +73,15 @@
 static bool isBadMemBlock(FILE *output, const psMemBlock *memBlock, const char *file, unsigned int lineo, const char *func);
 
-// pointer to the last mem block that was allocated
-static psMemBlock *lastMemBlockAllocated = NULL;
-
 // memBlockListMutex protects access to:
-//      lastMemBlockAllocated
-//      safeThreads
-//      memid
-//      memory_is_persistent
+//      safeThreads -- very rarely accessed
+//      memory_is_persistent -- very rarely accessed
+//      memAllocCallback -- very rarely accessed
+//      memFreeCallback -- very rarely accessed
+//      memExhaustedCallback -- very rarely accessed
+//      p_psMemAllocID -- rarely accessed
+//      p_psMemFreeID -- rarely accessed
+//      lastMemBlockAllocated 
+//      memid -- rarely accessby 
 //      "the linked list of mem blocks"
-//      p_psMemAllocID
-//      p_psMemFreeID
-//      memAllocCallback
-//      memFreeCallback
-//      memExhaustedCallback
 //
 // This is a fair ammount of stuff to protect with a single mutex but most of
@@ -98,46 +95,334 @@
 static pthread_mutex_t memBlockListMutex = PTHREAD_MUTEX_INITIALIZER;
 
-//private boolean for enabling/disabling thread safety.  Default = enabled.
+/******** thread safety options management ********/
+
+// private boolean for enabling/disabling thread safety.  Default = enabled.
 static bool safeThreads = true;
 
-// private boolean for deciding if allocated memory is persistent
+// Set the thread-safety state of the memory system: default is true
+bool psMemSetThreadSafety(bool safe)
+{
+    // this function is only called ~once per program, before threads are launched
+    MUTEX_LOCK(&memBlockListMutex);
+
+    bool oldState = safeThreads;
+    safeThreads = safe;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return oldState;
+}
+
+
+// Get the thread-safety state of the memory system
+bool psMemGetThreadSafety(void)
+{
+    // this function is only called ~once per program, probably before threads are launched
+    MUTEX_LOCK(&memBlockListMutex);
+
+    bool oldState = safeThreads;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return oldState;
+}
+
+/******** persistent memory options management ********/
+
+// private boolean for deciding if allocated memory is persistent by default
 static bool memory_is_persistent = false;
 
-/**
- * Unique ID for allocated blocks
+/* Set whether allocated memory is persistent
+ */
+bool p_psMemAllocatePersistent(bool is_persistent)
+{
+    // this function is only called ~once per program, before threads are launched
+    MUTEX_LOCK(&memBlockListMutex);
+
+    const bool old = memory_is_persistent;
+    memory_is_persistent = is_persistent;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/*
+ * And now the I-want-to-be-informed callbacks
+ *
+ * Call the callbacks when these IDs are allocated/freed
+ */
+/******** memory allocation callback management (memAllocCallback) ********/
+
+// Default memFreeCallback function
+static psMemId memAllocCallbackDefault(const psMemBlock *memBlock)
+{
+    static psMemId incr = 0; // "p_psMemAllocID += incr"
+
+    return incr;
+}
+
+static psMemAllocCallback memAllocCallback = memAllocCallbackDefault;
+
+psMemAllocCallback psMemAllocCallbackSet(psMemAllocCallback func)
+{
+    // this function is only called rarely per program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemAllocCallback old = memAllocCallback;
+
+    if (func != NULL) {
+        memAllocCallback = func;
+    } else {
+        memAllocCallback = memAllocCallbackDefault;
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+// notify user when this block is allocated
+// XXX why was this not static, and why was it p_psMemAllocID?
+static psMemId p_psMemAllocID = 0;       
+
+// the above callback is only called if a specific psMemId is set with this function
+// this function is rarely called in a given program
+psMemId psMemAllocCallbackSetID(psMemId id)
+{
+    // this function is only called rarely per program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId old = p_psMemAllocID;
+
+    p_psMemAllocID = id;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/******** memory free callback management (memFreeCallback) ********/
+
+// Default memFreeCallback function
+static psMemId memFreeCallbackDefault(const psMemBlock *memBlock)
+{
+    static psMemId incr = 0; // "p_psMemFreeID += incr"
+
+    return incr;
+}
+
+static psMemFreeCallback memFreeCallback = memFreeCallbackDefault;
+
+// this function is called only rarely in a program
+psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func)
+{
+    // this function is only called rarely per program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemFreeCallback old = memFreeCallback;
+
+    if (func != NULL) {
+        memFreeCallback = func;
+    } else {
+        memFreeCallback = memFreeCallbackDefault;
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+// notify user when this block is freed
+// XXX why was this not static?
+static psMemId p_psMemFreeID = 0;
+
+// the above callback is only called if a specific psMemId is set with this function
+psMemId psMemFreeCallbackSetID(psMemId id)
+{
+    // this function is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId old = p_psMemFreeID;
+
+    p_psMemFreeID = id;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/******** memory exhausted callback management (memExhaustedCallback) ********/
+
+// Default memExhaustedCallback function
+static void *memExhaustedCallbackDefault(size_t size)
+{
+    return NULL;
+}
+
+static psMemExhaustedCallback memExhaustedCallback = memExhaustedCallbackDefault;
+
+psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func)
+{
+    // this function is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemExhaustedCallback old = memExhaustedCallback;
+
+    if (func != NULL) {
+        memExhaustedCallback = func;
+    } else {
+        memExhaustedCallback = memExhaustedCallbackDefault;
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return old;
+}
+
+/* An example callback function to check the state of the memory system; may be registered
+ * with psMem{Alloc,Free}CallbackSet
+ */
+psMemId memAllocCallbackCheckCorruption(const psMemBlock *memBlock)
+{
+    static psMemId incr = 10; // "p_psMemAllocID += incr"
+
+    if (psMemCheckCorruption(stderr, false) > 0) {
+        fprintf(stderr, "Detected memory corruption\n"); // somewhere to set a breakpoint
+    }
+
+    return incr;
+}
+
+/**** Unique ID for allocated blocks and associated accessor functions
  */
 static psMemId memid = 0;
 
-/**
- *  Default memExhausted callback.
+/* Return memory ID counter for next block to be allocated
  */
-static void *memExhaustedCallbackDefault(size_t size)
-{
-    return NULL;
-}
-
-/*
- * Default callback for both allocate and free. Note that the
- * value of p_psMemAllocID/p_psMemFreeID is incremented
- * by the return value (so returning 0 means that the callback
- * isn't resignalled)
+psMemId psMemGetId(void)
+{
+    // this function is only occasionally called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId id = memid + 1;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return id;
+}
+
+/* Return memory ID counter for last block allocated
  */
-static psMemId memAllocCallbackDefault(const psMemBlock *memBlock)
-{
-    static psMemId incr = 0; // "p_psMemAllocID += incr"
-
-    return incr;
-}
-
-static psMemId memFreeCallbackDefault(const psMemBlock *memBlock)
-{
-    static psMemId incr = 0; // "p_psMemFreeID += incr"
-
-    return incr;
-}
-
-/*
- * Routines to check the consistency of the allocated and/or free memory arena
- *
+psMemId psMemGetLastId(void)
+{
+    // this function is only occasionally called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psMemId id = memid;
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return id;
+}
+
+// pointer to the last mem block that was allocated.  
+// This is the root of the entire memory list
+static psMemBlock *lastMemBlockAllocated = NULL;
+
+/* Actually allocate memory
+ */
+void *p_psAlloc(const char *file,
+                unsigned int lineno,
+                const char *func,
+                size_t size)
+{
+
+    psMemBlock *memBlock = malloc(sizeof(psMemBlock) + size + sizeof(void *));
+    if (memBlock == NULL) {
+	// this lock is only occasionally called in a given program (rarely fail malloc)
+        MUTEX_LOCK(&memBlockListMutex);
+        memBlock = memExhaustedCallback(size);
+        MUTEX_UNLOCK(&memBlockListMutex);
+        if (memBlock == NULL) {
+            PS_MEM_ABORT(__func__, "Failed to allocate %zd bytes at %s (%s:%d)", size, func, file, lineno);
+        }
+    }
+
+    // posts
+    *(psU32 *)&memBlock->startblock = P_PS_MEMMAGIC;
+    *(psU32 *)&memBlock->endblock   = P_PS_MEMMAGIC;
+    *(psU32 *)((char *) (memBlock + 1) + size) = P_PS_MEMMAGIC;
+
+    // size of memory allocated
+    memBlock->userMemorySize = size;
+
+    // alloc request by:
+    // thread
+    *(pthread_t *)&memBlock->tid = pthread_self();
+    // file
+    memBlock->file = file;
+    // line number
+    *(unsigned int *)&memBlock->lineno = (unsigned int)lineno;
+    // function
+    memBlock->func = func;
+
+    #if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
+    #define BACKTRACE_BUFFER_SIZE 32
+    // psMemBlock.func is a 'const char *', so basically we're going to abuse
+    // that and treat it as a void ** to carry around backtrace information.
+    // psMemBlock is not ifdef'd to make sure that psMemBlock is always the
+    // same size & layout reguardless of the pslib .so that's being linked
+    // against
+    void **bt = malloc(BACKTRACE_BUFFER_SIZE * sizeof(void *));
+    if (bt == NULL) {
+        PS_MEM_ABORT(__func__, "Failed to allocate memory for backtrace buffer: %zd bytes at %s (%s:%d)", 32 * sizeof(void *), func, file, lineno);
+    }
+    *(size_t *)&memBlock->backtraceSize = backtrace(bt, BACKTRACE_BUFFER_SIZE);
+    *(void ***)&memBlock->backtrace = bt;
+    #endif // ifdef HAVE_BACKTRACE
+
+    // free function
+    memBlock->freeFunc = NULL;
+
+    // persistent memory flag
+    memBlock->persistent = memory_is_persistent;
+
+    // this block will be add as the last mem block in the list
+    memBlock->previousBlock = NULL;
+
+    // ref count
+    memBlock->refCounter = 1;                   // one user so far
+
+    // 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
+    *(psMemId* )&memBlock->id = ++memid;
+
+    // insert the new block to the front of the memBlock linked-list
+    if (lastMemBlockAllocated != NULL) {
+	// exchange forward and backward references with the last allocated block
+	lastMemBlockAllocated->previousBlock = memBlock;
+	memBlock->nextBlock = lastMemBlockAllocated;
+    }
+    lastMemBlockAllocated = memBlock;
+
+    // Did the user ask to be informed about this allocation?
+    if (memBlock->id == p_psMemAllocID) {
+        // p_psMemAllocID can only be changed while the memBlockList mutex is
+        // held
+        p_psMemAllocID += memAllocCallback(memBlock);
+    }
+
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    // And return the user the memory that they allocated
+    return memBlock + 1;                        // user memory
+}
+
+/* internal routine to check the consistency of the allocated and/or free memory arena.
+ * this is used by the user functions below (psMem{Set,Get}Deallocator, 
  * N.b. If the block wasn't allocated by psAlloc, it will appear corrupted
  */
@@ -201,4 +486,5 @@
     //  users responsiblity) so all we're really risking is a garbage value.
 
+    //  XXX EAM : is this the error I'm catching in multithreaded processing?
     if (memBlock == memBlock->nextBlock) {
         if (!blockPrinted) {
@@ -225,302 +511,4 @@
     return bad;
 }
-
-
-/*
- * A callback function to check the state of the memory system; may be registered
- * with psMem{Alloc,Free}CallbackSet
- */
-static psMemId memAllocCallbackCheckCorruption(const psMemBlock *memBlock)
-{
-    static psMemId incr = 10; // "p_psMemAllocID += incr"
-
-    if (psMemCheckCorruption(stderr, false) > 0) {
-        fprintf(stderr, "Detected memory corruption\n"); // somewhere to set a breakpoint
-    }
-
-    return incr;
-}
-
-/*
- * The default callbacks
- */
-static psMemAllocCallback memAllocCallback = memAllocCallbackDefault;
-static psMemFreeCallback memFreeCallback = memFreeCallbackDefault;
-static psMemExhaustedCallback memExhaustedCallback = memExhaustedCallbackDefault;
-
-psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    psMemExhaustedCallback old = memExhaustedCallback;
-
-    if (func != NULL) {
-        memExhaustedCallback = func;
-    } else {
-        memExhaustedCallback = memExhaustedCallbackDefault;
-    }
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return old;
-}
-
-/*
- * And now the I-want-to-be-informed callbacks
- *
- * Call the callbacks when these IDs are allocated/freed
- */
-psMemId p_psMemAllocID = 0;       // notify user this block is allocated
-psMemId p_psMemFreeID = 0;   // notify user this block is freed
-
-psMemId psMemAllocCallbackSetID(psMemId id)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    psMemId old = p_psMemAllocID;
-
-    /*
-     * This is here purely to stop gcc complaining
-     */
-    assert (memAllocCallbackCheckCorruption != NULL);
-
-    p_psMemAllocID = id;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return old;
-}
-
-psMemId psMemFreeCallbackSetID(psMemId id)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    psMemId old = p_psMemFreeID;
-
-    p_psMemFreeID = id;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return old;
-}
-
-psMemAllocCallback psMemAllocCallbackSet(psMemAllocCallback func)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    psMemFreeCallback old = memAllocCallback;
-
-    if (func != NULL) {
-        memAllocCallback = func;
-    } else {
-        memAllocCallback = memAllocCallbackDefault;
-    }
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return old;
-}
-
-psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    psMemFreeCallback old = memFreeCallback;
-
-    if (func != NULL) {
-        memFreeCallback = func;
-    } else {
-        memFreeCallback = memFreeCallbackDefault;
-    }
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return old;
-}
-
-/*
- * Return memory ID counter for next block to be allocated
- */
-psMemId psMemGetId(void)
-{
-    return psMemGetLastId() + 1;
-}
-
-psMemId psMemGetLastId(void)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    psMemId id = memid;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return id;
-}
-
-int p_psMemCheckCorruption(const char *file,
-                           unsigned int lineno,
-                           const char *func,
-                           FILE *output,
-                           bool abort_on_error)
-{
-    // get exclusive access to the memBlock list to avoid it changing on us
-    // while we use it.
-    MUTEX_LOCK(&memBlockListMutex);
-
-    psS32 nbad = 0;               // number of bad blocks
-    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
-        if (isBadMemBlock(output, memBlock, __FILE__, __LINE__, __func__)) {
-            nbad++;
-
-            if (abort_on_error) {
-                // release the lock on the memblock list
-                MUTEX_UNLOCK(&memBlockListMutex);
-                PS_MEM_ABORT(__func__, "Detected memory corruption");
-            }
-        }
-    }
-
-    // release the lock on the memblock list
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return nbad;
-}
-
-bool p_psMemIsAlloced(const char *file,
-                      unsigned int lineno,
-                      const char *func,
-                      const void *ptr)
-{
-    // if ptr is a psAlloc()'d memory, find the actual address of the memBlock
-    psMemBlock *addr = ((psMemBlock *)ptr) - 1;
-
-    // get exclusive access to the memBlock list to avoid it changing on us
-    // while we use it.
-    MUTEX_LOCK(&memBlockListMutex);
-
-    // loop through the linked list of memBlocks looking for a matching pointer
-    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
-        if (memBlock == addr) {
-            // we found the memBlock
-            HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
-
-            MUTEX_UNLOCK(&memBlockListMutex);
-            return true;
-        }
-    }
-
-    // release the lock on the memblock list
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return false;
-}
-
-
-/*
- * Set whether allocated memory is persistent
- */
-bool p_psMemAllocatePersistent(bool is_persistent)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    const bool old = memory_is_persistent;
-    memory_is_persistent = is_persistent;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return old;
-}
-
-
-/*
- * Actually allocate memory
- */
-void *p_psAlloc(const char *file,
-                unsigned int lineno,
-                const char *func,
-                size_t size)
-{
-
-    psMemBlock *memBlock = malloc(sizeof(psMemBlock) + size + sizeof(void *));
-    if (memBlock == NULL) {
-        MUTEX_LOCK(&memBlockListMutex);
-        memBlock = memExhaustedCallback(size);
-        MUTEX_UNLOCK(&memBlockListMutex);
-        if (memBlock == NULL) {
-            PS_MEM_ABORT(__func__, "Failed to allocate %zd bytes at %s (%s:%d)", size, func, file, lineno);
-        }
-    }
-
-    // posts
-    *(psU32 *)&memBlock->startblock = P_PS_MEMMAGIC;
-    *(psU32 *)&memBlock->endblock   = P_PS_MEMMAGIC;
-    *(psU32 *)((char *) (memBlock + 1) + size) = P_PS_MEMMAGIC;
-
-    // size of memory allocated
-    memBlock->userMemorySize = size;
-
-    // alloc request by:
-    // thread
-    *(pthread_t *)&memBlock->tid = pthread_self();
-    // file
-    memBlock->file = file;
-    // line number
-    *(unsigned int *)&memBlock->lineno = (unsigned int)lineno;
-    // function
-    memBlock->func = func;
-
-    #if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE)
-    #define BACKTRACE_BUFFER_SIZE 32
-    // psMemBlock.func is a 'const char *', so basically we're going to abuse
-    // that and treat it as a void ** to carry around backtrace information.
-    // psMemBlock is not ifdef'd to make sure that psMemBlock is always the
-    // same size & layout reguardless of the pslib .so that's being linked
-    // against
-    void **bt = malloc(BACKTRACE_BUFFER_SIZE * sizeof(void *));
-    if (bt == NULL) {
-        PS_MEM_ABORT(__func__, "Failed to allocate memmory for backtrace buffer: %zd bytes at %s (%s:%d)", 32 * sizeof(void *), func, file, lineno);
-    }
-    *(size_t *)&memBlock->backtraceSize = backtrace(bt, BACKTRACE_BUFFER_SIZE);
-    *(void ***)&memBlock->backtrace = bt;
-    #endif // ifdef HAVE_BACKTRACE
-
-    // free function
-    memBlock->freeFunc = NULL;
-
-    // persistent memory flag
-    memBlock->persistent = memory_is_persistent;
-
-    // this block will be add as the last mem block in the list
-    memBlock->previousBlock = NULL;
-
-    // ref count
-    memBlock->refCounter = 1;                   // one user so far
-
-    // need exclusive access of the memory block list now...
-    MUTEX_LOCK(&memBlockListMutex);
-
-    // increment the memory id only after we've grabbed the memBlockListMutex
-    *(psMemId* )&memBlock->id = ++memid;
-
-    // insert the new block to the front of the memBlock linked-list
-    memBlock->nextBlock = lastMemBlockAllocated;
-    if (memBlock->nextBlock != NULL) {
-        memBlock->nextBlock->previousBlock = memBlock;
-    }
-    lastMemBlockAllocated = memBlock;
-
-    // Did the user ask to be informed about this allocation?
-    if (memBlock->id == p_psMemAllocID) {
-        // p_psMemAllocID can only be changed while the memBlockList mutex is
-        // held
-        p_psMemAllocID += memAllocCallback(memBlock);
-    }
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    // And return the user the memory that they allocated
-    return memBlock + 1;                        // user memory
-}
-
 
 void *p_psRealloc(const char *file,
@@ -545,4 +533,12 @@
     // Reallocate the memory
 
+    // we need to lock this whole section because we need to be able to adjust
+    // lastMemBlockAllocated if needed (ie, this IS lastMemBlockAllocated)
+    // this lock is frequently called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+    
+    psMemBlock *nextBlock = memBlock->nextBlock;
+    psMemBlock *previousBlock = memBlock->previousBlock;
+
     // Is this the last block we allocated?  If it is, we need to keep track of
     // this fact and update lastMemBlockAllocated *after* the realloc or
@@ -551,9 +547,7 @@
 
     memBlock = (psMemBlock *)realloc(memBlock, sizeof(psMemBlock) + size + sizeof(void *));
-    if (memBlock== NULL) {
-        MUTEX_LOCK(&memBlockListMutex);
+    if (memBlock == NULL) {
         memBlock = memExhaustedCallback(size);
         if (memBlock == NULL) {
-            MUTEX_UNLOCK(&memBlockListMutex);
             psMemBlockPrint(stderr,  ((psMemBlock *)ptr) - 1);
             fprintf(stderr, "Problem reallocating block\n");
@@ -562,26 +556,20 @@
     }
 
-    // realloc() successed so need to update lastMemBlockAllocated if we just
-    // mucked with the last mem block
+    memBlock->userMemorySize = size;
+    *(psU32 *)((char *)(memBlock + 1) + size) = P_PS_MEMMAGIC;
+
+    // update the references on the list:
+
+    // is we are modifying the last mem block, we need to update lastMemBlockAllocated
     if (isBlockLast) {
         lastMemBlockAllocated = memBlock;
     }
 
-    memBlock->userMemorySize = size;
-    *(psU32 *)((char *)(memBlock + 1) + size) = P_PS_MEMMAGIC;
-
-    MUTEX_LOCK(&memBlockListMutex);
-
-    // Is this the last block we allocated?
-    if (memBlock== lastMemBlockAllocated) {
-        lastMemBlockAllocated = memBlock;
-    }
-
     // the block location may have changed, so fix the linked list addresses.
-    if (memBlock->nextBlock != NULL) {
-        memBlock->nextBlock->previousBlock = memBlock;
-    }
-    if (memBlock->previousBlock != NULL) {
-        memBlock->previousBlock->nextBlock = memBlock;
+    if (nextBlock != NULL) {
+        nextBlock->previousBlock = memBlock;
+    }
+    if (previousBlock != NULL) {
+        previousBlock->nextBlock = memBlock;
     }
 
@@ -615,4 +603,5 @@
     p_psMemCheckCorruption(file, lineno, func, fd, true);
 
+    // this lock is rarely called in a given program
     MUTEX_LOCK(&memBlockListMutex);
 
@@ -690,4 +679,5 @@
     *array = psAlloc(nleak * sizeof(psMemBlock));
 
+    // this lock is rarely called in a given program
     MUTEX_LOCK(&memBlockListMutex);
 
@@ -744,12 +734,18 @@
     HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
 
+    // XXX we probably need a MUTEX_LOCK here: otherwise, two functions can race on recCounter++ and refCounter--;
     memBlock->refCounter++;
 
     // Did the user ask to be informed about this allocation?
-    MUTEX_LOCK(&memBlockListMutex);
+    // this lock is frequently called in a given program
+    // this lock is probably not needed: if someone changes the callback ID in a different thread,
+    // do we really care about really rarely getting this wrong?
+    // MUTEX_LOCK(&memBlockListMutex);
     if (memBlock->id == p_psMemAllocID) {
-        p_psMemAllocID += memAllocCallback(memBlock);
-    }
-    MUTEX_UNLOCK(&memBlockListMutex);
+	// XXX this is seriously bogus: why are we changing this value??
+        // p_psMemAllocID += memAllocCallback(memBlock);
+        memAllocCallback(memBlock);
+    }
+    // MUTEX_UNLOCK(&memBlockListMutex);
 
     return ptr;
@@ -806,14 +802,16 @@
 
     // if we have multiple references, just decrement the count and return.
+    // XXX we probably need a MUTEX_LOCK here: otherwise, two functions can race on refCounter--;
     if (memBlock->refCounter > 1) {
         memBlock->refCounter--;
 
         // Did the user ask to be informed about this deallocation?
-        MUTEX_LOCK(&memBlockListMutex);
+        // MUTEX_LOCK(&memBlockListMutex);
         if (memBlock->id == p_psMemFreeID) {
-            p_psMemFreeID += memFreeCallback(memBlock);
-        }
-        MUTEX_UNLOCK(&memBlockListMutex);
-
+	    // XXX why modify this?  this seems bogus
+            // p_psMemFreeID += memFreeCallback(memBlock);
+            memFreeCallback(memBlock);
+        }
+        // MUTEX_UNLOCK(&memBlockListMutex);
         return ptr;
     }
@@ -825,21 +823,30 @@
     }
 
+    // this lock is frequently set in a given program
     MUTEX_LOCK(&memBlockListMutex);
 
     // Did the user ask to be informed about this deallocation?
     if (memBlock->id == p_psMemFreeID) {
-        p_psMemFreeID += memFreeCallback(ptr);
-    }
+        // p_psMemFreeID += memFreeCallback(ptr);
+        memFreeCallback(ptr);
+    }
+
+    psMemBlock *nextBlock = memBlock->nextBlock;
+    psMemBlock *previousBlock = memBlock->previousBlock;
 
     // cut the memBlock out of the memBlock list
-    if (memBlock->nextBlock != NULL) {
-        memBlock->nextBlock->previousBlock = memBlock->previousBlock;
-    }
-    if (memBlock->previousBlock != NULL) {
-        memBlock->previousBlock->nextBlock = memBlock->nextBlock;
+    if (nextBlock != NULL) {
+        nextBlock->previousBlock = previousBlock;
+    }
+    if (previousBlock != NULL) {
+        previousBlock->nextBlock = nextBlock;
     }
     if (lastMemBlockAllocated == memBlock) {
-        lastMemBlockAllocated = memBlock->nextBlock;
-    }
+        lastMemBlockAllocated = nextBlock;
+    }
+
+    // NULL out the refs so no one can get confused
+    memBlock->nextBlock = NULL;
+    memBlock->previousBlock = NULL;
 
     MUTEX_UNLOCK(&memBlockListMutex);
@@ -860,4 +867,5 @@
 
 
+/**** user functions to manage memory references ****/
 void p_psMemSetDeallocator(const char *file,
                            unsigned int lineno,
@@ -895,29 +903,4 @@
 
 
-bool psMemSetThreadSafety(bool safe)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    bool oldState = safeThreads;
-    safeThreads = safe;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return oldState;
-}
-
-
-bool psMemGetThreadSafety(void)
-{
-    MUTEX_LOCK(&memBlockListMutex);
-
-    bool oldState = safeThreads;
-
-    MUTEX_UNLOCK(&memBlockListMutex);
-
-    return oldState;
-}
-
-
 bool p_psMemGetPersistent(const char *file,
                           unsigned int lineno,
@@ -954,6 +937,66 @@
 }
 
-
-/******************************************************************************/
+int p_psMemCheckCorruption(const char *file,
+                           unsigned int lineno,
+                           const char *func,
+                           FILE *output,
+                           bool abort_on_error)
+{
+    // get exclusive access to the memBlock list to avoid it changing on us while we check it.
+
+    // this lock is rarely called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    psS32 nbad = 0;               // number of bad blocks
+    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
+        if (isBadMemBlock(output, memBlock, __FILE__, __LINE__, __func__)) {
+            nbad++;
+
+            if (abort_on_error) {
+                // release the lock on the memblock list
+                MUTEX_UNLOCK(&memBlockListMutex);
+                PS_MEM_ABORT(__func__, "Detected memory corruption");
+            }
+        }
+    }
+
+    // release the lock on the memblock list
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return nbad;
+}
+
+bool p_psMemIsAlloced(const char *file,
+                      unsigned int lineno,
+                      const char *func,
+                      const void *ptr)
+{
+    // if ptr is a psAlloc()'d memory, find the actual address of the memBlock
+    psMemBlock *addr = ((psMemBlock *)ptr) - 1;
+
+    // get exclusive access to the memBlock list to avoid it changing on us
+    // this lock is only occasionally called in a given program
+    MUTEX_LOCK(&memBlockListMutex);
+
+    // loop through the linked list of memBlocks looking for a matching pointer
+    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
+        if (memBlock == addr) {
+            // we found the memBlock
+            HANDLE_BAD_BLOCK(memBlock, file, lineno, func);
+
+            MUTEX_UNLOCK(&memBlockListMutex);
+            return true;
+        }
+    }
+
+    // release the lock on the memblock list
+    MUTEX_UNLOCK(&memBlockListMutex);
+
+    return false;
+}
+
+
+/**** memory usage statistics functions ****/
+
 /*
  * Return the total amount of memory owned by psLib; if non-NULL also provide a
@@ -973,4 +1016,5 @@
     }
 
+    // this lock is rarely called in a given program
     MUTEX_LOCK(&memBlockListMutex);
     /*
