mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1207753 - Base thread-safety attribution support r=nika
With additions of MOZ_UNANNOTATED for *Mutex/*Monitor/RWLock classes Differential Revision: https://phabricator.services.mozilla.com/D130606
This commit is contained in:
parent
b1b54e8f25
commit
e72ce3cd6c
@ -21,6 +21,7 @@
|
||||
#include "mozilla/MemoryChecking.h"
|
||||
#include "mozilla/OperatorNewExtensions.h"
|
||||
#include "mozilla/Poison.h"
|
||||
#include "mozilla/ThreadSafety.h"
|
||||
|
||||
class nsCycleCollectionTraversalCallback;
|
||||
|
||||
@ -636,7 +637,13 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
|
||||
constexpr void reset() {
|
||||
if (isSome()) {
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
/*
|
||||
* Static analyzer gets confused if we have Maybe<MutexAutoLock>,
|
||||
* so we suppress thread-safety warnings here
|
||||
*/
|
||||
PUSH_IGNORE_THREAD_SAFETY
|
||||
ref().T::~T();
|
||||
POP_THREAD_SAFETY
|
||||
poisonData();
|
||||
}
|
||||
mIsSome = false;
|
||||
|
135
mfbt/ThreadSafety.h
Normal file
135
mfbt/ThreadSafety.h
Normal file
@ -0,0 +1,135 @@
|
||||
// Note: the file is largely imported directly from WebRTC upstream, so
|
||||
// comments may not completely apply to Mozilla's usage.
|
||||
//
|
||||
// Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
//
|
||||
// Borrowed from
|
||||
// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h
|
||||
// but adapted for clang attributes instead of the gcc.
|
||||
//
|
||||
// This header file contains the macro definitions for thread safety
|
||||
// annotations that allow the developers to document the locking policies
|
||||
// of their multi-threaded code. The annotations can also help program
|
||||
// analysis tools to identify potential thread safety issues.
|
||||
|
||||
#ifndef mozilla_ThreadSafety_h
|
||||
#define mozilla_ThreadSafety_h
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#if defined(__clang__) && (!defined(SWIG))
|
||||
# define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
// Allow for localized suppression of thread-safety warnings; finer-grained
|
||||
// than NO_THREAD_SAFETY_ANALYSIS
|
||||
# define PUSH_IGNORE_THREAD_SAFETY \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wthread-safety\"")
|
||||
# define POP_THREAD_SAFETY _Pragma("GCC diagnostic pop")
|
||||
|
||||
#else
|
||||
# define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||
# define PUSH_IGNORE_THREAD_SAFETY
|
||||
# define POP_THREAD_SAFETY
|
||||
#endif
|
||||
|
||||
// Document if a shared variable/field needs to be protected by a lock.
|
||||
// GUARDED_BY allows the user to specify a particular lock that should be
|
||||
// held when accessing the annotated variable, while GUARDED_VAR only
|
||||
// indicates a shared variable should be guarded (by any lock). GUARDED_VAR
|
||||
// is primarily used when the client cannot express the name of the lock.
|
||||
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded)
|
||||
|
||||
// Document if the memory location pointed to by a pointer should be guarded
|
||||
// by a lock when dereferencing the pointer. Similar to GUARDED_VAR,
|
||||
// PT_GUARDED_VAR is primarily used when the client cannot express the name
|
||||
// of the lock. Note that a pointer variable to a shared memory location
|
||||
// could itself be a shared variable. For example, if a shared global pointer
|
||||
// q, which is guarded by mu1, points to a shared memory location that is
|
||||
// guarded by mu2, q should be annotated as follows:
|
||||
// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
|
||||
#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||
#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded)
|
||||
|
||||
// Document the acquisition order between locks that can be held
|
||||
// simultaneously by a thread. For any two locks that need to be annotated
|
||||
// to establish an acquisition order, only one of them needs the annotation.
|
||||
// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
|
||||
// and ACQUIRED_BEFORE.)
|
||||
#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||
#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||
|
||||
// The following three annotations document the lock requirements for
|
||||
// functions/methods.
|
||||
|
||||
// Document if a function expects certain locks to be held before it is called
|
||||
#define REQUIRES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
|
||||
|
||||
// Document the locks acquired in the body of the function. These locks
|
||||
// cannot be held when calling this function (as google3's Mutex locks are
|
||||
// non-reentrant).
|
||||
#define EXCLUDES(x) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x))
|
||||
|
||||
// Document the lock the annotated function returns without acquiring it.
|
||||
#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
|
||||
// Document if a class/type is a lockable type (such as the Mutex class).
|
||||
#define CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(lockable)
|
||||
|
||||
// Document if a class is a scoped lockable type (such as the MutexLock class).
|
||||
#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
|
||||
// The following annotations specify lock and unlock primitives.
|
||||
#define CAPABILITY_ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
|
||||
|
||||
#define EXCLUSIVE_RELEASE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
|
||||
|
||||
#define SHARED_TRYLOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
|
||||
|
||||
#define CAPABILITY_RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
|
||||
|
||||
// An escape hatch for thread safety analysis to ignore the annotated function.
|
||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
|
||||
// Newer capabilities
|
||||
#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
|
||||
|
||||
#define ASSERT_SHARED_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
|
||||
|
||||
// Additions from current clang assertions.
|
||||
// Note: new-style definitions, since these didn't exist in the old style
|
||||
#define RELEASE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE_GENERIC(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))
|
||||
|
||||
// Mozilla additions:
|
||||
|
||||
// AutoUnlock is supported by clang currently, but oddly you must use
|
||||
// EXCLUSIVE_RELEASE() for both the RAII constructor *and* the destructor.
|
||||
// This hides the ugliness until they fix it upstream.
|
||||
#define SCOPED_UNLOCK_RELEASE(...) EXCLUSIVE_RELEASE(__VA_ARGS__)
|
||||
#define SCOPED_UNLOCK_REACQUIRE(...) EXCLUSIVE_RELEASE(__VA_ARGS__)
|
||||
|
||||
#endif /* mozilla_ThreadSafety_h */
|
@ -108,6 +108,7 @@ EXPORTS.mozilla = [
|
||||
"TemplateLib.h",
|
||||
"TextUtils.h",
|
||||
"ThreadLocal.h",
|
||||
"ThreadSafety.h",
|
||||
"ThreadSafeWeakPtr.h",
|
||||
"ToString.h",
|
||||
"Tuple.h",
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define BaseProfilerDetail_h
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/PlatformMutex.h"
|
||||
#include "mozilla/PlatformRWLock.h"
|
||||
|
@ -9,10 +9,11 @@
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/ThreadSafety.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMonitor {
|
||||
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS CAPABILITY 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
|
||||
@ -21,17 +22,20 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMonitor {
|
||||
StaticMonitor() { MOZ_ASSERT(!mMutex); }
|
||||
#endif
|
||||
|
||||
void Lock() { Mutex()->Lock(); }
|
||||
void Lock() CAPABILITY_ACQUIRE() { Mutex()->Lock(); }
|
||||
|
||||
void Unlock() { Mutex()->Unlock(); }
|
||||
void Unlock() CAPABILITY_RELEASE() { Mutex()->Unlock(); }
|
||||
|
||||
void Wait() { CondVar()->Wait(); }
|
||||
CVStatus Wait(TimeDuration aDuration) { return CondVar()->Wait(aDuration); }
|
||||
CVStatus Wait(TimeDuration aDuration) {
|
||||
AssertCurrentThreadOwns();
|
||||
return CondVar()->Wait(aDuration);
|
||||
}
|
||||
|
||||
void Notify() { CondVar()->Notify(); }
|
||||
void NotifyAll() { CondVar()->NotifyAll(); }
|
||||
|
||||
void AssertCurrentThreadOwns() {
|
||||
void AssertCurrentThreadOwns() ASSERT_CAPABILITY(this) {
|
||||
#ifdef DEBUG
|
||||
Mutex()->AssertCurrentThreadOwns();
|
||||
#endif
|
||||
@ -82,14 +86,14 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMonitor {
|
||||
static void operator delete(void*);
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS StaticMonitorAutoLock {
|
||||
class MOZ_STACK_CLASS SCOPED_CAPABILITY StaticMonitorAutoLock {
|
||||
public:
|
||||
explicit StaticMonitorAutoLock(StaticMonitor& aMonitor)
|
||||
explicit StaticMonitorAutoLock(StaticMonitor& aMonitor) CAPABILITY_ACQUIRE(aMonitor)
|
||||
: mMonitor(&aMonitor) {
|
||||
mMonitor->Lock();
|
||||
}
|
||||
|
||||
~StaticMonitorAutoLock() { mMonitor->Unlock(); }
|
||||
~StaticMonitorAutoLock() CAPABILITY_RELEASE() { mMonitor->Unlock(); }
|
||||
|
||||
void Wait() { mMonitor->Wait(); }
|
||||
CVStatus Wait(TimeDuration aDuration) { return mMonitor->Wait(aDuration); }
|
||||
|
@ -26,7 +26,7 @@ namespace mozilla {
|
||||
* initialized to 0 in order to initialize mMutex. It is only safe to use
|
||||
* StaticMutex as a global or static variable.
|
||||
*/
|
||||
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMutex {
|
||||
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS CAPABILITY StaticMutex {
|
||||
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
|
||||
@ -35,11 +35,11 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMutex {
|
||||
StaticMutex() { MOZ_ASSERT(!mMutex); }
|
||||
#endif
|
||||
|
||||
void Lock() { Mutex()->Lock(); }
|
||||
void Lock() CAPABILITY_ACQUIRE() { Mutex()->Lock(); }
|
||||
|
||||
void Unlock() { Mutex()->Unlock(); }
|
||||
void Unlock() CAPABILITY_RELEASE() { Mutex()->Unlock(); }
|
||||
|
||||
void AssertCurrentThreadOwns() {
|
||||
void AssertCurrentThreadOwns() ASSERT_CAPABILITY(this) {
|
||||
#ifdef DEBUG
|
||||
Mutex()->AssertCurrentThreadOwns();
|
||||
#endif
|
||||
|
@ -71,7 +71,7 @@ static void DisableCrashReporter() {
|
||||
// Single-threaded sanity tests
|
||||
|
||||
// Stupidest possible deadlock.
|
||||
static int Sanity_Child() {
|
||||
static int Sanity_Child() NO_THREAD_SAFETY_ANALYSIS {
|
||||
DisableCrashReporter();
|
||||
|
||||
MUTEX m1("dd.sanity.m1");
|
||||
@ -92,7 +92,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(SanityDeathTest)) {
|
||||
}
|
||||
|
||||
// Slightly less stupid deadlock.
|
||||
static int Sanity2_Child() {
|
||||
static int Sanity2_Child() NO_THREAD_SAFETY_ANALYSIS {
|
||||
DisableCrashReporter();
|
||||
|
||||
MUTEX m1("dd.sanity2.m1");
|
||||
@ -118,7 +118,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(Sanity2DeathTest)) {
|
||||
#if 0
|
||||
// Temporarily disabled, see bug 1370644.
|
||||
int
|
||||
Sanity3_Child()
|
||||
Sanity3_Child() NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
DisableCrashReporter();
|
||||
|
||||
@ -156,7 +156,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(Sanity3DeathTest))
|
||||
}
|
||||
#endif
|
||||
|
||||
static int Sanity4_Child() {
|
||||
static int Sanity4_Child() NO_THREAD_SAFETY_ANALYSIS {
|
||||
DisableCrashReporter();
|
||||
|
||||
mozilla::ReentrantMonitor m1 MOZ_UNANNOTATED("dd.sanity4.m1");
|
||||
@ -179,7 +179,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(Sanity4DeathTest)) {
|
||||
ASSERT_DEATH_IF_SUPPORTED(Sanity4_Child(), regex);
|
||||
}
|
||||
|
||||
static int Sanity5_Child() {
|
||||
static int Sanity5_Child() NO_THREAD_SAFETY_ANALYSIS {
|
||||
DisableCrashReporter();
|
||||
|
||||
mozilla::RecursiveMutex m1 MOZ_UNANNOTATED("dd.sanity4.m1");
|
||||
@ -225,7 +225,7 @@ struct ThreadState {
|
||||
#if 0
|
||||
// Temporarily disabled, see bug 1370644.
|
||||
static void
|
||||
TwoThreads_thread(void* arg)
|
||||
TwoThreads_thread(void* arg) NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
ThreadState* state = static_cast<ThreadState*>(arg);
|
||||
|
||||
@ -247,7 +247,7 @@ TwoThreads_thread(void* arg)
|
||||
}
|
||||
|
||||
int
|
||||
TwoThreads_Child()
|
||||
TwoThreads_Child() NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
DisableCrashReporter();
|
||||
|
||||
@ -284,7 +284,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(TwoThreadsDeathTest))
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ContentionNoDeadlock_thread(void* arg) {
|
||||
static void ContentionNoDeadlock_thread(void* arg) NO_THREAD_SAFETY_ANALYSIS {
|
||||
const uint32_t K = 100000;
|
||||
|
||||
ThreadState* state = static_cast<ThreadState*>(arg);
|
||||
@ -300,7 +300,7 @@ static void ContentionNoDeadlock_thread(void* arg) {
|
||||
}
|
||||
}
|
||||
|
||||
static int ContentionNoDeadlock_Child() {
|
||||
static int ContentionNoDeadlock_Child() NO_THREAD_SAFETY_ANALYSIS {
|
||||
const size_t kMutexCount = 4;
|
||||
|
||||
PRThread* threads[3];
|
||||
|
@ -15,7 +15,7 @@ using mozilla::RecursiveMutexAutoLock;
|
||||
// well, actually recursively acquirable.
|
||||
|
||||
TEST(RecursiveMutex, SmokeTest)
|
||||
{
|
||||
NO_THREAD_SAFETY_ANALYSIS {
|
||||
RecursiveMutex mutex("testing mutex");
|
||||
|
||||
RecursiveMutexAutoLock lock1(mutex);
|
||||
|
@ -21,7 +21,7 @@ static PRThread* spawn(void (*run)(void*), void* arg) {
|
||||
// Sanity check: tests that can be done on a single thread
|
||||
//
|
||||
TEST(Synchronization, Sanity)
|
||||
{
|
||||
NO_THREAD_SAFETY_ANALYSIS {
|
||||
Mutex lock("sanity::lock");
|
||||
lock.Lock();
|
||||
lock.AssertCurrentThreadOwns();
|
||||
@ -112,7 +112,7 @@ TEST(Synchronization, MonitorContention)
|
||||
|
||||
static ReentrantMonitor* gMon2;
|
||||
|
||||
static void MonitorContention2_thread(void* /*arg*/) {
|
||||
static void MonitorContention2_thread(void* /*arg*/) NO_THREAD_SAFETY_ANALYSIS {
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
gMon2->Enter();
|
||||
gMon2->AssertCurrentThreadIn();
|
||||
@ -144,7 +144,7 @@ TEST(Synchronization, MonitorContention2)
|
||||
static ReentrantMonitor* gMon3;
|
||||
static int32_t gMonFirst;
|
||||
|
||||
static void MonitorSyncSanity_thread(void* /*arg*/) {
|
||||
static void MonitorSyncSanity_thread(void* /*arg*/) NO_THREAD_SAFETY_ANALYSIS {
|
||||
gMon3->Enter();
|
||||
gMon3->AssertCurrentThreadIn();
|
||||
if (gMonFirst) {
|
||||
@ -294,7 +294,7 @@ TEST(Synchronization, AutoUnlock)
|
||||
// AutoMonitor tests
|
||||
//
|
||||
TEST(Synchronization, AutoMonitor)
|
||||
{
|
||||
NO_THREAD_SAFETY_ANALYSIS {
|
||||
ReentrantMonitor m1("automonitor");
|
||||
ReentrantMonitor m2("automonitor2");
|
||||
|
||||
|
@ -502,7 +502,7 @@ void RecursiveMutex::Unlock() {
|
||||
UnlockInternal();
|
||||
}
|
||||
|
||||
void RecursiveMutex::AssertCurrentThreadIn() {
|
||||
void RecursiveMutex::AssertCurrentThreadIn() const {
|
||||
MOZ_ASSERT(IsAcquired() && mOwningThread == PR_GetCurrentThread());
|
||||
}
|
||||
|
||||
|
@ -87,19 +87,21 @@ class OffTheBooksCondVar : BlockingResourceBase {
|
||||
* AssertCurrentThreadOwnsMutex
|
||||
* @see Mutex::AssertCurrentThreadOwns
|
||||
**/
|
||||
void AssertCurrentThreadOwnsMutex() { mLock->AssertCurrentThreadOwns(); }
|
||||
void AssertCurrentThreadOwnsMutex() const ASSERT_CAPABILITY(mLock) {
|
||||
mLock->AssertCurrentThreadOwns();
|
||||
}
|
||||
|
||||
/**
|
||||
* AssertNotCurrentThreadOwnsMutex
|
||||
* @see Mutex::AssertNotCurrentThreadOwns
|
||||
**/
|
||||
void AssertNotCurrentThreadOwnsMutex() {
|
||||
void AssertNotCurrentThreadOwnsMutex() const ASSERT_CAPABILITY(!mLock) {
|
||||
mLock->AssertNotCurrentThreadOwns();
|
||||
}
|
||||
|
||||
#else
|
||||
void AssertCurrentThreadOwnsMutex() {}
|
||||
void AssertNotCurrentThreadOwnsMutex() {}
|
||||
void AssertCurrentThreadOwnsMutex() const ASSERT_CAPABILITY(mLock) {}
|
||||
void AssertNotCurrentThreadOwnsMutex() const ASSERT_CAPABILITY(!mLock) {}
|
||||
|
||||
#endif // ifdef DEBUG
|
||||
|
||||
|
@ -21,16 +21,16 @@ namespace mozilla {
|
||||
* to instead use the RAII wrappers MonitorAutoLock and
|
||||
* MonitorAutoUnlock.
|
||||
*/
|
||||
class Monitor {
|
||||
class CAPABILITY Monitor {
|
||||
public:
|
||||
explicit Monitor(const char* aName)
|
||||
: mMutex(aName), mCondVar(mMutex, "[Monitor.mCondVar]") {}
|
||||
|
||||
~Monitor() = default;
|
||||
|
||||
void Lock() { mMutex.Lock(); }
|
||||
[[nodiscard]] bool TryLock() { return mMutex.TryLock(); }
|
||||
void Unlock() { mMutex.Unlock(); }
|
||||
void Lock() CAPABILITY_ACQUIRE() { mMutex.Lock(); }
|
||||
[[nodiscard]] bool TryLock() TRY_ACQUIRE(true) { return mMutex.TryLock(); }
|
||||
void Unlock() CAPABILITY_RELEASE() { mMutex.Unlock(); }
|
||||
|
||||
void Wait() { mCondVar.Wait(); }
|
||||
CVStatus Wait(TimeDuration aDuration) { return mCondVar.Wait(aDuration); }
|
||||
@ -38,21 +38,77 @@ class Monitor {
|
||||
void Notify() { mCondVar.Notify(); }
|
||||
void NotifyAll() { mCondVar.NotifyAll(); }
|
||||
|
||||
void AssertCurrentThreadOwns() const { mMutex.AssertCurrentThreadOwns(); }
|
||||
|
||||
void AssertNotCurrentThreadOwns() const {
|
||||
void AssertCurrentThreadOwns() const ASSERT_CAPABILITY(this) {
|
||||
mMutex.AssertCurrentThreadOwns();
|
||||
}
|
||||
void AssertNotCurrentThreadOwns() const ASSERT_CAPABILITY(!this) {
|
||||
mMutex.AssertNotCurrentThreadOwns();
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor();
|
||||
Monitor(const Monitor&);
|
||||
Monitor& operator=(const Monitor&);
|
||||
Monitor() = delete;
|
||||
Monitor(const Monitor&) = delete;
|
||||
Monitor& operator=(const Monitor&) = delete;
|
||||
|
||||
Mutex mMutex;
|
||||
CondVar mCondVar;
|
||||
};
|
||||
|
||||
/**
|
||||
* MonitorSingleWriter
|
||||
*
|
||||
* Monitor where a single writer exists, so that reads from the same thread
|
||||
* will not generate data races or consistency issues.
|
||||
*
|
||||
* When possible, use MonitorAutoLock/MonitorAutoUnlock to lock/unlock this
|
||||
* monitor within a scope, instead of calling Lock/Unlock directly.
|
||||
*
|
||||
* This requires an object implementing Mutex's SingleWriterLockOwner, so
|
||||
* we can do correct-thread checks.
|
||||
*/
|
||||
|
||||
class MonitorSingleWriter : public Monitor {
|
||||
public:
|
||||
// aOwner should be the object that contains the mutex, typically. We
|
||||
// will use that object (which must have a lifetime the same or greater
|
||||
// than this object) to verify that we're running on the correct thread,
|
||||
// typically only in DEBUG builds
|
||||
explicit MonitorSingleWriter(const char* aName, SingleWriterLockOwner* aOwner)
|
||||
: Monitor(aName)
|
||||
#ifdef DEBUG
|
||||
,
|
||||
mOwner(aOwner)
|
||||
#endif
|
||||
{
|
||||
MOZ_COUNT_CTOR(MonitorSingleWriter);
|
||||
MOZ_ASSERT(mOwner);
|
||||
}
|
||||
|
||||
MOZ_COUNTED_DTOR(MonitorSingleWriter)
|
||||
|
||||
void AssertOnWritingThread() const ASSERT_CAPABILITY(this) {
|
||||
MOZ_ASSERT(mOwner->OnWritingThread());
|
||||
}
|
||||
void AssertOnWritingThreadOrHeld() const ASSERT_CAPABILITY(this) {
|
||||
#ifdef DEBUG
|
||||
if (!mOwner->OnWritingThread()) {
|
||||
AssertCurrentThreadOwns();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
SingleWriterLockOwner* mOwner MOZ_UNSAFE_REF(
|
||||
"This is normally the object that contains the MonitorSingleWriter, so "
|
||||
"we don't want to hold a reference to ourselves");
|
||||
#endif
|
||||
|
||||
MonitorSingleWriter() = delete;
|
||||
MonitorSingleWriter(const MonitorSingleWriter&) = delete;
|
||||
MonitorSingleWriter& operator=(const MonitorSingleWriter&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lock the monitor for the lexical scope instances of this class are
|
||||
* bound to (except for MonitorAutoUnlock in nested scopes).
|
||||
@ -60,30 +116,79 @@ class Monitor {
|
||||
* The monitor must be unlocked when instances of this class are
|
||||
* created.
|
||||
*/
|
||||
class MOZ_STACK_CLASS MonitorAutoLock {
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
class SCOPED_CAPABILITY MOZ_STACK_CLASS BaseMonitorAutoLock {
|
||||
public:
|
||||
explicit MonitorAutoLock(Monitor& aMonitor) : mMonitor(&aMonitor) {
|
||||
explicit BaseMonitorAutoLock(T& aMonitor) CAPABILITY_ACQUIRE(aMonitor)
|
||||
: mMonitor(&aMonitor) {
|
||||
mMonitor->Lock();
|
||||
}
|
||||
|
||||
~MonitorAutoLock() { mMonitor->Unlock(); }
|
||||
|
||||
~BaseMonitorAutoLock() CAPABILITY_RELEASE() { mMonitor->Unlock(); }
|
||||
void Wait() { mMonitor->Wait(); }
|
||||
CVStatus Wait(TimeDuration aDuration) { return mMonitor->Wait(aDuration); }
|
||||
|
||||
void Notify() { mMonitor->Notify(); }
|
||||
void NotifyAll() { mMonitor->NotifyAll(); }
|
||||
|
||||
// Assert that aLock is the monitor passed to the constructor and that the
|
||||
// current thread owns the monitor. In coding patterns such as:
|
||||
//
|
||||
// void LockedMethod(const BaseAutoLock<T>& aProofOfLock)
|
||||
// {
|
||||
// aProofOfLock.AssertOwns(mMonitor);
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// Without this assertion, it could be that mMonitor is not actually
|
||||
// locked. It's possible to have code like:
|
||||
//
|
||||
// BaseAutoLock lock(someMonitor);
|
||||
// ...
|
||||
// BaseAutoUnlock unlock(someMonitor);
|
||||
// ...
|
||||
// LockedMethod(lock);
|
||||
//
|
||||
// and in such a case, simply asserting that the monitor pointers match is not
|
||||
// sufficient; monitor ownership must be asserted as well.
|
||||
//
|
||||
// Note that if you are going to use the coding pattern presented above, you
|
||||
// should use this method in preference to using AssertCurrentThreadOwns on
|
||||
// the mutex you expected to be held, since this method provides stronger
|
||||
// guarantees.
|
||||
void AssertOwns(const T& aMonitor) const ASSERT_CAPABILITY(aMonitor) {
|
||||
MOZ_ASSERT(&aMonitor == mMonitor);
|
||||
mMonitor->AssertCurrentThreadOwns();
|
||||
}
|
||||
|
||||
private:
|
||||
MonitorAutoLock();
|
||||
MonitorAutoLock(const MonitorAutoLock&);
|
||||
MonitorAutoLock& operator=(const MonitorAutoLock&);
|
||||
BaseMonitorAutoLock() = delete;
|
||||
BaseMonitorAutoLock(const BaseMonitorAutoLock&) = delete;
|
||||
BaseMonitorAutoLock& operator=(const BaseMonitorAutoLock&) = delete;
|
||||
static void* operator new(size_t) noexcept(true);
|
||||
|
||||
friend class MonitorAutoUnlock;
|
||||
|
||||
Monitor* mMonitor;
|
||||
protected:
|
||||
T* mMonitor;
|
||||
};
|
||||
} // namespace detail
|
||||
typedef detail::BaseMonitorAutoLock<Monitor> MonitorAutoLock;
|
||||
typedef detail::BaseMonitorAutoLock<MonitorSingleWriter>
|
||||
MonitorSingleWriterAutoLock;
|
||||
|
||||
// clang-format off
|
||||
// Use if we've done AssertOnWritingThread(), and then later need to take the
|
||||
// lock to write to a protected member. Instead of
|
||||
// MutexSingleWriterAutoLock lock(mutex)
|
||||
// use
|
||||
// MutexSingleWriterAutoLockOnThread(lock, mutex)
|
||||
// clang-format on
|
||||
#define MonitorSingleWriterAutoLockOnThread(lock, monitor) \
|
||||
PUSH_IGNORE_THREAD_SAFETY \
|
||||
MonitorSingleWriterAutoLock lock(monitor); \
|
||||
POP_THREAD_SAFETY
|
||||
|
||||
/**
|
||||
* Unlock the monitor for the lexical scope instances of this class
|
||||
@ -92,26 +197,99 @@ class MOZ_STACK_CLASS MonitorAutoLock {
|
||||
* The monitor must be locked by the current thread when instances of
|
||||
* this class are created.
|
||||
*/
|
||||
class MOZ_STACK_CLASS MonitorAutoUnlock {
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
class MOZ_STACK_CLASS SCOPED_CAPABILITY BaseMonitorAutoUnlock {
|
||||
public:
|
||||
explicit MonitorAutoUnlock(Monitor& aMonitor) : mMonitor(&aMonitor) {
|
||||
explicit BaseMonitorAutoUnlock(T& aMonitor) SCOPED_UNLOCK_RELEASE(aMonitor)
|
||||
: mMonitor(&aMonitor) {
|
||||
mMonitor->Unlock();
|
||||
}
|
||||
|
||||
explicit MonitorAutoUnlock(MonitorAutoLock& aMonitorLock)
|
||||
: mMonitor(aMonitorLock.mMonitor) {
|
||||
mMonitor->Unlock();
|
||||
}
|
||||
|
||||
~MonitorAutoUnlock() { mMonitor->Lock(); }
|
||||
~BaseMonitorAutoUnlock() SCOPED_UNLOCK_REACQUIRE() { mMonitor->Lock(); }
|
||||
|
||||
private:
|
||||
MonitorAutoUnlock();
|
||||
MonitorAutoUnlock(const MonitorAutoUnlock&);
|
||||
MonitorAutoUnlock& operator=(const MonitorAutoUnlock&);
|
||||
BaseMonitorAutoUnlock() = delete;
|
||||
BaseMonitorAutoUnlock(const BaseMonitorAutoUnlock&) = delete;
|
||||
BaseMonitorAutoUnlock& operator=(const BaseMonitorAutoUnlock&) = delete;
|
||||
static void* operator new(size_t) noexcept(true);
|
||||
|
||||
T* mMonitor;
|
||||
};
|
||||
} // namespace detail
|
||||
typedef detail::BaseMonitorAutoUnlock<Monitor> MonitorAutoUnlock;
|
||||
typedef detail::BaseMonitorAutoUnlock<MonitorSingleWriter>
|
||||
MonitorSingleWriterAutoUnlock;
|
||||
|
||||
/**
|
||||
* Lock the monitor for the lexical scope instances of this class are
|
||||
* bound to (except for MonitorAutoUnlock in nested scopes).
|
||||
*
|
||||
* The monitor must be unlocked when instances of this class are
|
||||
* created.
|
||||
*/
|
||||
class SCOPED_CAPABILITY MOZ_STACK_CLASS ReleaseableMonitorAutoLock {
|
||||
public:
|
||||
explicit ReleaseableMonitorAutoLock(Monitor& aMonitor) CAPABILITY_ACQUIRE(aMonitor)
|
||||
: mMonitor(&aMonitor) {
|
||||
mMonitor->Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
|
||||
~ReleaseableMonitorAutoLock() CAPABILITY_RELEASE() {
|
||||
if (mLocked) {
|
||||
mMonitor->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Wait() { mMonitor->Wait(); }
|
||||
CVStatus Wait(TimeDuration aDuration) {
|
||||
MOZ_ASSERT(mLocked);
|
||||
return mMonitor->Wait(aDuration);
|
||||
}
|
||||
|
||||
void Notify() {
|
||||
MOZ_ASSERT(mLocked);
|
||||
mMonitor->Notify();
|
||||
}
|
||||
void NotifyAll() {
|
||||
MOZ_ASSERT(mLocked);
|
||||
mMonitor->NotifyAll();
|
||||
}
|
||||
|
||||
// Allow dropping the lock prematurely; for example to support something like:
|
||||
// clang-format off
|
||||
// MonitorAutoLock lock(mMonitor);
|
||||
// ...
|
||||
// if (foo) {
|
||||
// lock.Unlock();
|
||||
// MethodThatCantBeCalledWithLock()
|
||||
// return;
|
||||
// }
|
||||
// clang-format on
|
||||
void Unlock() CAPABILITY_RELEASE() {
|
||||
MOZ_ASSERT(mLocked);
|
||||
mMonitor->Unlock();
|
||||
mLocked = false;
|
||||
}
|
||||
void Lock() CAPABILITY_ACQUIRE() {
|
||||
MOZ_ASSERT(!mLocked);
|
||||
mMonitor->Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
void AssertCurrentThreadOwns() const ASSERT_CAPABILITY() {
|
||||
mMonitor->AssertCurrentThreadOwns();
|
||||
}
|
||||
|
||||
private:
|
||||
bool mLocked;
|
||||
Monitor* mMonitor;
|
||||
|
||||
ReleaseableMonitorAutoLock() = delete;
|
||||
ReleaseableMonitorAutoLock(const ReleaseableMonitorAutoLock&) = delete;
|
||||
ReleaseableMonitorAutoLock& operator=(const ReleaseableMonitorAutoLock&) =
|
||||
delete;
|
||||
static void* operator new(size_t) noexcept(true);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_Mutex_h
|
||||
|
||||
#include "mozilla/BlockingResourceBase.h"
|
||||
#include "mozilla/ThreadSafety.h"
|
||||
#include "mozilla/PlatformMutex.h"
|
||||
#include "nsISupports.h"
|
||||
|
||||
@ -33,7 +34,8 @@ namespace mozilla {
|
||||
* 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 {
|
||||
class CAPABILITY OffTheBooksMutex : public detail::MutexImpl,
|
||||
BlockingResourceBase {
|
||||
public:
|
||||
/**
|
||||
* @param aName A name which can reference this lock
|
||||
@ -60,24 +62,24 @@ class OffTheBooksMutex : public detail::MutexImpl, BlockingResourceBase {
|
||||
/**
|
||||
* Lock this mutex.
|
||||
**/
|
||||
void Lock() { this->lock(); }
|
||||
void Lock() CAPABILITY_ACQUIRE() { this->lock(); }
|
||||
|
||||
/**
|
||||
* Try to lock this mutex, returning true if we were successful.
|
||||
**/
|
||||
[[nodiscard]] bool TryLock() { return this->tryLock(); }
|
||||
[[nodiscard]] bool TryLock() TRY_ACQUIRE(true) { return this->tryLock(); }
|
||||
|
||||
/**
|
||||
* Unlock this mutex.
|
||||
**/
|
||||
void Unlock() { this->unlock(); }
|
||||
void Unlock() CAPABILITY_RELEASE() { this->unlock(); }
|
||||
|
||||
/**
|
||||
* Assert that the current thread owns this mutex in debug builds.
|
||||
*
|
||||
* Does nothing in non-debug builds.
|
||||
**/
|
||||
void AssertCurrentThreadOwns() const {}
|
||||
void AssertCurrentThreadOwns() const ASSERT_CAPABILITY(this) {}
|
||||
|
||||
/**
|
||||
* Assert that the current thread does not own this mutex.
|
||||
@ -87,25 +89,24 @@ class OffTheBooksMutex : public detail::MutexImpl, BlockingResourceBase {
|
||||
*
|
||||
* It is therefore mostly useful as documentation.
|
||||
**/
|
||||
void AssertNotCurrentThreadOwns() const {}
|
||||
void AssertNotCurrentThreadOwns() const ASSERT_CAPABILITY(!this) {}
|
||||
|
||||
#else
|
||||
void Lock();
|
||||
[[nodiscard]] bool TryLock();
|
||||
void Unlock();
|
||||
void Lock() CAPABILITY_ACQUIRE();
|
||||
|
||||
void AssertCurrentThreadOwns() const;
|
||||
[[nodiscard]] bool TryLock() TRY_ACQUIRE(true);
|
||||
void Unlock() CAPABILITY_RELEASE();
|
||||
|
||||
void AssertNotCurrentThreadOwns() const {
|
||||
void AssertCurrentThreadOwns() const ASSERT_CAPABILITY(this);
|
||||
void AssertNotCurrentThreadOwns() const ASSERT_CAPABILITY(!this) {
|
||||
// FIXME bug 476536
|
||||
}
|
||||
|
||||
#endif // ifndef DEBUG
|
||||
|
||||
private:
|
||||
OffTheBooksMutex();
|
||||
OffTheBooksMutex(const OffTheBooksMutex&);
|
||||
OffTheBooksMutex& operator=(const OffTheBooksMutex&);
|
||||
OffTheBooksMutex() = delete;
|
||||
OffTheBooksMutex(const OffTheBooksMutex&) = delete;
|
||||
OffTheBooksMutex& operator=(const OffTheBooksMutex&) = delete;
|
||||
|
||||
friend class OffTheBooksCondVar;
|
||||
|
||||
@ -128,9 +129,85 @@ class Mutex : public OffTheBooksMutex {
|
||||
MOZ_COUNTED_DTOR(Mutex)
|
||||
|
||||
private:
|
||||
Mutex();
|
||||
Mutex(const Mutex&);
|
||||
Mutex& operator=(const Mutex&);
|
||||
Mutex() = delete;
|
||||
Mutex(const Mutex&) = delete;
|
||||
Mutex& operator=(const Mutex&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* MutexSingleWriter
|
||||
*
|
||||
* Mutex where a single writer exists, so that reads from the same thread
|
||||
* will not generate data races or consistency issues.
|
||||
*
|
||||
* When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
|
||||
* mutex within a scope, instead of calling Lock/Unlock directly.
|
||||
*
|
||||
* This requires an object implementing Mutex's SingleWriterLockOwner, so
|
||||
* we can do correct-thread checks.
|
||||
*/
|
||||
// Subclass this in the object owning the mutex
|
||||
class SingleWriterLockOwner {
|
||||
public:
|
||||
SingleWriterLockOwner() = default;
|
||||
~SingleWriterLockOwner() = default;
|
||||
|
||||
virtual bool OnWritingThread() const = 0;
|
||||
};
|
||||
|
||||
class MutexSingleWriter : public OffTheBooksMutex {
|
||||
public:
|
||||
// aOwner should be the object that contains the mutex, typically. We
|
||||
// will use that object (which must have a lifetime the same or greater
|
||||
// than this object) to verify that we're running on the correct thread,
|
||||
// typically only in DEBUG builds
|
||||
explicit MutexSingleWriter(const char* aName, SingleWriterLockOwner* aOwner)
|
||||
: OffTheBooksMutex(aName)
|
||||
#ifdef DEBUG
|
||||
,
|
||||
mOwner(aOwner)
|
||||
#endif
|
||||
{
|
||||
MOZ_COUNT_CTOR(MutexSingleWriter);
|
||||
MOZ_ASSERT(mOwner);
|
||||
}
|
||||
|
||||
MOZ_COUNTED_DTOR(MutexSingleWriter)
|
||||
|
||||
/**
|
||||
* Statically assert that we're on the only thread that modifies data
|
||||
* guarded by this Mutex. This allows static checking for the pattern of
|
||||
* having a single thread modify a set of data, and read it (under lock)
|
||||
* on other threads, and reads on the thread that modifies it doesn't
|
||||
* require a lock. This doesn't solve the issue of some data under the
|
||||
* Mutex following this pattern, and other data under the mutex being
|
||||
* written from multiple threads.
|
||||
*
|
||||
* We could set the writing thread and dynamically check it in debug
|
||||
* builds, but this doesn't. We could also use thread-safety/capability
|
||||
* system to provide direct thread assertions.
|
||||
**/
|
||||
void AssertOnWritingThread() const ASSERT_CAPABILITY(this) {
|
||||
MOZ_ASSERT(mOwner->OnWritingThread());
|
||||
}
|
||||
void AssertOnWritingThreadOrHeld() const ASSERT_CAPABILITY(this) {
|
||||
#ifdef DEBUG
|
||||
if (!mOwner->OnWritingThread()) {
|
||||
AssertCurrentThreadOwns();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
SingleWriterLockOwner* mOwner MOZ_UNSAFE_REF(
|
||||
"This is normally the object that contains the MonitorSingleWriter, so "
|
||||
"we don't want to hold a reference to ourselves");
|
||||
#endif
|
||||
|
||||
MutexSingleWriter() = delete;
|
||||
MutexSingleWriter(const MutexSingleWriter&) = delete;
|
||||
MutexSingleWriter& operator=(const MutexSingleWriter&) = delete;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
@ -145,7 +222,7 @@ class MOZ_RAII BaseAutoUnlock;
|
||||
* MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
|
||||
*/
|
||||
template <typename T>
|
||||
class MOZ_RAII BaseAutoLock {
|
||||
class MOZ_RAII SCOPED_CAPABILITY BaseAutoLock {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
@ -155,9 +232,9 @@ class MOZ_RAII BaseAutoLock {
|
||||
* @param aLock A valid mozilla::Mutex* returned by
|
||||
* mozilla::Mutex::NewMutex.
|
||||
**/
|
||||
explicit BaseAutoLock(T aLock) : mLock(aLock) { mLock.Lock(); }
|
||||
explicit BaseAutoLock(T aLock) CAPABILITY_ACQUIRE(aLock) : mLock(aLock) { mLock.Lock(); }
|
||||
|
||||
~BaseAutoLock(void) { mLock.Unlock(); }
|
||||
~BaseAutoLock(void) CAPABILITY_RELEASE() { mLock.Unlock(); }
|
||||
|
||||
// Assert that aLock is the mutex passed to the constructor and that the
|
||||
// current thread owns the mutex. In coding patterns such as:
|
||||
@ -184,15 +261,15 @@ class MOZ_RAII BaseAutoLock {
|
||||
// should use this method in preference to using AssertCurrentThreadOwns on
|
||||
// the mutex you expected to be held, since this method provides stronger
|
||||
// guarantees.
|
||||
void AssertOwns(const T& aMutex) const {
|
||||
void AssertOwns(const T& aMutex) const ASSERT_CAPABILITY(aMutex) {
|
||||
MOZ_ASSERT(&aMutex == &mLock);
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
}
|
||||
|
||||
private:
|
||||
BaseAutoLock();
|
||||
BaseAutoLock(BaseAutoLock&);
|
||||
BaseAutoLock& operator=(BaseAutoLock&);
|
||||
BaseAutoLock() = delete;
|
||||
BaseAutoLock(BaseAutoLock&) = delete;
|
||||
BaseAutoLock& operator=(BaseAutoLock&) = delete;
|
||||
static void* operator new(size_t) noexcept(true);
|
||||
|
||||
friend class BaseAutoUnlock<T>;
|
||||
@ -205,8 +282,90 @@ BaseAutoLock(MutexType&) -> BaseAutoLock<MutexType&>;
|
||||
} // namespace detail
|
||||
|
||||
typedef detail::BaseAutoLock<Mutex&> MutexAutoLock;
|
||||
typedef detail::BaseAutoLock<MutexSingleWriter&> MutexSingleWriterAutoLock;
|
||||
typedef detail::BaseAutoLock<OffTheBooksMutex&> OffTheBooksMutexAutoLock;
|
||||
|
||||
// Use if we've done AssertOnWritingThread(), and then later need to take the
|
||||
// lock to write to a protected member. Instead of
|
||||
// MutexSingleWriterAutoLock lock(mutex)
|
||||
// use
|
||||
// MutexSingleWriterAutoLockOnThread(lock, mutex)
|
||||
#define MutexSingleWriterAutoLockOnThread(lock, mutex) \
|
||||
PUSH_IGNORE_THREAD_SAFETY \
|
||||
MutexSingleWriterAutoLock lock(mutex); \
|
||||
POP_THREAD_SAFETY
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* ReleaseableMutexAutoLock
|
||||
* Acquires the Mutex when it enters scope, and releases it when it leaves
|
||||
* scope. Allows calling Unlock (and Lock) as an alternative to
|
||||
* MutexAutoUnlock; this can avoid an extra lock/unlock pair.
|
||||
*
|
||||
*/
|
||||
template <typename T>
|
||||
class MOZ_RAII SCOPED_CAPABILITY ReleaseableBaseAutoLock {
|
||||
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 ReleaseableBaseAutoLock(T aLock) CAPABILITY_ACQUIRE(aLock)
|
||||
: BaseAutoLock<T>(aLock) {
|
||||
mLock.Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
|
||||
~ReleaseableBaseAutoLock(void) CAPABILITY_RELEASE() {
|
||||
if (!mLocked) {
|
||||
mLock.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void AssertOwns(const T& aMutex) const ASSERT_CAPABILITY(mLock) {
|
||||
MOZ_ASSERT(&aMutex == &mLock);
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
}
|
||||
|
||||
// Allow dropping the lock prematurely; for example to support something like:
|
||||
// clang-format off
|
||||
// MutexAutoLock lock(mMutex);
|
||||
// ...
|
||||
// if (foo) {
|
||||
// lock.Unlock();
|
||||
// MethodThatCantBeCalledWithLock()
|
||||
// return;
|
||||
// }
|
||||
// clang-format on
|
||||
void Unlock() CAPABILITY_RELEASE() {
|
||||
mLock.Unlock();
|
||||
mLocked = false;
|
||||
}
|
||||
void Lock() CAPABILITY_ACQUIRE() {
|
||||
mLock.Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
|
||||
private:
|
||||
ReleaseableBaseAutoLock() = delete;
|
||||
ReleaseableBaseAutoLock(ReleaseableBaseAutoLock&) = delete;
|
||||
ReleaseableBaseAutoLock& operator=(ReleaseableBaseAutoLock&) = delete;
|
||||
static void* operator new(size_t) noexcept(true);
|
||||
|
||||
bool mLocked;
|
||||
T mLock;
|
||||
};
|
||||
|
||||
template <typename MutexType>
|
||||
ReleaseableBaseAutoLock(MutexType&) -> ReleaseableBaseAutoLock<MutexType&>;
|
||||
} // namespace detail
|
||||
|
||||
typedef detail::ReleaseableBaseAutoLock<Mutex&> ReleaseableMutexAutoLock;
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* BaseAutoUnlock
|
||||
@ -216,21 +375,25 @@ namespace detail {
|
||||
* MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
|
||||
*/
|
||||
template <typename T>
|
||||
class MOZ_RAII BaseAutoUnlock {
|
||||
class MOZ_RAII SCOPED_CAPABILITY BaseAutoUnlock {
|
||||
public:
|
||||
explicit BaseAutoUnlock(T aLock) : mLock(aLock) { mLock.Unlock(); }
|
||||
explicit BaseAutoUnlock(T aLock) SCOPED_UNLOCK_RELEASE(aLock) : mLock(aLock) {
|
||||
mLock.Unlock();
|
||||
}
|
||||
|
||||
explicit BaseAutoUnlock(BaseAutoLock<T>& aAutoLock) : mLock(aAutoLock.mLock) {
|
||||
explicit BaseAutoUnlock(BaseAutoLock<T>& aAutoLock)
|
||||
/* CAPABILITY_RELEASE(aAutoLock.mLock) */
|
||||
: mLock(aAutoLock.mLock) {
|
||||
NS_ASSERTION(mLock, "null lock");
|
||||
mLock->Unlock();
|
||||
}
|
||||
|
||||
~BaseAutoUnlock() { mLock.Lock(); }
|
||||
~BaseAutoUnlock() SCOPED_UNLOCK_REACQUIRE() { mLock.Lock(); }
|
||||
|
||||
private:
|
||||
BaseAutoUnlock();
|
||||
BaseAutoUnlock(BaseAutoUnlock&);
|
||||
BaseAutoUnlock& operator=(BaseAutoUnlock&);
|
||||
BaseAutoUnlock() = delete;
|
||||
BaseAutoUnlock(BaseAutoUnlock&) = delete;
|
||||
BaseAutoUnlock& operator=(BaseAutoUnlock&) = delete;
|
||||
static void* operator new(size_t) noexcept(true);
|
||||
|
||||
T mLock;
|
||||
@ -241,6 +404,7 @@ BaseAutoUnlock(MutexType&) -> BaseAutoUnlock<MutexType&>;
|
||||
} // namespace detail
|
||||
|
||||
typedef detail::BaseAutoUnlock<Mutex&> MutexAutoUnlock;
|
||||
typedef detail::BaseAutoUnlock<MutexSingleWriter&> MutexSingleWriterAutoUnlock;
|
||||
typedef detail::BaseAutoUnlock<OffTheBooksMutex&> OffTheBooksMutexAutoUnlock;
|
||||
|
||||
namespace detail {
|
||||
@ -252,21 +416,21 @@ namespace detail {
|
||||
* MUCH PREFERRED to bare calls to Mutex.TryLock and Unlock.
|
||||
*/
|
||||
template <typename T>
|
||||
class MOZ_RAII BaseAutoTryLock {
|
||||
class MOZ_RAII SCOPED_CAPABILITY BaseAutoTryLock {
|
||||
public:
|
||||
explicit BaseAutoTryLock(T& aLock)
|
||||
explicit BaseAutoTryLock(T& aLock) CAPABILITY_ACQUIRE(aLock)
|
||||
: mLock(aLock.TryLock() ? &aLock : nullptr) {}
|
||||
|
||||
~BaseAutoTryLock() {
|
||||
~BaseAutoTryLock() CAPABILITY_RELEASE() {
|
||||
if (mLock) {
|
||||
mLock->Unlock();
|
||||
mLock = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const { return mLock; }
|
||||
|
||||
private:
|
||||
BaseAutoTryLock() = delete;
|
||||
BaseAutoTryLock(BaseAutoTryLock&) = delete;
|
||||
BaseAutoTryLock& operator=(BaseAutoTryLock&) = delete;
|
||||
static void* operator new(size_t) noexcept(true);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/BlockingResourceBase.h"
|
||||
#include "mozilla/PlatformRWLock.h"
|
||||
#include "mozilla/ThreadSafety.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -38,27 +39,30 @@ namespace mozilla {
|
||||
//
|
||||
// It is unspecified whether RWLock gives priority to waiting readers or
|
||||
// a waiting writer when unlocking.
|
||||
class RWLock : public detail::RWLockImpl, public BlockingResourceBase {
|
||||
class CAPABILITY RWLock : public detail::RWLockImpl,
|
||||
public BlockingResourceBase {
|
||||
public:
|
||||
explicit RWLock(const char* aName);
|
||||
|
||||
#ifdef DEBUG
|
||||
bool LockedForWritingByCurrentThread();
|
||||
[[nodiscard]] bool TryReadLock();
|
||||
void ReadLock();
|
||||
void ReadUnlock();
|
||||
[[nodiscard]] bool TryWriteLock();
|
||||
void WriteLock();
|
||||
void WriteUnlock();
|
||||
[[nodiscard]] bool TryReadLock() SHARED_TRYLOCK_FUNCTION(true);
|
||||
void ReadLock() ACQUIRE_SHARED();
|
||||
void ReadUnlock() RELEASE_SHARED();
|
||||
[[nodiscard]] bool TryWriteLock() TRY_ACQUIRE(true);
|
||||
void WriteLock() CAPABILITY_ACQUIRE();
|
||||
void WriteUnlock() EXCLUSIVE_RELEASE();
|
||||
#else
|
||||
[[nodiscard]] bool TryReadLock() { return detail::RWLockImpl::tryReadLock(); }
|
||||
void ReadLock() { detail::RWLockImpl::readLock(); }
|
||||
void ReadUnlock() { detail::RWLockImpl::readUnlock(); }
|
||||
[[nodiscard]] bool TryWriteLock() {
|
||||
[[nodiscard]] bool TryReadLock() SHARED_TRYLOCK_FUNCTION(true) {
|
||||
return detail::RWLockImpl::tryReadLock();
|
||||
}
|
||||
void ReadLock() ACQUIRE_SHARED() { detail::RWLockImpl::readLock(); }
|
||||
void ReadUnlock() RELEASE_SHARED() { detail::RWLockImpl::readUnlock(); }
|
||||
[[nodiscard]] bool TryWriteLock() TRY_ACQUIRE(true) {
|
||||
return detail::RWLockImpl::tryWriteLock();
|
||||
}
|
||||
void WriteLock() { detail::RWLockImpl::writeLock(); }
|
||||
void WriteUnlock() { detail::RWLockImpl::writeUnlock(); }
|
||||
void WriteLock() CAPABILITY_ACQUIRE() { detail::RWLockImpl::writeLock(); }
|
||||
void WriteUnlock() EXCLUSIVE_RELEASE() { detail::RWLockImpl::writeUnlock(); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
@ -72,6 +76,7 @@ class RWLock : public detail::RWLockImpl, public BlockingResourceBase {
|
||||
#endif
|
||||
};
|
||||
|
||||
// We only use this once; not sure we can add thread safety attributions here
|
||||
template <typename T>
|
||||
class MOZ_RAII BaseAutoTryReadLock {
|
||||
public:
|
||||
@ -95,14 +100,17 @@ class MOZ_RAII BaseAutoTryReadLock {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class MOZ_RAII BaseAutoReadLock {
|
||||
class SCOPED_CAPABILITY MOZ_RAII BaseAutoReadLock {
|
||||
public:
|
||||
explicit BaseAutoReadLock(T& aLock) : mLock(&aLock) {
|
||||
explicit BaseAutoReadLock(T& aLock) ACQUIRE_SHARED(aLock) : mLock(&aLock) {
|
||||
MOZ_ASSERT(mLock, "null lock");
|
||||
mLock->ReadLock();
|
||||
}
|
||||
|
||||
~BaseAutoReadLock() { mLock->ReadUnlock(); }
|
||||
// Not RELEASE_SHARED(), which would make sense - apparently this trips
|
||||
// over a bug in clang's static analyzer and it says it expected an
|
||||
// exclusive unlock.
|
||||
~BaseAutoReadLock() RELEASE_GENERIC() { mLock->ReadUnlock(); }
|
||||
|
||||
private:
|
||||
BaseAutoReadLock() = delete;
|
||||
@ -112,6 +120,7 @@ class MOZ_RAII BaseAutoReadLock {
|
||||
T* mLock;
|
||||
};
|
||||
|
||||
// XXX Mutex attributions?
|
||||
template <typename T>
|
||||
class MOZ_RAII BaseAutoTryWriteLock {
|
||||
public:
|
||||
@ -135,14 +144,14 @@ class MOZ_RAII BaseAutoTryWriteLock {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class MOZ_RAII BaseAutoWriteLock final {
|
||||
class SCOPED_CAPABILITY MOZ_RAII BaseAutoWriteLock final {
|
||||
public:
|
||||
explicit BaseAutoWriteLock(T& aLock) : mLock(&aLock) {
|
||||
explicit BaseAutoWriteLock(T& aLock) CAPABILITY_ACQUIRE(aLock) : mLock(&aLock) {
|
||||
MOZ_ASSERT(mLock, "null lock");
|
||||
mLock->WriteLock();
|
||||
}
|
||||
|
||||
~BaseAutoWriteLock() { mLock->WriteUnlock(); }
|
||||
~BaseAutoWriteLock() CAPABILITY_RELEASE() { mLock->WriteUnlock(); }
|
||||
|
||||
private:
|
||||
BaseAutoWriteLock() = delete;
|
||||
@ -177,7 +186,7 @@ typedef BaseAutoWriteLock<RWLock> AutoWriteLock;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class StaticRWLock {
|
||||
class CAPABILITY StaticRWLock {
|
||||
public:
|
||||
// In debug builds, check that mLock is initialized for us as we expect by
|
||||
// the compiler. In non-debug builds, don't declare a constructor so that
|
||||
@ -186,15 +195,19 @@ class StaticRWLock {
|
||||
StaticRWLock() { MOZ_ASSERT(!mLock); }
|
||||
#endif
|
||||
|
||||
[[nodiscard]] bool TryReadLock() { return Lock()->TryReadLock(); }
|
||||
void ReadLock() { Lock()->ReadLock(); }
|
||||
void ReadUnlock() { Lock()->ReadUnlock(); }
|
||||
[[nodiscard]] bool TryWriteLock() { return Lock()->TryWriteLock(); }
|
||||
void WriteLock() { Lock()->WriteLock(); }
|
||||
void WriteUnlock() { Lock()->WriteUnlock(); }
|
||||
[[nodiscard]] bool TryReadLock() SHARED_TRYLOCK_FUNCTION(true) {
|
||||
return Lock()->TryReadLock();
|
||||
}
|
||||
void ReadLock() ACQUIRE_SHARED() { Lock()->ReadLock(); }
|
||||
void ReadUnlock() RELEASE_SHARED() { Lock()->ReadUnlock(); }
|
||||
[[nodiscard]] bool TryWriteLock() TRY_ACQUIRE(true) {
|
||||
return Lock()->TryWriteLock();
|
||||
}
|
||||
void WriteLock() CAPABILITY_ACQUIRE() { Lock()->WriteLock(); }
|
||||
void WriteUnlock() EXCLUSIVE_RELEASE() { Lock()->WriteUnlock(); }
|
||||
|
||||
private:
|
||||
[[nodiscard]] RWLock* Lock() {
|
||||
[[nodiscard]] RWLock* Lock() RETURN_CAPABILITY(*mLock) {
|
||||
if (mLock) {
|
||||
return mLock;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef mozilla_RecursiveMutex_h
|
||||
#define mozilla_RecursiveMutex_h
|
||||
|
||||
#include "mozilla/ThreadSafety.h"
|
||||
#include "mozilla/BlockingResourceBase.h"
|
||||
|
||||
#ifndef XP_WIN
|
||||
@ -17,33 +18,33 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class RecursiveMutex : public BlockingResourceBase {
|
||||
class CAPABILITY RecursiveMutex : public BlockingResourceBase {
|
||||
public:
|
||||
explicit RecursiveMutex(const char* aName);
|
||||
~RecursiveMutex();
|
||||
|
||||
#ifdef DEBUG
|
||||
void Lock();
|
||||
void Unlock();
|
||||
void Lock() CAPABILITY_ACQUIRE();
|
||||
void Unlock() CAPABILITY_RELEASE();
|
||||
#else
|
||||
void Lock() { LockInternal(); }
|
||||
void Unlock() { UnlockInternal(); }
|
||||
void Lock() CAPABILITY_ACQUIRE() { LockInternal(); }
|
||||
void Unlock() CAPABILITY_RELEASE() { UnlockInternal(); }
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* AssertCurrentThreadIn
|
||||
**/
|
||||
void AssertCurrentThreadIn();
|
||||
void AssertCurrentThreadIn() const ASSERT_CAPABILITY(this);
|
||||
/**
|
||||
* AssertNotCurrentThreadIn
|
||||
**/
|
||||
void AssertNotCurrentThreadIn() {
|
||||
void AssertNotCurrentThreadIn() const EXCLUDES(this) {
|
||||
// Not currently implemented. See bug 476536 for discussion.
|
||||
}
|
||||
#else
|
||||
void AssertCurrentThreadIn() {}
|
||||
void AssertNotCurrentThreadIn() {}
|
||||
void AssertCurrentThreadIn() const ASSERT_CAPABILITY(this) {}
|
||||
void AssertNotCurrentThreadIn() const EXCLUDES(this) {}
|
||||
#endif
|
||||
|
||||
private:
|
||||
@ -69,15 +70,16 @@ class RecursiveMutex : public BlockingResourceBase {
|
||||
#endif
|
||||
};
|
||||
|
||||
class MOZ_RAII RecursiveMutexAutoLock {
|
||||
class MOZ_RAII SCOPED_CAPABILITY RecursiveMutexAutoLock {
|
||||
public:
|
||||
explicit RecursiveMutexAutoLock(RecursiveMutex& aRecursiveMutex)
|
||||
CAPABILITY_ACQUIRE(aRecursiveMutex)
|
||||
: mRecursiveMutex(&aRecursiveMutex) {
|
||||
NS_ASSERTION(mRecursiveMutex, "null mutex");
|
||||
mRecursiveMutex->Lock();
|
||||
}
|
||||
|
||||
~RecursiveMutexAutoLock(void) { mRecursiveMutex->Unlock(); }
|
||||
~RecursiveMutexAutoLock(void) CAPABILITY_RELEASE() { mRecursiveMutex->Unlock(); }
|
||||
|
||||
private:
|
||||
RecursiveMutexAutoLock() = delete;
|
||||
@ -88,15 +90,18 @@ class MOZ_RAII RecursiveMutexAutoLock {
|
||||
mozilla::RecursiveMutex* mRecursiveMutex;
|
||||
};
|
||||
|
||||
class MOZ_RAII RecursiveMutexAutoUnlock {
|
||||
class MOZ_RAII SCOPED_CAPABILITY RecursiveMutexAutoUnlock {
|
||||
public:
|
||||
explicit RecursiveMutexAutoUnlock(RecursiveMutex& aRecursiveMutex)
|
||||
SCOPED_UNLOCK_RELEASE(aRecursiveMutex)
|
||||
: mRecursiveMutex(&aRecursiveMutex) {
|
||||
NS_ASSERTION(mRecursiveMutex, "null mutex");
|
||||
mRecursiveMutex->Unlock();
|
||||
}
|
||||
|
||||
~RecursiveMutexAutoUnlock(void) { mRecursiveMutex->Lock(); }
|
||||
~RecursiveMutexAutoUnlock(void) SCOPED_UNLOCK_REACQUIRE() {
|
||||
mRecursiveMutex->Lock();
|
||||
}
|
||||
|
||||
private:
|
||||
RecursiveMutexAutoUnlock() = delete;
|
||||
|
@ -14,8 +14,8 @@
|
||||
#endif // defined( MOZILLA_INTERNAL_API) && !defined(DEBUG)
|
||||
|
||||
#include "mozilla/BlockingResourceBase.h"
|
||||
#include "mozilla/ThreadSafety.h"
|
||||
#include "nsISupports.h"
|
||||
|
||||
//
|
||||
// Provides:
|
||||
//
|
||||
@ -34,7 +34,7 @@ namespace mozilla {
|
||||
* When possible, use ReentrantMonitorAutoEnter to hold this monitor within a
|
||||
* scope, instead of calling Enter/Exit directly.
|
||||
**/
|
||||
class ReentrantMonitor : BlockingResourceBase {
|
||||
class CAPABILITY ReentrantMonitor : BlockingResourceBase {
|
||||
public:
|
||||
/**
|
||||
* ReentrantMonitor
|
||||
@ -70,19 +70,20 @@ class ReentrantMonitor : BlockingResourceBase {
|
||||
* Enter
|
||||
* @see prmon.h
|
||||
**/
|
||||
void Enter() { PR_EnterMonitor(mReentrantMonitor); }
|
||||
void Enter() CAPABILITY_ACQUIRE() { PR_EnterMonitor(mReentrantMonitor); }
|
||||
|
||||
/**
|
||||
* Exit
|
||||
* @see prmon.h
|
||||
**/
|
||||
void Exit() { PR_ExitMonitor(mReentrantMonitor); }
|
||||
void Exit() CAPABILITY_RELEASE() { PR_ExitMonitor(mReentrantMonitor); }
|
||||
|
||||
/**
|
||||
* Wait
|
||||
* @see prmon.h
|
||||
**/
|
||||
nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT) {
|
||||
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mReentrantMonitor);
|
||||
# ifdef MOZILLA_INTERNAL_API
|
||||
AUTO_PROFILER_THREAD_SLEEP;
|
||||
# endif // MOZILLA_INTERNAL_API
|
||||
@ -92,8 +93,8 @@ class ReentrantMonitor : BlockingResourceBase {
|
||||
}
|
||||
|
||||
#else // ifndef DEBUG
|
||||
void Enter();
|
||||
void Exit();
|
||||
void Enter() CAPABILITY_ACQUIRE();
|
||||
void Exit() CAPABILITY_RELEASE();
|
||||
nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT);
|
||||
|
||||
#endif // ifndef DEBUG
|
||||
@ -121,7 +122,7 @@ class ReentrantMonitor : BlockingResourceBase {
|
||||
* AssertCurrentThreadIn
|
||||
* @see prmon.h
|
||||
**/
|
||||
void AssertCurrentThreadIn() {
|
||||
void AssertCurrentThreadIn() ASSERT_CAPABILITY(mReentrantMonitor) {
|
||||
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mReentrantMonitor);
|
||||
}
|
||||
|
||||
@ -129,13 +130,13 @@ class ReentrantMonitor : BlockingResourceBase {
|
||||
* AssertNotCurrentThreadIn
|
||||
* @see prmon.h
|
||||
**/
|
||||
void AssertNotCurrentThreadIn() {
|
||||
void AssertNotCurrentThreadIn() EXCLUDES(mReentrantMonitor) {
|
||||
// FIXME bug 476536
|
||||
}
|
||||
|
||||
#else
|
||||
void AssertCurrentThreadIn() {}
|
||||
void AssertNotCurrentThreadIn() {}
|
||||
void AssertCurrentThreadIn() ASSERT_CAPABILITY(mReentrantMonitor) {}
|
||||
void AssertNotCurrentThreadIn() EXCLUDES(mReentrantMonitor) {}
|
||||
|
||||
#endif // ifdef DEBUG
|
||||
|
||||
@ -157,7 +158,7 @@ class ReentrantMonitor : BlockingResourceBase {
|
||||
*
|
||||
* MUCH PREFERRED to bare calls to ReentrantMonitor.Enter and Exit.
|
||||
*/
|
||||
class MOZ_STACK_CLASS ReentrantMonitorAutoEnter {
|
||||
class SCOPED_CAPABILITY MOZ_STACK_CLASS ReentrantMonitorAutoEnter {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
@ -167,13 +168,13 @@ class MOZ_STACK_CLASS ReentrantMonitorAutoEnter {
|
||||
* @param aReentrantMonitor A valid mozilla::ReentrantMonitor*.
|
||||
**/
|
||||
explicit ReentrantMonitorAutoEnter(
|
||||
mozilla::ReentrantMonitor& aReentrantMonitor)
|
||||
mozilla::ReentrantMonitor& aReentrantMonitor) CAPABILITY_ACQUIRE(aReentrantMonitor)
|
||||
: mReentrantMonitor(&aReentrantMonitor) {
|
||||
NS_ASSERTION(mReentrantMonitor, "null monitor");
|
||||
mReentrantMonitor->Enter();
|
||||
}
|
||||
|
||||
~ReentrantMonitorAutoEnter(void) { mReentrantMonitor->Exit(); }
|
||||
~ReentrantMonitorAutoEnter(void) CAPABILITY_RELEASE() { mReentrantMonitor->Exit(); }
|
||||
|
||||
nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT) {
|
||||
return mReentrantMonitor->Wait(aInterval);
|
||||
@ -200,7 +201,7 @@ class MOZ_STACK_CLASS ReentrantMonitorAutoEnter {
|
||||
*
|
||||
* MUCH PREFERRED to bare calls to ReentrantMonitor.Exit and Enter.
|
||||
*/
|
||||
class MOZ_STACK_CLASS ReentrantMonitorAutoExit {
|
||||
class SCOPED_CAPABILITY MOZ_STACK_CLASS ReentrantMonitorAutoExit {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
@ -212,6 +213,7 @@ class MOZ_STACK_CLASS ReentrantMonitorAutoExit {
|
||||
* must be already locked.
|
||||
**/
|
||||
explicit ReentrantMonitorAutoExit(ReentrantMonitor& aReentrantMonitor)
|
||||
EXCLUSIVE_RELEASE(aReentrantMonitor)
|
||||
: mReentrantMonitor(&aReentrantMonitor) {
|
||||
NS_ASSERTION(mReentrantMonitor, "null monitor");
|
||||
mReentrantMonitor->AssertCurrentThreadIn();
|
||||
@ -220,13 +222,16 @@ class MOZ_STACK_CLASS ReentrantMonitorAutoExit {
|
||||
|
||||
explicit ReentrantMonitorAutoExit(
|
||||
ReentrantMonitorAutoEnter& aReentrantMonitorAutoEnter)
|
||||
EXCLUSIVE_RELEASE(aReentrantMonitorAutoEnter.mReentrantMonitor)
|
||||
: mReentrantMonitor(aReentrantMonitorAutoEnter.mReentrantMonitor) {
|
||||
NS_ASSERTION(mReentrantMonitor, "null monitor");
|
||||
mReentrantMonitor->AssertCurrentThreadIn();
|
||||
mReentrantMonitor->Exit();
|
||||
}
|
||||
|
||||
~ReentrantMonitorAutoExit(void) { mReentrantMonitor->Enter(); }
|
||||
~ReentrantMonitorAutoExit(void) EXCLUSIVE_RELEASE() {
|
||||
mReentrantMonitor->Enter();
|
||||
}
|
||||
|
||||
private:
|
||||
ReentrantMonitorAutoExit();
|
||||
|
Loading…
Reference in New Issue
Block a user