--- 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;
+}