// // Copyright 2021 The ANGLE 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. // // SynchronizedValue.h: // A class that ensures that the correct mutex is locked when the encapsulated data is accessed. // Based on boost::synchronized_value, which probably becomes part of the next C++ standard. // https://www.boost.org/doc/libs/1_76_0/doc/html/thread/sds.html#thread.sds.synchronized_valuesxxx #ifndef COMMON_SYNCHRONIZEDVALUE_H_ #define COMMON_SYNCHRONIZEDVALUE_H_ #include "common/debug.h" #include #include namespace angle { template class ConstStrictLockPtr { public: using value_type = T; using mutex_type = Lockable; ConstStrictLockPtr(const T &value, Lockable &mutex) : mLock(mutex), mValue(value) {} ConstStrictLockPtr(const T &value, Lockable &mutex, std::adopt_lock_t) noexcept : mLock(mutex, std::adopt_lock), mValue(value) {} ConstStrictLockPtr(ConstStrictLockPtr &&other) noexcept : mLock(std::move(other.mLock)), mValue(other.mValue) {} ConstStrictLockPtr(const ConstStrictLockPtr &) = delete; ConstStrictLockPtr &operator=(const ConstStrictLockPtr &) = delete; ~ConstStrictLockPtr() = default; const T *operator->() const { return &mValue; } const T &operator*() const { return mValue; } protected: std::unique_lock mLock; T const &mValue; }; template class StrictLockPtr : public ConstStrictLockPtr { private: using BaseType = ConstStrictLockPtr; public: StrictLockPtr(T &value, Lockable &mutex) : BaseType(value, mutex) {} StrictLockPtr(T &value, Lockable &mutex, std::adopt_lock_t) noexcept : BaseType(value, mutex, std::adopt_lock) {} StrictLockPtr(StrictLockPtr &&other) noexcept : BaseType(std::move(static_cast(other))) {} StrictLockPtr(const StrictLockPtr &) = delete; StrictLockPtr &operator=(const StrictLockPtr &) = delete; ~StrictLockPtr() = default; T *operator->() { return const_cast(&this->mValue); } T &operator*() { return const_cast(this->mValue); } }; template struct SynchronizedValueStrictLockPtr { using type = StrictLockPtr; }; template struct SynchronizedValueStrictLockPtr { using type = ConstStrictLockPtr; }; template class ConstUniqueLockPtr : public std::unique_lock { private: using BaseType = std::unique_lock; public: using value_type = T; using mutex_type = Lockable; ConstUniqueLockPtr(const T &value, Lockable &mutex) : BaseType(mutex), mValue(value) {} ConstUniqueLockPtr(const T &value, Lockable &mutex, std::adopt_lock_t) noexcept : BaseType(mutex, std::adopt_lock), mValue(value) {} ConstUniqueLockPtr(const T &value, Lockable &mutex, std::defer_lock_t) noexcept : BaseType(mutex, std::defer_lock), mValue(value) {} ConstUniqueLockPtr(const T &value, Lockable &mutex, std::try_to_lock_t) noexcept : BaseType(mutex, std::try_to_lock), mValue(value) {} ConstUniqueLockPtr(ConstUniqueLockPtr &&other) noexcept : BaseType(std::move(static_cast(other))), mValue(other.mValue) {} ConstUniqueLockPtr(const ConstUniqueLockPtr &) = delete; ConstUniqueLockPtr &operator=(const ConstUniqueLockPtr &) = delete; ~ConstUniqueLockPtr() = default; const T *operator->() const { ASSERT(this->owns_lock()); return &mValue; } const T &operator*() const { ASSERT(this->owns_lock()); return mValue; } protected: T const &mValue; }; template class UniqueLockPtr : public ConstUniqueLockPtr { private: using BaseType = ConstUniqueLockPtr; public: UniqueLockPtr(T &value, Lockable &mutex) : BaseType(value, mutex) {} UniqueLockPtr(T &value, Lockable &mutex, std::adopt_lock_t) noexcept : BaseType(value, mutex, std::adopt_lock) {} UniqueLockPtr(T &value, Lockable &mutex, std::defer_lock_t) noexcept : BaseType(value, mutex, std::defer_lock) {} UniqueLockPtr(T &value, Lockable &mutex, std::try_to_lock_t) noexcept : BaseType(value, mutex, std::try_to_lock) {} UniqueLockPtr(UniqueLockPtr &&other) noexcept : BaseType(std::move(static_cast(other))) {} UniqueLockPtr(const UniqueLockPtr &) = delete; UniqueLockPtr &operator=(const UniqueLockPtr &) = delete; ~UniqueLockPtr() = default; T *operator->() { ASSERT(this->owns_lock()); return const_cast(&this->mValue); } T &operator*() { ASSERT(this->owns_lock()); return const_cast(this->mValue); } }; template struct SynchronizedValueUniqueLockPtr { using type = UniqueLockPtr; }; template struct SynchronizedValueUniqueLockPtr { using type = ConstUniqueLockPtr; }; template class SynchronizedValue { public: using value_type = T; using mutex_type = Lockable; SynchronizedValue() noexcept(std::is_nothrow_default_constructible::value) : mValue() {} SynchronizedValue(const T &other) noexcept(std::is_nothrow_copy_constructible::value) : mValue(other) {} SynchronizedValue(T &&other) noexcept(std::is_nothrow_move_constructible::value) : mValue(std::move(other)) {} template SynchronizedValue(Args &&... args) noexcept(noexcept(T(std::forward(args)...))) : mValue(std::forward(args)...) {} SynchronizedValue(const SynchronizedValue &other) { std::lock_guard lock(other.mMutex); mValue = other.mValue; } SynchronizedValue(SynchronizedValue &&other) { std::lock_guard lock(other.mMutex); mValue = std::move(other.mValue); } SynchronizedValue &operator=(const SynchronizedValue &other) { if (&other != this) { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); mValue = other.mValue; } return *this; } SynchronizedValue &operator=(SynchronizedValue &&other) { if (&other != this) { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); mValue = std::move(other.mValue); } return *this; } SynchronizedValue &operator=(const T &value) { { std::lock_guard lock(mMutex); mValue = value; } return *this; } SynchronizedValue &operator=(T &&value) { { std::lock_guard lock(mMutex); mValue = std::move(value); } return *this; } T get() const { std::lock_guard lock(mMutex); return mValue; } explicit operator T() const { return get(); } void swap(SynchronizedValue &other) { if (this == &other) { return; } std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); std::swap(mValue, other.mValue); } void swap(T &other) { std::lock_guard lock(mMutex); std::swap(mValue, other); } StrictLockPtr operator->() { return StrictLockPtr(mValue, mMutex); } ConstStrictLockPtr operator->() const { return ConstStrictLockPtr(mValue, mMutex); } StrictLockPtr synchronize() { return StrictLockPtr(mValue, mMutex); } ConstStrictLockPtr synchronize() const { return ConstStrictLockPtr(mValue, mMutex); } UniqueLockPtr unique_synchronize() { return UniqueLockPtr(mValue, mMutex); } ConstUniqueLockPtr unique_synchronize() const { return ConstUniqueLockPtr(mValue, mMutex); } UniqueLockPtr defer_synchronize() noexcept { return UniqueLockPtr(mValue, mMutex, std::defer_lock); } ConstUniqueLockPtr defer_synchronize() const noexcept { return ConstUniqueLockPtr(mValue, mMutex, std::defer_lock); } UniqueLockPtr try_to_synchronize() noexcept { return UniqueLockPtr(mValue, mMutex, std::try_to_lock); } ConstUniqueLockPtr try_to_synchronize() const noexcept { return ConstUniqueLockPtr(mValue, mMutex, std::try_to_lock); } UniqueLockPtr adopt_synchronize() noexcept { return UniqueLockPtr(mValue, mMutex, std::adopt_lock); } ConstUniqueLockPtr adopt_synchronize() const noexcept { return ConstUniqueLockPtr(mValue, mMutex, std::adopt_lock); } class DerefValue { public: DerefValue(DerefValue &&other) : mLock(std::move(other.mLock)), mValue(other.mValue) {} DerefValue(const DerefValue &) = delete; DerefValue &operator=(const DerefValue &) = delete; operator T &() { return mValue; } DerefValue &operator=(const T &other) { mValue = other; return *this; } private: explicit DerefValue(SynchronizedValue &outer) : mLock(outer.mMutex), mValue(outer.mValue) {} std::unique_lock mLock; T &mValue; friend class SynchronizedValue; }; class ConstDerefValue { public: ConstDerefValue(ConstDerefValue &&other) : mLock(std::move(other.mLock)), mValue(other.mValue) {} ConstDerefValue(const ConstDerefValue &) = delete; ConstDerefValue &operator=(const ConstDerefValue &) = delete; operator const T &() { return mValue; } private: explicit ConstDerefValue(const SynchronizedValue &outer) : mLock(outer.mMutex), mValue(outer.mValue) {} std::unique_lock mLock; const T &mValue; friend class SynchronizedValue; }; DerefValue operator*() { return DerefValue(*this); } ConstDerefValue operator*() const { return ConstDerefValue(*this); } template void save(OStream &os) const { std::lock_guard lock(mMutex); os << mValue; } template void load(IStream &is) { std::lock_guard lock(mMutex); is >> mValue; } bool operator==(const SynchronizedValue &other) const { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); return mValue == other.mValue; } bool operator!=(const SynchronizedValue &other) const { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); return mValue != other.mValue; } bool operator<(const SynchronizedValue &other) const { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); return mValue < other.mValue; } bool operator>(const SynchronizedValue &other) const { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); return mValue > other.mValue; } bool operator<=(const SynchronizedValue &other) const { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); return mValue <= other.mValue; } bool operator>=(const SynchronizedValue &other) const { std::unique_lock lock1(mMutex, std::defer_lock); std::unique_lock lock2(other.mMutex, std::defer_lock); std::lock(lock1, lock2); return mValue >= other.mValue; } bool operator==(const T &other) const { std::lock_guard lock(mMutex); return mValue == other; } bool operator!=(const T &other) const { std::lock_guard lock(mMutex); return mValue != other; } bool operator<(const T &other) const { std::lock_guard lock(mMutex); return mValue < other; } bool operator>(const T &other) const { std::lock_guard lock(mMutex); return mValue > other; } bool operator<=(const T &other) const { std::lock_guard lock(mMutex); return mValue <= other; } bool operator>=(const T &other) const { std::lock_guard lock(mMutex); return mValue >= other; } private: T mValue; mutable Lockable mMutex; }; template inline OStream &operator<<(OStream &os, SynchronizedValue const &sv) { sv.save(os); return os; } template inline IStream &operator>>(IStream &is, SynchronizedValue &sv) { sv.load(is); return is; } template bool operator==(const T &lhs, const SynchronizedValue &rhs) { return rhs == lhs; } template bool operator!=(const T &lhs, const SynchronizedValue &rhs) { return rhs != lhs; } template bool operator<(const T &lhs, const SynchronizedValue &rhs) { return rhs < lhs; } template bool operator>(const T &lhs, const SynchronizedValue &rhs) { return rhs > lhs; } template bool operator<=(const T &lhs, const SynchronizedValue &rhs) { return rhs <= lhs; } template bool operator>=(const T &lhs, const SynchronizedValue &rhs) { return rhs >= lhs; } } // namespace angle #endif // COMMON_SYNCHRONIZEDVALUE_H_