Bug 58904: Create strong types for synchronization primitives. r=bsmedberg.
authorChris Jones <jones.chris.g@gmail.com>
Sat, 18 Apr 2009 18:54:23 -0700
changeset 27510 58c8f67ce6f6a25814a96f9889210d03a24a5ca7
parent 27509 1b7ba90547692df0827e508b9b494adf9c605557
child 27511 62f1e1dfb3eebe92d10302a532fa7520f05d81fa
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs58904
milestone1.9.2a1pre
Bug 58904: Create strong types for synchronization primitives. r=bsmedberg.
intl/strres/src/nsStringBundle.cpp
intl/strres/src/nsStringBundle.h
xpcom/base/nsDebugImpl.cpp
xpcom/glue/BlockingResourceBase.cpp
xpcom/glue/BlockingResourceBase.h
xpcom/glue/CondVar.h
xpcom/glue/Makefile.in
xpcom/glue/Monitor.h
xpcom/glue/Mutex.h
xpcom/glue/nsDebug.h
xpcom/glue/objs.mk
xpcom/tests/Makefile.in
xpcom/tests/TestPipes.cpp
xpcom/tests/TestSynchronization.cpp
--- a/intl/strres/src/nsStringBundle.cpp
+++ b/intl/strres/src/nsStringBundle.cpp
@@ -83,16 +83,17 @@ static NS_DEFINE_CID(kPersistentProperti
 nsStringBundle::~nsStringBundle()
 {
 }
 
 nsStringBundle::nsStringBundle(const char* aURLSpec,
                                nsIStringBundleOverride* aOverrideStrings) :
   mPropertiesURL(aURLSpec),
   mOverrideStrings(aOverrideStrings),
+  mMonitor(0),
   mAttemptedLoad(PR_FALSE),
   mLoaded(PR_FALSE)
 {
 }
 
 nsresult
 nsStringBundle::LoadProperties()
 {
@@ -106,16 +107,20 @@ nsStringBundle::LoadProperties()
       
     return NS_ERROR_UNEXPECTED;
   }
   
   mAttemptedLoad = PR_TRUE;
 
   nsresult rv;
 
+  mMonitor = nsAutoMonitor::NewMonitor("StringBundle monitor");
+  if (!mMonitor)
+    return NS_ERROR_OUT_OF_MEMORY;
+
   // do it synchronously
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), mPropertiesURL);
   if (NS_FAILED(rv)) return rv;
 
   // We don't use NS_OpenURI because we want to tweak the channel
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel), uri);
@@ -143,17 +148,17 @@ nsStringBundle::LoadProperties()
   
   return rv;
 }
 
 
 nsresult
 nsStringBundle::GetStringFromID(PRInt32 aID, nsAString& aResult)
 {  
-  nsAutoCMonitor(this);
+  nsAutoMonitor automon(mMonitor);
   nsCAutoString name;
   name.AppendInt(aID, 10);
 
   nsresult rv;
   
   // try override first
   if (mOverrideStrings) {
     rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
@@ -264,17 +269,17 @@ nsStringBundle::GetStringFromName(const 
 {
   NS_ENSURE_ARG_POINTER(aName);
   NS_ENSURE_ARG_POINTER(aResult);
 
   nsresult rv;
   rv = LoadProperties();
   if (NS_FAILED(rv)) return rv;
 
-  nsAutoCMonitor(this);
+  nsAutoMonitor automon(mMonitor);
   *aResult = nsnull;
   nsAutoString tmpstr;
   rv = GetStringFromName(nsDependentString(aName), tmpstr);
   if (NS_FAILED(rv))
   {
 #if 0
     // it is not uncommon for apps to request a string name which may not exist
     // so be quiet about it. 
--- a/intl/strres/src/nsStringBundle.h
+++ b/intl/strres/src/nsStringBundle.h
@@ -39,16 +39,17 @@
 #define nsStringBundle_h__
 
 #include "nsIStringBundle.h"
 #include "nsCOMPtr.h"
 #include "nsIPersistentProperties2.h"
 #include "nsString.h"
 #include "nsCOMArray.h"
 #include "nsIStringBundleOverride.h"
+#include "nsAutoLock.h"
 
 class nsStringBundle : public nsIStringBundle
 {
 public:
     // init version
     nsStringBundle(const char* aURLSpec, nsIStringBundleOverride*);
     nsresult LoadProperties();
     virtual ~nsStringBundle();
@@ -65,16 +66,17 @@ protected:
     nsresult GetStringFromID(PRInt32 aID, nsAString& aResult);
     nsresult GetStringFromName(const nsAString& aName, nsAString& aResult);
 
     nsresult GetCombinedEnumeration(nsIStringBundleOverride* aOverrideString,
                                     nsISimpleEnumerator** aResult);
 private:
     nsCString              mPropertiesURL;
     nsCOMPtr<nsIStringBundleOverride> mOverrideStrings;
+    PRMonitor*                   mMonitor;
     PRPackedBool                 mAttemptedLoad;
     PRPackedBool                 mLoaded;
     
 public:
     static nsresult FormatString(const PRUnichar *formatStr,
                                  const PRUnichar **aParams, PRUint32 aLength,
                                  PRUnichar **aResult);
 };
--- a/xpcom/base/nsDebugImpl.cpp
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -373,16 +373,24 @@ Abort(const char *aMsg)
 #ifndef DEBUG_cls
 	DEBUGGER(aMsg);
 #endif
   }
 #else
   // Don't know how to abort on this platform! call Break() instead
   Break(aMsg);
 #endif
+
+  // Still haven't aborted?  Try dereferencing null.
+  // (Written this way to lessen the likelihood of it being optimized away.)
+  gAssertionCount += *((PRInt32 *) 0); // TODO annotation saying we know 
+                                       // this is crazy
+
+  // Still haven't aborted?  Try _exit().
+  PR_ProcessExit(127);
 }
 
 // Abort() calls this function, don't call it!
 static void
 Break(const char *aMsg)
 {
 #if defined(_WIN32)
 #ifndef WINCE // we really just want to crash for now
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/BlockingResourceBase.cpp
@@ -0,0 +1,566 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifdef DEBUG
+
+#include "plhash.h"
+#include "prprf.h"
+#include "prlock.h"
+#include "prthread.h"
+#include "nsDebug.h"
+#include "nsVoidArray.h"
+
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Mutex.h"
+
+#ifdef NS_TRACE_MALLOC
+# include <stdio.h>
+# include "nsTraceMalloc.h"
+#endif
+
+static PRUintn      ResourceAcqnChainFrontTPI = (PRUintn)-1;
+static PLHashTable* OrderTable = 0;
+static PRLock*      OrderTableLock = 0;
+static PRLock*      ResourceMutex = 0;
+
+// Needs to be kept in sync with BlockingResourceType. */
+static char const *const kResourceTypeNames[] = {
+    "Mutex", "Monitor", "CondVar"
+};
+
+struct nsNamedVector : public nsVoidArray {
+    const char* mName;
+
+#ifdef NS_TRACE_MALLOC
+    // Callsites for the inner locks/monitors stored in our base nsVoidArray.
+    // This array parallels our base nsVoidArray.
+    nsVoidArray mInnerSites;
+#endif
+
+    nsNamedVector(const char* name = 0, PRUint32 initialSize = 0)
+        : nsVoidArray(initialSize),
+          mName(name)
+    {
+    }
+};
+
+static void *
+_hash_alloc_table(void *pool, PRSize size)
+{
+    return operator new(size);
+}
+
+static void 
+_hash_free_table(void *pool, void *item)
+{
+    operator delete(item);
+}
+
+static PLHashEntry *
+_hash_alloc_entry(void *pool, const void *key)
+{
+    return new PLHashEntry;
+}
+
+/*
+ * Because monitors and locks may be associated with an mozilla::BlockingResourceBase,
+ * without having had their associated nsNamedVector created explicitly in
+ * nsAutoMonitor::NewMonitor/DeleteMonitor, we need to provide a freeEntry
+ * PLHashTable hook, to avoid leaking nsNamedVectors which are replaced by
+ * nsAutoMonitor::NewMonitor.
+ *
+ * There is still a problem with the OrderTable containing orphaned
+ * nsNamedVector entries, for manually created locks wrapped by nsAutoLocks.
+ * (there should be no manually created monitors wrapped by nsAutoMonitors:
+ * you should use nsAutoMonitor::NewMonitor and nsAutoMonitor::DestroyMonitor
+ * instead of PR_NewMonitor and PR_DestroyMonitor).  These lock vectors don't
+ * strictly leak, as they are killed on shutdown, but there are unnecessary
+ * named vectors in the hash table that outlive their associated locks.
+ *
+ * XXX so we should have nsLock, nsMonitor, etc. and strongly type their
+ * XXX nsAutoXXX counterparts to take only the non-auto types as inputs
+ */
+static void 
+_hash_free_entry(void *pool, PLHashEntry *entry, PRUintn flag)
+{
+    nsNamedVector* vec = (nsNamedVector*) entry->value;
+    if (vec) {
+        entry->value = 0;
+        delete vec;
+    }
+    if (flag == HT_FREE_ENTRY)
+        delete entry;
+}
+
+static const PLHashAllocOps _hash_alloc_ops = {
+    _hash_alloc_table, _hash_free_table,
+    _hash_alloc_entry, _hash_free_entry
+};
+
+static PRIntn
+_purge_one(PLHashEntry* he, PRIntn cnt, void* arg)
+{
+    nsNamedVector* vec = (nsNamedVector*) he->value;
+
+    if (he->key == arg)
+        return HT_ENUMERATE_REMOVE;
+    vec->RemoveElement(arg);
+    return HT_ENUMERATE_NEXT;
+}
+
+static void
+OnResourceRecycle(void* aResource)
+{
+    NS_PRECONDITION(OrderTable, "should be inited!");
+
+    PR_Lock(OrderTableLock);
+    PL_HashTableEnumerateEntries(OrderTable, _purge_one, aResource);
+    PR_Unlock(OrderTableLock);
+}
+
+static PLHashNumber
+_hash_pointer(const void* key)
+{
+    return PLHashNumber(NS_PTR_TO_INT32(key)) >> 2;
+}
+
+// TODO just included for function below
+#include "prcmon.h"
+
+// Must be single-threaded here, early in primordial thread.
+static void InitAutoLockStatics()
+{
+    (void) PR_NewThreadPrivateIndex(&ResourceAcqnChainFrontTPI, 0);
+    OrderTable = PL_NewHashTable(64, _hash_pointer,
+                                 PL_CompareValues, PL_CompareValues,
+                                 &_hash_alloc_ops, 0);
+    if (OrderTable && !(OrderTableLock = PR_NewLock())) {
+        PL_HashTableDestroy(OrderTable);
+        OrderTable = 0;
+    }
+
+    if (OrderTable && !(ResourceMutex = PR_NewLock())) {
+        PL_HashTableDestroy(OrderTable);
+        OrderTable = 0;
+    }
+
+    // TODO unnecessary after API changes
+    PR_CSetOnMonitorRecycle(OnResourceRecycle);
+}
+
+/* TODO re-enable this after API change.  with backwards compatibility
+ enabled, it conflicts with the impl in nsAutoLock.cpp.  Not freeing
+ this stuff will "leak" memory that is cleanup up when the process
+ exits. */
+#if 0
+void _FreeAutoLockStatics()
+{
+    PLHashTable* table = OrderTable;
+    if (!table) return;
+
+    // Called at shutdown, so we don't need to lock.
+    // TODO unnecessary after API changes
+    PR_CSetOnMonitorRecycle(0);
+    PR_DestroyLock(OrderTableLock);
+    OrderTableLock = 0;
+    PL_HashTableDestroy(table);
+    OrderTable = 0;
+}
+#endif
+
+
+static nsNamedVector* GetVector(PLHashTable* table, const void* key)
+{
+    PLHashNumber hash = _hash_pointer(key);
+    PLHashEntry** hep = PL_HashTableRawLookup(table, hash, key);
+    PLHashEntry* he = *hep;
+    if (he)
+        return (nsNamedVector*) he->value;
+    nsNamedVector* vec = new nsNamedVector();
+    if (vec)
+        PL_HashTableRawAdd(table, hep, hash, key, vec);
+    return vec;
+}
+
+static void OnResourceCreated(const void* key, const char* name )
+{
+    NS_PRECONDITION(key && OrderTable, "should be inited!");
+
+    nsNamedVector* value = new nsNamedVector(name);
+    if (value) {
+        PR_Lock(OrderTableLock);
+        PL_HashTableAdd(OrderTable, key, value);
+        PR_Unlock(OrderTableLock);
+    }
+}
+
+// We maintain an acyclic graph in OrderTable, so recursion can't diverge.
+static PRBool Reachable(PLHashTable* table, const void* goal, const void* start)
+{
+    PR_ASSERT(goal);
+    PR_ASSERT(start);
+    nsNamedVector* vec = GetVector(table, start);
+    for (PRUint32 i = 0, n = vec->Count(); i < n; i++) {
+        void* aResource = vec->ElementAt(i);
+        if (aResource == goal || Reachable(table, goal, aResource))
+            return PR_TRUE;
+    }
+    return PR_FALSE;
+}
+
+static PRBool WellOrdered(const void* aResource1, const void* aResource2,
+                          const void *aCallsite2, PRUint32* aIndex2p,
+                          nsNamedVector** aVec1p, nsNamedVector** aVec2p)
+{
+    NS_ASSERTION(OrderTable && OrderTableLock, "supposed to be initialized!");
+
+    PRBool rv = PR_TRUE;
+    PLHashTable* table = OrderTable;
+
+    PR_Lock(OrderTableLock);
+
+    // Check whether we've already asserted (addr1 < addr2).
+    nsNamedVector* vec1 = GetVector(table, aResource1);
+    if (vec1) {
+        PRUint32 i, n;
+
+        for (i = 0, n = vec1->Count(); i < n; i++)
+            if (vec1->ElementAt(i) == aResource2)
+                break;
+
+        if (i == n) {
+            // Now check for (addr2 < addr1) and return false if so.
+            nsNamedVector* vec2 = GetVector(table, aResource2);
+            if (vec2) {
+                for (i = 0, n = vec2->Count(); i < n; i++) {
+                    void* aResourcei = vec2->ElementAt(i);
+                    PR_ASSERT(aResourcei);
+                    if (aResourcei == aResource1 
+                        || Reachable(table, aResource1, aResourcei)) {
+                        *aIndex2p = i;
+                        *aVec1p = vec1;
+                        *aVec2p = vec2;
+                        rv = PR_FALSE;
+                        break;
+                    }
+                }
+
+                if (rv) {
+                    // Insert (addr1 < addr2) into the order table.
+                    // XXX fix plvector/nsVector to use const void*
+                    vec1->AppendElement((void*) aResource2);
+#ifdef NS_TRACE_MALLOC
+                    vec1->mInnerSites.AppendElement((void*) aCallsite2);
+#endif
+                }
+            }
+        }
+    }
+
+    // all control flow must pass through this point
+//unlock:
+    PR_Unlock(OrderTableLock);
+    return rv;
+}
+
+//
+// BlockingResourceBase implementation
+//
+// Note that in static/member functions, user code abiding by the 
+// BlockingResourceBase API's contract gives us the following guarantees:
+//
+//  - mResource is not NULL
+//  - mResource points to a valid underlying resource
+//  - mResource is NOT shared with any other mozilla::BlockingResourceBase
+//
+// If user code violates the API contract, the behavior of the following
+// functions is undefined.  So no error checking is strictly necessary.
+//
+// That said, assertions of the the above facts are sprinkled throughout the
+// following code, to check for errors in user code.
+//
+namespace mozilla {
+
+
+void
+BlockingResourceBase::Init(void* aResource, 
+                           const char* aName, 
+                           BlockingResourceBase::BlockingResourceType aType)
+{
+    if (NS_UNLIKELY(ResourceAcqnChainFrontTPI == PRUintn(-1)))
+        InitAutoLockStatics();
+
+    NS_PRECONDITION(aResource, "null resource");
+
+    OnResourceCreated(aResource, aName);
+    mResource = aResource;
+    mChainPrev = 0;
+    mType = aType;
+}
+
+BlockingResourceBase::~BlockingResourceBase()
+{
+    NS_PRECONDITION(mResource, "bad subclass impl, or double free");
+
+    // we don't expect to find this resouce in the acquisition chain.
+    // it should have been Release()'n as many times as it has been 
+    // Acquire()ed, before this destructor was called.
+    // additionally, WE are the only ones who can create underlying 
+    // resources, so there should be one underlying instance per 
+    // BlockingResourceBase.  thus we don't need to do any cleanup unless 
+    // client code has done something seriously and obviously wrong.
+    // that, plus the potential performance impact of full cleanup, mean
+    // that it has been removed for now.
+
+    OnResourceRecycle(mResource);
+    mResource = 0;
+    mChainPrev = 0;
+}
+
+void BlockingResourceBase::Acquire()
+{
+    NS_PRECONDITION(mResource, "bad base class impl or double free");
+    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ResourceMutex);
+
+    if (mType == eCondVar) {
+        NS_NOTYETIMPLEMENTED("TODO: figure out how to Acquire() condvars");
+        return;
+    }
+
+    BlockingResourceBase* chainFront = 
+        (BlockingResourceBase*) 
+            PR_GetThreadPrivate(ResourceAcqnChainFrontTPI);
+
+    if (eMonitor == mType) {
+        // reentering a monitor: the old implementation ensured that this
+        // was only done immediately after a previous entry.  little
+        // tricky here since we can't rely on the monitor already having
+        // been entered, as AutoMonitor used to let us do (sort of)
+
+        if (this == chainFront) {
+            // acceptable reentry, and nothing to update.
+            return;
+        } 
+        else if (chainFront) {
+            BlockingResourceBase* br = chainFront->mChainPrev;
+            while (br) {
+                if (br == this) {
+                    NS_ASSERTION(br != this, 
+                                 "reentered monitor after acquiring "
+                                 "other resources");
+                    // have to ignore this allocation and return.  even if
+                    // we cleaned up the old acquisition of the monitor and
+                    // put the new one at the front of the chain, we'd
+                    // almost certainly set off the deadlock detector.
+                    // this error is interesting enough.
+                    return;
+                }
+                br = br->mChainPrev;
+            }
+        }
+    }
+
+    const void* callContext =
+#ifdef NS_TRACE_MALLOC
+        (const void*)NS_TraceMallocGetStackTrace();
+#else
+        nsnull
+#endif
+        ;
+
+    if (eMutex == mType
+        && chainFront == this
+        && !(chainFront->mChainPrev)) {
+        // corner case: acquire only a single lock, then try to reacquire 
+        // the lock.  there's no entry in the order table for the first
+        // acquisition, so we might not detect this (immediate and real!) 
+        // deadlock.
+        //
+        // XXX need to remove this corner case, and get a stack trace for 
+        // first acquisition, and get the lock's name
+        char buf[128];
+        PR_snprintf(buf, sizeof buf,
+                    "Imminent deadlock between Mutex@%p and itself!",
+                    chainFront->mResource);
+
+#ifdef NS_TRACE_MALLOC
+        fputs("\nDeadlock will occur here:\n", stderr);
+        NS_TraceMallocPrintStackTrace(
+            stderr, (nsTMStackTraceIDStruct*)callContext);
+        putc('\n', stderr);
+#endif
+
+        NS_ERROR(buf);
+
+        return;                 // what else to do? we're screwed
+    }
+
+    nsNamedVector* vec1;
+    nsNamedVector* vec2;
+    PRUint32 i2;
+
+    if (!chainFront 
+        || WellOrdered(chainFront->mResource, mResource, 
+                       callContext, 
+                       &i2, 
+                       &vec1, &vec2)) {
+        mChainPrev = chainFront;
+        PR_SetThreadPrivate(ResourceAcqnChainFrontTPI, this);
+    }
+    else {
+        char buf[128];
+        PR_snprintf(buf, sizeof buf,
+                    "Potential deadlock between %s%s@%p and %s%s@%p",
+                    vec1->mName ? vec1->mName : "",
+                    kResourceTypeNames[chainFront->mType],
+                    chainFront->mResource,
+                    vec2->mName ? vec2->mName : "",
+                    kResourceTypeNames[mType],
+                    mResource);
+
+#ifdef NS_TRACE_MALLOC
+        fprintf(stderr, "\n*** %s\n\nStack of current acquisition:\n", buf);
+        NS_TraceMallocPrintStackTrace(
+            stderr, NS_TraceMallocGetStackTrace());
+
+        fputs("\nStack of conflicting acquisition:\n", stderr);
+        NS_TraceMallocPrintStackTrace(
+            stderr, (nsTMStackTraceIDStruct *)vec2->mInnerSites.ElementAt(i2));
+        putc('\n', stderr);
+#endif
+
+        NS_ERROR(buf);
+
+        // because we know the latest acquisition set off the deadlock,
+        // detector, it's debatable whether we should append it to the 
+        // acquisition chain; it might just trigger later errors that 
+        // have already been reported here.  I choose not too add it.
+    }
+}
+
+void BlockingResourceBase::Release()
+{
+    NS_PRECONDITION(mResource, "bad base class impl or double free");
+    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ResourceMutex);
+
+    if (mType == eCondVar) {
+        NS_NOTYETIMPLEMENTED("TODO: figure out how to Release() condvars");
+        return;
+    }
+
+    BlockingResourceBase* chainFront = 
+        (BlockingResourceBase*) 
+            PR_GetThreadPrivate(ResourceAcqnChainFrontTPI);
+    NS_ASSERTION(chainFront, 
+                 "Release()ing something that hasn't been Acquire()ed");
+
+    if (NS_LIKELY(chainFront == this)) {
+        PR_SetThreadPrivate(ResourceAcqnChainFrontTPI, mChainPrev);
+    }
+    else {
+        // not an error, but makes code hard to reason about.
+        NS_WARNING("you're releasing a resource in non-LIFO order; why?");
+
+        // we walk backwards in order of acquisition:
+        //  (1)  ...node<-prev<-curr...
+        //              /     /
+        //  (2)  ...prev<-curr...
+        BlockingResourceBase* curr = chainFront;
+        BlockingResourceBase* prev = nsnull;
+        while (curr && (prev = curr->mChainPrev) && (prev != this))
+            curr = prev;
+        if (prev == this)
+            curr->mChainPrev = prev->mChainPrev;
+    }
+}
+
+//
+// Debug implementation of Mutex
+void
+Mutex::Lock()
+{
+    PR_Lock(ResourceMutex);
+    Acquire();
+    PR_Unlock(ResourceMutex);
+
+    PR_Lock(mLock);
+}
+
+void
+Mutex::Unlock()
+{
+    PRStatus status = PR_Unlock(mLock);
+    NS_ASSERTION(PR_SUCCESS == status, "problem Unlock()ing");
+
+    PR_Lock(ResourceMutex);
+    Release();
+    PR_Unlock(ResourceMutex);
+}
+
+//
+// Debug implementation of Monitor
+void 
+Monitor::Enter() 
+{
+    PR_Lock(ResourceMutex);
+    ++mEntryCount;
+    Acquire();
+    PR_Unlock(ResourceMutex);
+
+    PR_EnterMonitor(mMonitor);
+}
+
+void
+Monitor::Exit()
+{
+    PRStatus status = PR_ExitMonitor(mMonitor);
+    NS_ASSERTION(PR_SUCCESS == status, "bad ExitMonitor()");
+
+    PR_Lock(ResourceMutex);
+    if (--mEntryCount == 0)
+        Release();
+    PR_Unlock(ResourceMutex);
+}
+
+
+} // namespace mozilla
+
+
+#endif // ifdef DEBUG
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/BlockingResourceBase.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#ifndef mozilla_BlockingResourceBase_h
+#define mozilla_BlockingResourceBase_h
+
+#include "nscore.h"
+#include "prlog.h"
+#include "nsError.h"
+#include "nsDebug.h"
+
+//
+// This header is not meant to be included by client code.
+//
+
+namespace mozilla {
+
+
+/**
+ * Base class of resources that might block clients trying to acquire them.  
+ * Aids in debugging, deadlock detection, etc.
+ **/
+class NS_COM_GLUE BlockingResourceBase
+{
+protected:
+    BlockingResourceBase() {
+    }
+    // Needs to be kept in sync with kResourceTypeNames.
+    enum BlockingResourceType { eMutex, eMonitor, eCondVar };
+
+#ifdef DEBUG
+    ~BlockingResourceBase();
+
+    /**
+     * Initialize this blocking resource.  Also hooks the resource into
+     * instrumentation code.
+     * @param aResource Unique handle for the underlying blocking resource.
+     * @param aName A meaningful, unique name that can be used in
+     *              error messages, et al.
+     * @param aType The specific type of |aResource|, if any.
+     **/
+    void Init(void* aResource, 
+              const char* aName,
+              BlockingResourceType aType);
+
+    /*
+     * The following variables and methods are used by the deadlock detector.
+     * To understand how they're used, it's best to give a quick overview
+     * of how the deadlock detector works.
+     *
+     * The deadlock detector ensures that all blocking resources are
+     * acquired according to a partial order P.  One type of blocking
+     * resource is a lock.  If a lock l1 is acquired (locked) before l2, 
+     * then we say that $l1 <_P l2$.  The detector flags an error if two
+     * locks l1 and l2 have an inconsistent ordering in P; that is, if both
+     * $l1 <_P l2$ and $l2 <_P l1$.  This is a potential error because a
+     * thread acquiring l1,l2 according to the first order might simultaneous
+     * run with a thread acquiring them according to the second order.
+     * If this happens under the right conditions, then the
+     * acquisitions will deadlock.
+     *
+     * This deadlock detector doesn't know at compile-time what P is.  So, 
+     * it tries to discover the order at run time.  More precisely, it 
+     * finds <i>some</i> order P, then tries to find chains of resource
+     * acquisitions that violate P.  An example acquisition sequence, and
+     * the orders they impose, is the following:
+     *   l1.lock()   // current chain: [ l1 ]
+     *               // order: { }
+     *
+     *   l2.lock()   // current chain: [ l1, l2 ]
+     *               // order: { l1 <_P l2 }
+     *
+     *   l3.lock()   // current chain: [ l1, l2, l3 ]
+     *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
+     *               // (note: <_P is transitive, so also $l1 <_P l3$)
+     *
+     *   l2.unlock() // current chain: [ l1, l3 ]
+     *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
+     *               // (note: it's OK, but weird, that l2 was unlocked out
+     *               //  of order.  we still have l1 <_P l3).
+     *
+     *   l2.lock()   // current chain: [ l1, l3, l2 ]
+     *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3,
+     *                                      l3 <_P l2 (!!!) }
+     * BEEP BEEP!  Here the detector will flag a potential error, since 
+     * l2 and l3 were used inconsistently (and potentially deadlocking-
+     * inducingly).
+     *
+     * In reality, this is somewhat more complicated because each thread
+     * has its own acquisition chain.  In addition, the example above 
+     * elides several important details from the detector implementation.
+     */
+
+    /**
+     * mResource
+     * Some handle to a resource that may block acquisition.  Used to 
+     * identify the resource in acquisition chains.
+     **/
+    void* mResource;
+
+    /**
+     * mType
+     * The more specific type of this resource.  Used to implement special
+     * semantics (e.g., reentrancy of monitors.)
+     **/
+    BlockingResourceType mType;
+
+    /**
+     * mChainPrev
+     * A series of resource acquisitions creates a chain of orders.  This
+     * chain is implemented as a linked list; |mChainPrev| points to the
+     * resource most recently Show()'n before this one.
+     **/
+    BlockingResourceBase* mChainPrev;
+
+    /**
+     * Acquire
+     * Add this resource to the front of the current thread's acquisition 
+     * chain.  Should be called by, e.g., Mutex::Lock().
+     **/
+    void Acquire();
+    
+    /**
+     * Release
+     * Remove this resource from the current thread's acquisition chain.
+     * The resource does not have to be at the front of the chain, although
+     * it is confusing to release resources in a different order than they
+     * are acquired.
+     **/
+    void Release();
+
+#else
+    ~BlockingResourceBase() {
+    }
+
+    void Init(void* aResource, 
+              const char* aName,
+              BlockingResourceType aType) { }
+
+    void            Acquire() { }
+    void            Release() { }
+
+#endif
+};
+
+
+} // namespace mozilla
+
+
+#endif // mozilla_BlockingResourceBase_h
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/CondVar.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_CondVar_h
+#define mozilla_CondVar_h
+
+#include "prcvar.h"
+
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+
+
+/**
+ * CondVar
+ * Vanilla condition variable.  Please don't use this unless you have a 
+ * compelling reason --- Monitor provides a better API.
+ **/
+class NS_COM_GLUE CondVar : BlockingResourceBase
+{
+public:
+    /**
+     * CondVar
+     *
+     * The CALLER owns |lock|.
+     *
+     * @param lock An Mutex to associate with this condition variable.
+     * @param name A name which can reference this monitor
+     * @returns If failure, nsnull.
+     *          If success, a valid Monitor* which must be destroyed
+     *          by Monitor::DestroyMonitor()
+     **/
+    CondVar(Mutex& aLock, const char* name)
+        : mLock(&aLock) {
+        // |lock| must necessarily already be known to the deadlock detector
+        mCvar = PR_NewCondVar(mLock->mLock);
+        if (!mCvar)
+            NS_RUNTIMEABORT("Can't allocate mozilla::CondVar");
+        Init(mCvar, name, eCondVar);
+    }
+
+    /**
+     * ~CondVar
+     * Clean up after this CondVar, but NOT its associated Mutex.
+     **/
+    ~CondVar() {
+        NS_ASSERTION(mCvar && mLock,
+                     "improperly constructed CondVar or double free");
+        PR_DestroyCondVar(mCvar);
+        mCvar = 0;
+        mLock = 0;
+    }
+
+    /** 
+     * Wait
+     * @see prcvar.h 
+     **/      
+    nsresult Wait(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT) {
+        // NSPR checks for lock ownership
+        return PR_WaitCondVar(mCvar, interval) == PR_SUCCESS
+            ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+    /** 
+     * Notify
+     * @see prcvar.h 
+     **/      
+    nsresult Notify() {
+        // NSPR checks for lock ownership
+        return PR_NotifyCondVar(mCvar) == PR_SUCCESS
+            ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+    /** 
+     * NotifyAll
+     * @see prcvar.h 
+     **/      
+    nsresult NotifyAll() {
+        // NSPR checks for lock ownership
+        return PR_NotifyAllCondVar(mCvar) == PR_SUCCESS
+            ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+#ifdef DEBUG
+    /**
+     * AssertCurrentThreadOwnsMutex
+     * @see Mutex::AssertCurrentThreadOwns
+     **/
+    void AssertCurrentThreadOwnsMutex () {
+        mLock->AssertCurrentThreadOwns();
+    }
+
+    /**
+     * AssertNotCurrentThreadOwnsMutex
+     * @see Mutex::AssertNotCurrentThreadOwns
+     **/
+    void AssertNotCurrentThreadOwnsMutex () {
+        mLock->AssertNotCurrentThreadOwns();
+    }
+
+#else
+    void AssertCurrentThreadOwnsMutex () { }
+    void AssertNotCurrentThreadOwnsMutex () { }
+
+#endif  // ifdef DEBUG
+
+private:
+    CondVar();
+    CondVar(CondVar&);
+    CondVar& operator=(CondVar&);
+
+    Mutex* mLock;
+    PRCondVar* mCvar;
+};
+
+
+} // namespace mozilla
+
+
+#endif  // ifndef mozilla_CondVar_h  
--- a/xpcom/glue/Makefile.in
+++ b/xpcom/glue/Makefile.in
@@ -68,73 +68,80 @@ CSRCS		= \
 		$(NULL)  
 
 CPPSRCS		= \
 		$(XPCOM_GLUE_SRC_LCPPSRCS) \
 		$(XPCOM_GLUENS_SRC_LCPPSRCS) \
 		nsStringAPI.cpp \
 		$(NULL)
 
+# TODO nsAutoLock.h should die soon
 SDK_HEADERS = \
-		pldhash.h \
 		nsArrayEnumerator.h \
 		nsArrayUtils.h \
 		nsAutoLock.h \
 		nsBaseHashtable.h \
-		nsCategoryCache.h \
 		nsCOMArray.h \
+		nsCOMPtr.h \
 		nsCRTGlue.h \
+		nsCategoryCache.h \
 		nsClassHashtable.h \
+		nsComponentManagerUtils.h \
+		nsCycleCollectionParticipant.h \
 		nsDataHashtable.h \
+		nsDebug.h \
+		nsDeque.h \
 		nsEnumeratorUtils.h \
+		nsGenericFactory.h \
 		nsHashKeys.h \
-		nsINIParser.h \
-		nsInterfaceHashtable.h \
-		nsQuickSort.h \
-		nsStringGlue.h \
-		nsRefPtrHashtable.h \
-		nsStringAPI.h \
-		nsTextFormatter.h \
-		nsTHashtable.h \
-		nsVoidArray.h \
-		nsTArray.h \
-		nsTPtrArray.h \
-		nsTWeakRef.h \
+		nsIClassInfoImpl.h \
 		nsID.h \
 		nsIGenericFactory.h \
+		nsIGenericFactory.h \
 		nsIInterfaceRequestorUtils.h \
+		nsINIParser.h \
 		nsISupportsImpl.h \
 		nsISupportsUtils.h \
 		nsIWeakReferenceUtils.h \
-		nsCOMPtr.h \
-		nsDebug.h \
-		nsGenericFactory.h \
-		nsIGenericFactory.h \
+		nsInterfaceHashtable.h \
 		nsMemory.h \
+		nsQuickSort.h \
+		nsRefPtrHashtable.h \
+		nsServiceManagerUtils.h \
+		nsStringAPI.h \
+		nsStringGlue.h \
+		nsTArray.h \
+		nsTHashtable.h \
+		nsTObserverArray.h \
+		nsTPtrArray.h \
+		nsTWeakRef.h \
+		nsTextFormatter.h \
 		nsTraceRefcnt.h \
+		nsVersionComparator.h \
+		nsVoidArray.h \
 		nsWeakReference.h \
-		nsComponentManagerUtils.h \
-		nsServiceManagerUtils.h \
-		nsVersionComparator.h \
-		nsIClassInfoImpl.h \
-		nsTObserverArray.h \
-		nsCycleCollectionParticipant.h \
-		nsDeque.h \
+		pldhash.h \
 		$(NULL)
 
 EXPORTS = \
 		nsThreadUtils.h \
 		nsProxyRelease.h \
 		nsXPTCUtils.h \
 		$(NULL)
 
 SDK_LIBRARY     =                        \
 		$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
 		$(NULL)
 
+
+## TODO temp hack until build system handles this
+export:: BlockingResourceBase.h CondVar.h Monitor.h Mutex.h
+	$(INSTALL) $^ $(DIST)/include/mozilla
+
+
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 # Force use of PIC
 FORCE_USE_PIC	= 1
 
 # Pretend we're statically linking the CRT, even though we might not be: this
 # avoids "msvcrp" and assembly dependencies from creeping into the directives
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/Monitor.h
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_Monitor_h
+#define mozilla_Monitor_h
+
+#include "prmon.h"
+
+#include "mozilla/BlockingResourceBase.h"
+
+//
+// Provides:
+//
+//  - Monitor, a Java-like monitor
+//  - MonitorAutoEnter, an RAII class for ensuring that Monitors are properly 
+//    entered and exited
+//
+// Using MonitorAutoEnter is MUCH preferred to making bare calls to 
+// Monitor.Enter and Exit.
+//
+namespace mozilla {
+
+
+/**
+ * Monitor
+ * Java-like monitor.
+ * When possible, use MonitorAutoEnter to hold this monitor within a
+ * scope, instead of calling Enter/Exit directly.
+ **/
+class NS_COM_GLUE Monitor : BlockingResourceBase
+{
+public:
+    /**
+     * Monitor
+     * @param name A name which can reference this monitor
+     * @returns If failure, nsnull
+     *          If success, a valid Monitor*, which must be destroyed
+     *          by Monitor::DestroyMonitor()
+     **/
+    Monitor(const char* name) {
+        mMonitor = PR_NewMonitor();
+        if (!mMonitor)
+            NS_RUNTIMEABORT("Can't allocate mozilla::Monitor");
+        Init(mMonitor, name, eMonitor);
+    }
+
+    /**
+     * ~Monitor
+     **/
+    ~Monitor() {
+        NS_ASSERTION(mMonitor,
+                     "improperly constructed Monitor or double free");
+        PR_DestroyMonitor(mMonitor);
+        mMonitor = 0;
+    }
+
+#ifndef DEBUG
+    /** 
+     * Enter
+     * @see prmon.h 
+     **/
+    void Enter() {
+        // NSPR does consistency checks for us
+        PR_EnterMonitor(mMonitor);
+    }
+
+    /** 
+     * Exit
+     * @see prmon.h 
+     **/
+    void Exit() {
+        PR_ExitMonitor(mMonitor);
+    }
+
+#else
+    void Enter();
+    void Exit();
+
+#endif  // ifndef DEBUG
+
+    /** 
+     * Wait
+     * @see prmon.h 
+     **/      
+    nsresult Wait(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT) {
+        return PR_Wait(mMonitor, interval) == PR_SUCCESS
+            ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+    /** 
+     * Notify
+     * @see prmon.h 
+     **/      
+    nsresult Notify() {
+        return PR_Notify(mMonitor) == PR_SUCCESS
+            ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+    /** 
+     * NotifyAll
+     * @see prmon.h 
+     **/      
+    nsresult NotifyAll() {
+        return PR_NotifyAll(mMonitor) == PR_SUCCESS
+            ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+#ifdef DEBUG
+    /**
+     * AssertCurrentThreadIn
+     * @see prmon.h
+     **/
+    void AssertCurrentThreadIn () {
+        PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+    }
+
+    /**
+     * AssertNotCurrentThreadIn
+     * @see prmon.h
+     **/
+    void AssertNotCurrentThreadIn () {
+        // TODO
+    }
+
+#else
+    void AssertCurrentThreadIn () { }
+    void AssertNotCurrentThreadIn () { }
+
+#endif  // ifdef DEBUG
+
+private:
+    Monitor();
+    Monitor(const Monitor&);
+    Monitor& operator =(const Monitor&);
+
+    PRMonitor* mMonitor;
+#ifdef DEBUG
+    PRInt32 mEntryCount;
+#endif
+};
+
+
+/**
+ * MonitorAutoEnter
+ * Enters the Monitor when it enters scope, and exits it when it leaves 
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to Monitor.Enter and Exit.
+ */ 
+class NS_COM_GLUE NS_STACK_CLASS MonitorAutoEnter
+{
+public:
+    /**
+     * Constructor
+     * The constructor aquires the given lock.  The destructor
+     * releases the lock.
+     * 
+     * @param aMonitor A valid mozilla::Monitor* returned by 
+     *                 mozilla::Monitor::NewMonitor. 
+     **/
+    MonitorAutoEnter(mozilla::Monitor &aMonitor)
+        : mMonitor(&aMonitor) {
+        NS_ASSERTION(mMonitor, "null monitor");
+        mMonitor->Enter();
+    }
+    
+    ~MonitorAutoEnter(void) {
+        mMonitor->Exit();
+    }
+ 
+    nsresult Wait(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT) {
+       return mMonitor->Wait(interval);
+    }
+
+    nsresult Notify() {
+        return mMonitor->Notify();
+    }
+
+    nsresult NotifyAll() {
+        return mMonitor->Notify();
+    }
+
+private:
+    MonitorAutoEnter();
+    MonitorAutoEnter(const MonitorAutoEnter&);
+    MonitorAutoEnter& operator =(const MonitorAutoEnter&);
+    static void* operator new(size_t) CPP_THROW_NEW;
+    static void operator delete(void*);
+
+    mozilla::Monitor* mMonitor;
+};
+
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_Monitor_h
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/Mutex.h
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_Mutex_h
+#define mozilla_Mutex_h
+
+#include "prlock.h"
+
+#include "mozilla/BlockingResourceBase.h"
+
+//
+// Provides:
+//
+//  - Mutex, a non-recursive mutex
+//  - MutexAutoLock, an RAII class for ensuring that Mutexes are properly 
+//    locked and unlocked
+//  - MutexAutoUnlock, complementary sibling to MutexAutoLock
+//
+// Using MutexAutoLock/MutexAutoUnlock is MUCH preferred to making bare
+// calls to Mutex.Lock and Unlock.
+//
+namespace mozilla {
+
+
+/**
+ * Mutex
+ * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
+ * mutex within a scope, instead of calling Lock/Unlock directly.
+ **/
+
+class NS_COM_GLUE Mutex : BlockingResourceBase
+{
+public:
+    /**
+     * Mutex
+     * @param name A name which can reference this lock
+     * @returns If failure, nsnull
+     *          If success, a valid Mutex* which must be destroyed
+     *          by Mutex::DestroyMutex()
+     **/
+    Mutex(const char* name) {
+        mLock = PR_NewLock();
+        if (!mLock)
+            NS_RUNTIMEABORT("Can't allocate mozilla::Mutex");
+        Init(mLock, name, eMutex);
+    }
+
+    /**
+     * ~Mutex
+     **/
+    ~Mutex() {
+        NS_ASSERTION(mLock,
+                     "improperly constructed Lock or double free");
+        // NSPR does consistency checks for us
+        PR_DestroyLock(mLock);
+        mLock = 0;
+    }
+
+#ifndef DEBUG
+    /**
+     * Lock
+     * @see prlock.h
+     **/
+    void Lock() {
+        PR_Lock(mLock);
+    }
+
+    /**
+     * Unlock
+     * @see prlock.h
+     **/
+    void Unlock() {
+        PR_Unlock(mLock);
+    }
+
+    /**
+     * AssertCurrentThreadOwns
+     * @see prlock.h
+     **/
+    void AssertCurrentThreadOwns () { }
+
+    /**
+     * AssertNotCurrentThreadOwns
+     * @see prlock.h
+     **/
+    void AssertNotCurrentThreadOwns () { }
+
+#else
+    void Lock();
+    void Unlock();
+
+    void AssertCurrentThreadOwns () {
+        PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mLock);
+    }
+
+    void AssertNotCurrentThreadOwns () {
+        // TODO
+    }
+
+#endif  // ifndef DEBUG
+
+private:
+    Mutex();
+    Mutex(const Mutex&);
+    Mutex& operator=(const Mutex&);
+
+    PRLock* mLock;
+
+    friend class CondVar;
+};
+
+
+/**
+ * MutexAutoLock
+ * Acquires the Mutex when it enters scope, and releases it when it leaves 
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
+ */ 
+class NS_COM_GLUE NS_STACK_CLASS MutexAutoLock
+{
+public:
+    /**
+     * Constructor
+     * The constructor aquires the given lock.  The destructor
+     * releases the lock.
+     * 
+     * @param aLock A valid mozilla::Mutex* returned by 
+     *              mozilla::Mutex::NewMutex. 
+     **/
+    MutexAutoLock(mozilla::Mutex& aLock)
+        : mLock(&aLock) {
+        NS_ASSERTION(mLock, "null mutex");
+        mLock->Lock();
+    }
+    
+    ~MutexAutoLock(void) {
+        mLock->Unlock();
+    }
+ 
+private:
+    MutexAutoLock();
+    MutexAutoLock(MutexAutoLock&);
+    MutexAutoLock& operator=(MutexAutoLock&);
+    static void* operator new(size_t) CPP_THROW_NEW;
+    static void operator delete(void*);
+
+    mozilla::Mutex* mLock;
+};
+
+
+/**
+ * MutexAutoUnlock
+ * Releases the Mutex when it enters scope, and re-acquires it when it leaves 
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
+ */ 
+class NS_COM_GLUE NS_STACK_CLASS MutexAutoUnlock 
+{
+public:
+    MutexAutoUnlock(mozilla::Mutex& aLock) 
+        : mLock(&aLock) {
+        NS_ASSERTION(mLock, "null lock");
+        mLock->Unlock();
+    }
+
+    ~MutexAutoUnlock() 
+    {
+        mLock->Lock();
+    }
+
+private:
+    MutexAutoUnlock();
+    MutexAutoUnlock(MutexAutoUnlock&);
+    MutexAutoUnlock& operator =(MutexAutoUnlock&);
+    static void* operator new(size_t) CPP_THROW_NEW;
+    static void operator delete(void*);
+     
+    mozilla::Mutex* mLock;
+};
+
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_Mutex_h
--- a/xpcom/glue/nsDebug.h
+++ b/xpcom/glue/nsDebug.h
@@ -179,16 +179,30 @@
 #define NS_NOTREACHED(str)             PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
 #define NS_ERROR(str)                  PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
 #define NS_WARNING(str)                PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
 #define NS_ABORT()                     PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
 #define NS_BREAK()                     PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
 
 #endif /* ! NS_DEBUG */
 
+/******************************************************************************
+** Macros for terminating execution when an unrecoverable condition is
+** reached.  These need to be compiled regardless of the NS_DEBUG flag. 
+******************************************************************************/
+
+/**
+ * Terminate execution <i>immediately</i>, and if possible on the current
+ * platform, in such a way that execution can't be continued by other
+ * code (e.g., by intercepting a signal).
+ */
+#define NS_RUNTIMEABORT(msg)                                    \
+  NS_DebugBreak(NS_DEBUG_ABORT, msg, nsnull, __FILE__, __LINE__)
+
+
 /* Macros for checking the trueness of an expression passed in within an 
  * interface implementation.  These need to be compiled regardless of the */
 /* NS_DEBUG flag
 ******************************************************************************/
 
 #define NS_ENSURE_TRUE(x, ret)                                \
   PR_BEGIN_MACRO                                              \
     if (NS_UNLIKELY(!(x))) {                                  \
--- a/xpcom/glue/objs.mk
+++ b/xpcom/glue/objs.mk
@@ -68,16 +68,19 @@ XPCOM_GLUE_SRC_LCPPSRCS =        \
   $(NULL)
 
 XPCOM_GLUE_SRC_CPPSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCPPSRCS))
 
 # nsGenericFactory is not really all that helpful in the standalone glue,
 # and it has a bad dependency on the NSPR AtomicIncrement function, so we
 # only build it for the dependent XPCOM glue and builtin to xpcom-core.
 
+# TODO nsAutoLock.cpp should die soon
+
 XPCOM_GLUENS_SRC_LCPPSRCS =      \
+  BlockingResourceBase.cpp       \
   nsAutoLock.cpp                 \
   nsGenericFactory.cpp           \
   nsProxyRelease.cpp             \
   nsTextFormatter.cpp            \
   $(NULL)
 
 XPCOM_GLUENS_SRC_CPPSRCS = $(addprefix $(topsrcdir)/xpcom/glue/,$(XPCOM_GLUENS_SRC_LCPPSRCS))
--- a/xpcom/tests/Makefile.in
+++ b/xpcom/tests/Makefile.in
@@ -108,16 +108,17 @@ CPP_UNIT_TESTS += \
                   TestPipes.cpp \
                   TestProxies.cpp \
                   TestThreads.cpp \
                   TestThreadPool.cpp \
                   TestXPIDLString.cpp \
                   TestDeque.cpp \
                   TestStrings.cpp \
                   TestStorageStream.cpp \
+                  TestSynchronization.cpp \
                   TestTArray.cpp \
                   TestTimeStamp.cpp \
                   $(NULL)
 endif
 
 ifndef MOZILLA_INTERNAL_API
 CPP_UNIT_TESTS += \
                   TestStringAPI.cpp \
--- a/xpcom/tests/TestPipes.cpp
+++ b/xpcom/tests/TestPipes.cpp
@@ -258,42 +258,44 @@ public:
             Received(count);
             total += count;
         }
         printf("read  %d bytes\n", total);
         return rv;
     }
 
     nsShortReader(nsIInputStream* in) : mIn(in), mReceived(0) {
+        mMon = nsAutoMonitor::NewMonitor("nsShortReader");
     }
 
     void Received(PRUint32 count) {
-        nsAutoCMonitor mon(this);
+        nsAutoMonitor mon(mMon);
         mReceived += count;
         mon.Notify();
     }
 
     PRUint32 WaitForReceipt(const PRUint32 aWriteCount) {
-        nsAutoCMonitor mon(this);
+        nsAutoMonitor mon(mMon);
         PRUint32 result = mReceived;
 
         while (result < aWriteCount) {
             mon.Wait();
 
             NS_ASSERTION(mReceived > result, "failed to receive");
             result = mReceived;
         }
 
         mReceived = 0;
         return result;
     }
 
 protected:
     nsCOMPtr<nsIInputStream> mIn;
     PRUint32            mReceived;
+    PRMonitor*          mMon;
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsShortReader, nsIRunnable)
 
 nsresult
 TestShortWrites(nsIInputStream* in, nsIOutputStream* out)
 {
     nsCOMPtr<nsShortReader> receiver = new nsShortReader(in);
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/TestSynchronization.cpp
@@ -0,0 +1,382 @@
+#include "TestHarness.h"
+
+#include "mozilla/CondVar.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Mutex.h"
+#include "nsAutoLock.h"
+
+using namespace mozilla;
+
+static PRThread*
+spawn(void (*run)(void*), void* arg)
+{
+    return PR_CreateThread(PR_SYSTEM_THREAD,
+                           run,
+                           arg,
+                           PR_PRIORITY_NORMAL,
+                           PR_GLOBAL_THREAD,
+                           PR_JOINABLE_THREAD,
+                           0);
+}
+
+
+#define PASS()                                  \
+    do {                                        \
+        passed(__FUNCTION__);                   \
+        return NS_OK;                           \
+    } while (0);
+
+
+#define FAIL(why)                               \
+    do {                                        \
+        fail(why);                              \
+        return NS_ERROR_FAILURE;                \
+    } while (0);
+
+//-----------------------------------------------------------------------------
+// Sanity check: tests that can be done on a single thread
+//
+static nsresult
+Sanity()
+{
+    Mutex lock("sanity::lock");
+    lock.Lock();
+    lock.AssertCurrentThreadOwns();
+    lock.Unlock();
+    
+    {
+        MutexAutoLock autolock(lock);
+        lock.AssertCurrentThreadOwns();
+    }
+
+    lock.Lock();
+    lock.AssertCurrentThreadOwns();
+    {
+        MutexAutoUnlock autounlock(lock);
+    }
+    lock.AssertCurrentThreadOwns();
+    lock.Unlock();
+
+    Monitor mon("sanity::monitor");
+    mon.Enter();
+    mon.AssertCurrentThreadIn();
+    mon.Enter();
+    mon.AssertCurrentThreadIn();
+    mon.Exit();
+    mon.AssertCurrentThreadIn();
+    mon.Exit();
+
+    {
+        MonitorAutoEnter automon(mon);
+        mon.AssertCurrentThreadIn();
+    }
+
+    PASS();
+}
+
+//-----------------------------------------------------------------------------
+// Mutex contention tests
+//
+static Mutex* gLock1;
+
+static void
+MutexContention_thread(void* /*arg*/)
+{
+    for (int i = 0; i < 100000; ++i) {
+        gLock1->Lock();
+        gLock1->AssertCurrentThreadOwns();
+        gLock1->Unlock();
+    }
+}
+
+static nsresult
+MutexContention()
+{
+    gLock1 = new Mutex("lock1");
+    // PURPOSELY not checking for OOM.  YAY!
+
+    PRThread* t1 = spawn(MutexContention_thread, nsnull);
+    PRThread* t2 = spawn(MutexContention_thread, nsnull);
+    PRThread* t3 = spawn(MutexContention_thread, nsnull);
+
+    PR_JoinThread(t1);
+    PR_JoinThread(t2);
+    PR_JoinThread(t3);
+
+    delete gLock1;
+
+    PASS();
+}
+
+//-----------------------------------------------------------------------------
+// Monitor tests
+//
+static Monitor* gMon1;
+
+static void
+MonitorContention_thread(void* /*arg*/)
+{
+    for (int i = 0; i < 100000; ++i) {
+        gMon1->Enter();
+        gMon1->AssertCurrentThreadIn();
+        gMon1->Exit();
+    }
+}
+
+static nsresult
+MonitorContention()
+{
+    gMon1 = new Monitor("mon1");
+
+    PRThread* t1 = spawn(MonitorContention_thread, nsnull);
+    PRThread* t2 = spawn(MonitorContention_thread, nsnull);
+    PRThread* t3 = spawn(MonitorContention_thread, nsnull);
+
+    PR_JoinThread(t1);
+    PR_JoinThread(t2);
+    PR_JoinThread(t3);
+
+    delete gMon1;
+
+    PASS();
+}
+
+
+static Monitor* gMon2;
+
+static void
+MonitorContention2_thread(void* /*arg*/)
+{
+    for (int i = 0; i < 100000; ++i) {
+        gMon2->Enter();
+        gMon2->AssertCurrentThreadIn();
+        {
+            gMon2->Enter();
+            gMon2->AssertCurrentThreadIn();
+            gMon2->Exit();
+        }
+        gMon2->AssertCurrentThreadIn();
+        gMon2->Exit();
+    }
+}
+
+static nsresult
+MonitorContention2()
+{
+    gMon2 = new Monitor("mon1");
+
+    PRThread* t1 = spawn(MonitorContention2_thread, nsnull);
+    PRThread* t2 = spawn(MonitorContention2_thread, nsnull);
+    PRThread* t3 = spawn(MonitorContention2_thread, nsnull);
+
+    PR_JoinThread(t1);
+    PR_JoinThread(t2);
+    PR_JoinThread(t3);
+
+    delete gMon2;
+
+    PASS();
+}
+
+
+static Monitor* gMon3;
+static PRInt32 gMonFirst;
+
+static void
+MonitorSyncSanity_thread(void* /*arg*/)
+{
+    gMon3->Enter();
+    gMon3->AssertCurrentThreadIn();
+    if (gMonFirst) {
+        gMonFirst = 0;
+        gMon3->Wait();
+        gMon3->Enter();
+    } else {
+        gMon3->Notify();
+        gMon3->Enter();
+    }
+    gMon3->AssertCurrentThreadIn();
+    gMon3->Exit();
+    gMon3->AssertCurrentThreadIn();
+    gMon3->Exit();
+}
+
+static nsresult
+MonitorSyncSanity()
+{
+    gMon3 = new Monitor("monitor::syncsanity");
+   
+    for (PRInt32 i = 0; i < 10000; ++i) {
+        gMonFirst = 1;
+        PRThread* ping = spawn(MonitorSyncSanity_thread, nsnull);
+        PRThread* pong = spawn(MonitorSyncSanity_thread, nsnull);
+        PR_JoinThread(ping);
+        PR_JoinThread(pong);
+    }
+
+    delete gMon3;
+
+    PASS();
+}
+
+//-----------------------------------------------------------------------------
+// Condvar tests
+//
+static Mutex* gCvlock1;
+static CondVar* gCv1;
+static PRInt32 gCvFirst;
+
+static void
+CondVarSanity_thread(void* /*arg*/)
+{
+    gCvlock1->Lock();
+    gCvlock1->AssertCurrentThreadOwns();
+    if (gCvFirst) {
+        gCvFirst = 0;
+        gCv1->Wait();
+    } else {
+        gCv1->Notify();
+    }
+    gCvlock1->AssertCurrentThreadOwns();
+    gCvlock1->Unlock();
+}
+
+static nsresult
+CondVarSanity()
+{
+    gCvlock1 = new Mutex("cvlock1");
+    gCv1 = new CondVar(*gCvlock1, "cvlock1");
+
+    for (PRInt32 i = 0; i < 10000; ++i) {
+        gCvFirst = 1;
+        PRThread* ping = spawn(CondVarSanity_thread, nsnull);
+        PRThread* pong = spawn(CondVarSanity_thread, nsnull);
+        PR_JoinThread(ping);
+        PR_JoinThread(pong);
+    }
+
+    delete gCv1;
+    delete gCvlock1;
+
+    PASS();
+}
+
+//-----------------------------------------------------------------------------
+// AutoLock tests
+//
+static nsresult
+AutoLock()
+{
+    Mutex l1("autolock");
+    MutexAutoLock autol1(l1);
+
+    l1.AssertCurrentThreadOwns();
+
+    {
+        Mutex l2("autolock2");
+        MutexAutoLock autol2(l2);
+
+        l1.AssertCurrentThreadOwns();
+        l2.AssertCurrentThreadOwns();
+    }
+
+    l1.AssertCurrentThreadOwns();
+
+    PASS();
+}
+
+//-----------------------------------------------------------------------------
+// AutoUnlock tests
+//
+static nsresult
+AutoUnlock()
+{
+    Mutex l1("autounlock");
+    Mutex l2("autounlock2");
+
+    l1.Lock();
+    l1.AssertCurrentThreadOwns();
+
+    {
+        MutexAutoUnlock autol1(l1);
+        {
+            l2.Lock();
+            l2.AssertCurrentThreadOwns();
+
+            MutexAutoUnlock autol2(l2);
+        }
+        l2.AssertCurrentThreadOwns();
+        l2.Unlock();
+    }
+    l1.AssertCurrentThreadOwns();
+
+    l1.Unlock();
+
+    PASS();
+}
+
+//-----------------------------------------------------------------------------
+// AutoMonitor tests
+//
+static nsresult
+AutoMonitor()
+{
+    Monitor m1("automonitor");
+    Monitor m2("automonitor2");
+
+    m1.Enter();
+    m1.AssertCurrentThreadIn();
+    {
+        MonitorAutoEnter autom1(m1);
+        m1.AssertCurrentThreadIn();
+
+        m2.Enter();
+        m2.AssertCurrentThreadIn();
+        {
+            MonitorAutoEnter autom2(m2);
+            m1.AssertCurrentThreadIn();
+            m2.AssertCurrentThreadIn();
+        }
+        m2.AssertCurrentThreadIn();
+        m2.Exit();
+
+        m1.AssertCurrentThreadIn();
+    }
+    m1.AssertCurrentThreadIn();
+    m1.Exit();
+
+    PASS();
+}
+
+//-----------------------------------------------------------------------------
+
+int
+main(int argc, char** argv)
+{
+    ScopedXPCOM xpcom("Synchronization");
+    if (xpcom.failed())
+        return 1;
+
+    int rv = 0;
+
+    if (NS_FAILED(Sanity()))
+        rv = 1;
+    if (NS_FAILED(MutexContention()))
+        rv = 1;
+    if (NS_FAILED(MonitorContention()))
+        rv = 1;
+    if (NS_FAILED(MonitorContention2()))
+        rv = 1;
+    if (NS_FAILED(MonitorSyncSanity()))
+        rv = 1;
+    if (NS_FAILED(CondVarSanity()))
+        rv = 1;
+    if (NS_FAILED(AutoLock()))
+        rv = 1;
+    if (NS_FAILED(AutoUnlock()))
+        rv = 1;
+    if (NS_FAILED(AutoMonitor()))
+        rv = 1;
+
+    return rv;
+}