diff --git a/xpcom/base/StaticMonitor.h b/xpcom/base/StaticMonitor.h new file mode 100644 index 000000000000..1912d348775c --- /dev/null +++ b/xpcom/base/StaticMonitor.h @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_StaticMonitor_h +#define mozilla_StaticMonitor_h + +#include "mozilla/Atomics.h" +#include "mozilla/CondVar.h" + +namespace mozilla { + +class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMonitor +{ +public: + // In debug builds, check that mMutex is initialized for us as we expect by + // the compiler. In non-debug builds, don't declare a constructor so that + // the compiler can see that the constructor is trivial. +#ifdef DEBUG + StaticMonitor() + { + MOZ_ASSERT(!mMutex); + } +#endif + + void Lock() + { + Mutex()->Lock(); + } + + void Unlock() + { + Mutex()->Unlock(); + } + + void Wait() { CondVar()->Wait(); } + CVStatus Wait(TimeDuration aDuration) { return CondVar()->Wait(aDuration); } + + nsresult Notify() { return CondVar()->Notify(); } + nsresult NotifyAll() { return CondVar()->NotifyAll(); } + + void AssertCurrentThreadOwns() + { +#ifdef DEBUG + Mutex()->AssertCurrentThreadOwns(); +#endif + } + +private: + OffTheBooksMutex* Mutex() + { + if (mMutex) { + return mMutex; + } + + OffTheBooksMutex* mutex = new OffTheBooksMutex("StaticMutex"); + if (!mMutex.compareExchange(nullptr, mutex)) { + delete mutex; + } + + return mMutex; + } + + OffTheBooksCondVar* CondVar() + { + if (mCondVar) { + return mCondVar; + } + + OffTheBooksCondVar* condvar = new OffTheBooksCondVar(*Mutex(), "StaticCondVar"); + if (!mCondVar.compareExchange(nullptr, condvar)) { + delete condvar; + } + + return mCondVar; + } + + Atomic mMutex; + Atomic mCondVar; + + + // Disallow copy constructor, but only in debug mode. We only define + // a default constructor in debug mode (see above); if we declared + // this constructor always, the compiler wouldn't generate a trivial + // default constructor for us in non-debug mode. +#ifdef DEBUG + StaticMonitor(const StaticMonitor& aOther); +#endif + + // Disallow these operators. + StaticMonitor& operator=(const StaticMonitor& aRhs); + static void* operator new(size_t) CPP_THROW_NEW; + static void operator delete(void*); +}; + +class MOZ_STACK_CLASS StaticMonitorAutoLock +{ +public: + explicit StaticMonitorAutoLock(StaticMonitor& aMonitor) + : mMonitor(&aMonitor) + { + mMonitor->Lock(); + } + + ~StaticMonitorAutoLock() + { + mMonitor->Unlock(); + } + + void Wait() { mMonitor->Wait(); } + CVStatus Wait(TimeDuration aDuration) { return mMonitor->Wait(aDuration); } + + nsresult Notify() { return mMonitor->Notify(); } + nsresult NotifyAll() { return mMonitor->NotifyAll(); } + +private: + StaticMonitorAutoLock(); + StaticMonitorAutoLock(const StaticMonitorAutoLock&); + StaticMonitorAutoLock& operator=(const StaticMonitorAutoLock&); + static void* operator new(size_t) CPP_THROW_NEW; + + StaticMonitor* mMonitor; +}; + +} // namespace mozilla + +#endif diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index 853b7f135e06..9e1fab4471b1 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -117,6 +117,7 @@ EXPORTS.mozilla += [ 'NSPRLogModulesParser.h', 'OwningNonNull.h', 'SizeOfState.h', + 'StaticMonitor.h', 'StaticMutex.h', 'StaticPtr.h', ] diff --git a/xpcom/threads/BlockingResourceBase.cpp b/xpcom/threads/BlockingResourceBase.cpp index b135a97ddd83..ca72f16a2ff9 100644 --- a/xpcom/threads/BlockingResourceBase.cpp +++ b/xpcom/threads/BlockingResourceBase.cpp @@ -587,15 +587,15 @@ RecursiveMutex::AssertCurrentThreadIn() // // Debug implementation of CondVar void -CondVar::Wait() +OffTheBooksCondVar::Wait() { - // Forward to the timed version of CondVar::Wait to avoid code duplication. + // Forward to the timed version of OffTheBooksCondVar::Wait to avoid code duplication. CVStatus status = Wait(TimeDuration::Forever()); MOZ_ASSERT(status == CVStatus::NoTimeout); } CVStatus -CondVar::Wait(TimeDuration aDuration) +OffTheBooksCondVar::Wait(TimeDuration aDuration) { AssertCurrentThreadOwnsMutex(); diff --git a/xpcom/threads/CondVar.h b/xpcom/threads/CondVar.h index fe9bdec6cb74..348a53a75329 100644 --- a/xpcom/threads/CondVar.h +++ b/xpcom/threads/CondVar.h @@ -17,17 +17,17 @@ namespace mozilla { - /** - * CondVar - * Vanilla condition variable. Please don't use this unless you have a - * compelling reason --- Monitor provides a simpler API. + * Similarly to OffTheBooksMutex, OffTheBooksCondvar is identical to CondVar, + * except that OffTheBooksCondVar doesn't include leak checking. Sometimes + * you want to intentionally "leak" a CondVar until shutdown; in these cases, + * OffTheBooksCondVar is for you. */ -class CondVar : BlockingResourceBase +class OffTheBooksCondVar : BlockingResourceBase { public: /** - * CondVar + * OffTheBooksCondVar * * The CALLER owns |aLock|. * @@ -37,20 +37,18 @@ public: * If success, a valid Monitor* which must be destroyed * by Monitor::DestroyMonitor() **/ - CondVar(Mutex& aLock, const char* aName) + OffTheBooksCondVar(OffTheBooksMutex& aLock, const char* aName) : BlockingResourceBase(aName, eCondVar) , mLock(&aLock) { - MOZ_COUNT_CTOR(CondVar); } /** - * ~CondVar - * Clean up after this CondVar, but NOT its associated Mutex. + * ~OffTheBooksCondVar + * Clean up after this OffTheBooksCondVar, but NOT its associated Mutex. **/ - ~CondVar() + ~OffTheBooksCondVar() { - MOZ_COUNT_DTOR(CondVar); } /** @@ -125,14 +123,39 @@ public: #endif // ifdef DEBUG private: - CondVar(); - CondVar(const CondVar&) = delete; - CondVar& operator=(const CondVar&) = delete; + OffTheBooksCondVar(); + OffTheBooksCondVar(const OffTheBooksCondVar&) = delete; + OffTheBooksCondVar& operator=(const OffTheBooksCondVar&) = delete; - Mutex* mLock; + OffTheBooksMutex* mLock; detail::ConditionVariableImpl mImpl; }; +/** + * CondVar + * Vanilla condition variable. Please don't use this unless you have a + * compelling reason --- Monitor provides a simpler API. + */ +class CondVar : public OffTheBooksCondVar +{ +public: + CondVar(OffTheBooksMutex& aLock, const char* aName) + : OffTheBooksCondVar(aLock, aName) + { + MOZ_COUNT_CTOR(CondVar); + } + + ~CondVar() + { + MOZ_COUNT_DTOR(CondVar); + } + +private: + CondVar(); + CondVar(const CondVar&); + CondVar& operator=(const CondVar&); +}; + } // namespace mozilla diff --git a/xpcom/threads/Mutex.h b/xpcom/threads/Mutex.h index 57babe338768..e7815f8c8012 100644 --- a/xpcom/threads/Mutex.h +++ b/xpcom/threads/Mutex.h @@ -104,7 +104,7 @@ private: OffTheBooksMutex(const OffTheBooksMutex&); OffTheBooksMutex& operator=(const OffTheBooksMutex&); - friend class CondVar; + friend class OffTheBooksCondVar; #ifdef DEBUG PRThread* mOwningThread;