gecko-dev/xpcom/threads/Mutex.h
Nathan Froyd 5fd1d453a9 Bug 1312087 - part 1 - move mozilla::{Mutex,CondVar} to use mozglue locking primitives; r=erahm
This change moves us away from NSPR primitives for our primary
synchronization primitives.  We're still using PRMonitor for
ReentrantMonitor, however.

The benefits of this change:

* Slightly faster, as we don't have to deal with some of NSPR's overhead;

* Smaller datatypes.  On POSIX platforms in particular, PRLock is
  enormous. PRCondVar also has some unnecessary overhead.

* Less dynamic memory allocation.  Out of necessity, Mutex and CondVar
  allocated the NSPR data structures they needed, which lead to
  unnecessary checks for failure.

  While sizeof(Mutex) and sizeof(CondVar) may get bigger, since they're
  embedding structures now, the total memory usage should be less.

* Less NSPR usage.  This shouldn't need any explanation.
2017-03-21 10:20:36 -05:00

225 lines
5.1 KiB
C++

/* -*- 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_Mutex_h
#define mozilla_Mutex_h
#include "mozilla/BlockingResourceBase.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/PlatformMutex.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
//
// - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking
// - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for
// an OffTheBooksMutex.
//
// Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare
// calls to Lock and Unlock.
//
namespace mozilla {
/**
* OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't
* include leak checking. Sometimes you want to intentionally "leak" a mutex
* until shutdown; in these cases, OffTheBooksMutex is for you.
*/
class OffTheBooksMutex : public detail::MutexImpl, BlockingResourceBase
{
public:
/**
* @param aName A name which can reference this lock
* @returns If failure, nullptr
* If success, a valid Mutex* which must be destroyed
* by Mutex::DestroyMutex()
**/
explicit OffTheBooksMutex(const char* aName)
: detail::MutexImpl()
, BlockingResourceBase(aName, eMutex)
#ifdef DEBUG
, mOwningThread(nullptr)
#endif
{
}
~OffTheBooksMutex()
{
#ifdef DEBUG
MOZ_ASSERT(!mOwningThread, "destroying a still-owned lock!");
#endif
}
#ifndef DEBUG
/**
* Lock this mutex.
**/
void Lock() { this->lock(); }
/**
* Unlock this mutex.
**/
void Unlock() { this->unlock(); }
/**
* Assert that the current thread owns this mutex in debug builds.
*
* Does nothing in non-debug builds.
**/
void AssertCurrentThreadOwns() const {}
/**
* Assert that the current thread does not own this mutex.
*
* Note that this function is not implemented for debug builds *and*
* non-debug builds due to difficulties in dealing with memory ordering.
*
* It is therefore mostly useful as documentation.
**/
void AssertNotCurrentThreadOwns() const {}
#else
void Lock();
void Unlock();
void AssertCurrentThreadOwns() const;
void AssertNotCurrentThreadOwns() const
{
// FIXME bug 476536
}
#endif // ifndef DEBUG
private:
OffTheBooksMutex();
OffTheBooksMutex(const OffTheBooksMutex&);
OffTheBooksMutex& operator=(const OffTheBooksMutex&);
friend class CondVar;
#ifdef DEBUG
PRThread* mOwningThread;
#endif
};
/**
* Mutex
* When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
* mutex within a scope, instead of calling Lock/Unlock directly.
*/
class Mutex : public OffTheBooksMutex
{
public:
explicit Mutex(const char* aName)
: OffTheBooksMutex(aName)
{
MOZ_COUNT_CTOR(Mutex);
}
~Mutex()
{
MOZ_COUNT_DTOR(Mutex);
}
private:
Mutex();
Mutex(const Mutex&);
Mutex& operator=(const Mutex&);
};
/**
* 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.
*/
template<typename T>
class MOZ_RAII BaseAutoLock
{
public:
/**
* Constructor
* The constructor aquires the given lock. The destructor
* releases the lock.
*
* @param aLock A valid mozilla::Mutex* returned by
* mozilla::Mutex::NewMutex.
**/
explicit BaseAutoLock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mLock(&aLock)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
NS_ASSERTION(mLock, "null mutex");
mLock->Lock();
}
~BaseAutoLock(void)
{
mLock->Unlock();
}
private:
BaseAutoLock();
BaseAutoLock(BaseAutoLock&);
BaseAutoLock& operator=(BaseAutoLock&);
static void* operator new(size_t) CPP_THROW_NEW;
T* mLock;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
typedef BaseAutoLock<Mutex> MutexAutoLock;
typedef BaseAutoLock<OffTheBooksMutex> OffTheBooksMutexAutoLock;
/**
* 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.
*/
template<typename T>
class MOZ_RAII BaseAutoUnlock
{
public:
explicit BaseAutoUnlock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mLock(&aLock)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
NS_ASSERTION(mLock, "null lock");
mLock->Unlock();
}
~BaseAutoUnlock()
{
mLock->Lock();
}
private:
BaseAutoUnlock();
BaseAutoUnlock(BaseAutoUnlock&);
BaseAutoUnlock& operator=(BaseAutoUnlock&);
static void* operator new(size_t) CPP_THROW_NEW;
T* mLock;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
typedef BaseAutoUnlock<Mutex> MutexAutoUnlock;
typedef BaseAutoUnlock<OffTheBooksMutex> OffTheBooksMutexAutoUnlock;
} // namespace mozilla
#endif // ifndef mozilla_Mutex_h