зеркало из https://github.com/mozilla/gecko-dev.git
126 строки
3.5 KiB
C++
126 строки
3.5 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 DataMutex_h__
|
|
#define DataMutex_h__
|
|
|
|
#include <utility>
|
|
#include "mozilla/Mutex.h"
|
|
#include "mozilla/StaticMutex.h"
|
|
|
|
namespace mozilla {
|
|
|
|
// A template to wrap a type with a mutex so that accesses to the type's
|
|
// data are required to take the lock before accessing it. This ensures
|
|
// that a mutex is explicitly associated with the data that it protects,
|
|
// and makes it impossible to access the data without first taking the
|
|
// associated mutex.
|
|
//
|
|
// This is based on Rust's std::sync::Mutex, which operates under the
|
|
// strategy of locking data, rather than code.
|
|
//
|
|
// Examples:
|
|
//
|
|
// DataMutex<uint32_t> u32DataMutex(1, "u32DataMutex");
|
|
// auto x = u32DataMutex.Lock();
|
|
// *x = 4;
|
|
// assert(*x, 4u);
|
|
//
|
|
// DataMutex<nsTArray<uint32_t>> arrayDataMutex("arrayDataMutex");
|
|
// auto a = arrayDataMutex.Lock();
|
|
// auto& x = a.ref();
|
|
// x.AppendElement(1u);
|
|
// assert(x[0], 1u);
|
|
//
|
|
template <typename T, typename MutexType>
|
|
class DataMutexBase {
|
|
public:
|
|
class MOZ_STACK_CLASS AutoLock {
|
|
public:
|
|
T* operator->() const& { return &ref(); }
|
|
T* operator->() const&& = delete;
|
|
|
|
T& operator*() const& { return ref(); }
|
|
T& operator*() const&& = delete;
|
|
|
|
// Like RefPtr, make this act like its underlying raw pointer type
|
|
// whenever it is used in a context where a raw pointer is expected.
|
|
operator T*() const& { return &ref(); }
|
|
|
|
// Like RefPtr, don't allow implicit conversion of temporary to raw pointer.
|
|
operator T*() const&& = delete;
|
|
|
|
T& ref() const& {
|
|
MOZ_ASSERT(mOwner);
|
|
return mOwner->mValue;
|
|
}
|
|
T& ref() const&& = delete;
|
|
|
|
AutoLock(AutoLock&& aOther) : mOwner(aOther.mOwner) {
|
|
aOther.mOwner = nullptr;
|
|
}
|
|
|
|
~AutoLock() {
|
|
if (mOwner) {
|
|
mOwner->mMutex.Unlock();
|
|
mOwner = nullptr;
|
|
}
|
|
}
|
|
|
|
private:
|
|
friend class DataMutexBase;
|
|
|
|
AutoLock(const AutoLock& aOther) = delete;
|
|
|
|
explicit AutoLock(DataMutexBase<T, MutexType>* aDataMutex)
|
|
: mOwner(aDataMutex) {
|
|
MOZ_ASSERT(!!mOwner);
|
|
mOwner->mMutex.Lock();
|
|
}
|
|
|
|
DataMutexBase<T, MutexType>* mOwner;
|
|
};
|
|
|
|
explicit DataMutexBase(const char* aName) : mMutex(aName) {}
|
|
|
|
DataMutexBase(T&& aValue, const char* aName)
|
|
: mMutex(aName), mValue(std::move(aValue)) {}
|
|
|
|
AutoLock Lock() { return AutoLock(this); }
|
|
|
|
const MutexType& Mutex() const { return mMutex; }
|
|
|
|
private:
|
|
MutexType mMutex;
|
|
T mValue;
|
|
};
|
|
|
|
// Craft a version of StaticMutex that takes a const char* in its ctor.
|
|
// We need this so it works interchangeably with Mutex which requires a const
|
|
// char* aName in its ctor.
|
|
class StaticMutexNameless : public StaticMutex {
|
|
public:
|
|
explicit StaticMutexNameless(const char* aName) : StaticMutex() {}
|
|
|
|
private:
|
|
// Disallow copy construction, `=`, `new`, and `delete` like BaseStaticMutex.
|
|
#ifdef DEBUG
|
|
StaticMutexNameless(StaticMutexNameless& aOther);
|
|
#endif // DEBUG
|
|
StaticMutexNameless& operator=(StaticMutexNameless* aRhs);
|
|
static void* operator new(size_t) noexcept(true);
|
|
static void operator delete(void*);
|
|
};
|
|
|
|
template <typename T>
|
|
using DataMutex = DataMutexBase<T, Mutex>;
|
|
template <typename T>
|
|
using StaticDataMutex = DataMutexBase<T, StaticMutexNameless>;
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // DataMutex_h__
|