| // |
| // 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 <mutex> |
| #include <type_traits> |
| |
| namespace angle |
| { |
| |
| template <typename T, typename Lockable = std::mutex> |
| 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<Lockable> mLock; |
| T const &mValue; |
| }; |
| |
| template <typename T, typename Lockable = std::mutex> |
| class StrictLockPtr : public ConstStrictLockPtr<T, Lockable> |
| { |
| private: |
| using BaseType = ConstStrictLockPtr<T, Lockable>; |
| |
| 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<BaseType &&>(other))) |
| {} |
| |
| StrictLockPtr(const StrictLockPtr &) = delete; |
| StrictLockPtr &operator=(const StrictLockPtr &) = delete; |
| |
| ~StrictLockPtr() = default; |
| |
| T *operator->() { return const_cast<T *>(&this->mValue); } |
| T &operator*() { return const_cast<T &>(this->mValue); } |
| }; |
| |
| template <typename SV> |
| struct SynchronizedValueStrictLockPtr |
| { |
| using type = StrictLockPtr<typename SV::value_type, typename SV::mutex_type>; |
| }; |
| |
| template <typename SV> |
| struct SynchronizedValueStrictLockPtr<const SV> |
| { |
| using type = ConstStrictLockPtr<typename SV::value_type, typename SV::mutex_type>; |
| }; |
| |
| template <typename T, typename Lockable = std::mutex> |
| class ConstUniqueLockPtr : public std::unique_lock<Lockable> |
| { |
| private: |
| using BaseType = std::unique_lock<Lockable>; |
| |
| 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<BaseType &&>(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 <typename T, typename Lockable = std::mutex> |
| class UniqueLockPtr : public ConstUniqueLockPtr<T, Lockable> |
| { |
| private: |
| using BaseType = ConstUniqueLockPtr<T, Lockable>; |
| |
| 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<BaseType &&>(other))) |
| {} |
| |
| UniqueLockPtr(const UniqueLockPtr &) = delete; |
| UniqueLockPtr &operator=(const UniqueLockPtr &) = delete; |
| |
| ~UniqueLockPtr() = default; |
| |
| T *operator->() |
| { |
| ASSERT(this->owns_lock()); |
| return const_cast<T *>(&this->mValue); |
| } |
| T &operator*() |
| { |
| ASSERT(this->owns_lock()); |
| return const_cast<T &>(this->mValue); |
| } |
| }; |
| |
| template <typename SV> |
| struct SynchronizedValueUniqueLockPtr |
| { |
| using type = UniqueLockPtr<typename SV::value_type, typename SV::mutex_type>; |
| }; |
| |
| template <typename SV> |
| struct SynchronizedValueUniqueLockPtr<const SV> |
| { |
| using type = ConstUniqueLockPtr<typename SV::value_type, typename SV::mutex_type>; |
| }; |
| |
| template <typename T, typename Lockable = std::mutex> |
| class SynchronizedValue |
| { |
| public: |
| using value_type = T; |
| using mutex_type = Lockable; |
| |
| SynchronizedValue() noexcept(std::is_nothrow_default_constructible<T>::value) : mValue() {} |
| |
| SynchronizedValue(const T &other) noexcept(std::is_nothrow_copy_constructible<T>::value) |
| : mValue(other) |
| {} |
| |
| SynchronizedValue(T &&other) noexcept(std::is_nothrow_move_constructible<T>::value) |
| : mValue(std::move(other)) |
| {} |
| |
| template <typename... Args> |
| SynchronizedValue(Args &&... args) noexcept(noexcept(T(std::forward<Args>(args)...))) |
| : mValue(std::forward<Args>(args)...) |
| {} |
| |
| SynchronizedValue(const SynchronizedValue &other) |
| { |
| std::lock_guard<Lockable> lock(other.mMutex); |
| mValue = other.mValue; |
| } |
| |
| SynchronizedValue(SynchronizedValue &&other) |
| { |
| std::lock_guard<Lockable> lock(other.mMutex); |
| mValue = std::move(other.mValue); |
| } |
| |
| SynchronizedValue &operator=(const SynchronizedValue &other) |
| { |
| if (&other != this) |
| { |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> 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<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> 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<Lockable> lock(mMutex); |
| mValue = value; |
| } |
| return *this; |
| } |
| |
| SynchronizedValue &operator=(T &&value) |
| { |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| mValue = std::move(value); |
| } |
| return *this; |
| } |
| |
| T get() const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| return mValue; |
| } |
| |
| explicit operator T() const { return get(); } |
| |
| void swap(SynchronizedValue &other) |
| { |
| if (this == &other) |
| { |
| return; |
| } |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); |
| std::lock(lock1, lock2); |
| std::swap(mValue, other.mValue); |
| } |
| |
| void swap(T &other) |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| std::swap(mValue, other); |
| } |
| |
| StrictLockPtr<T, Lockable> operator->() { return StrictLockPtr<T, Lockable>(mValue, mMutex); } |
| ConstStrictLockPtr<T, Lockable> operator->() const |
| { |
| return ConstStrictLockPtr<T, Lockable>(mValue, mMutex); |
| } |
| |
| StrictLockPtr<T, Lockable> synchronize() { return StrictLockPtr<T, Lockable>(mValue, mMutex); } |
| ConstStrictLockPtr<T, Lockable> synchronize() const |
| { |
| return ConstStrictLockPtr<T, Lockable>(mValue, mMutex); |
| } |
| |
| UniqueLockPtr<T, Lockable> unique_synchronize() |
| { |
| return UniqueLockPtr<T, Lockable>(mValue, mMutex); |
| } |
| ConstUniqueLockPtr<T, Lockable> unique_synchronize() const |
| { |
| return ConstUniqueLockPtr<T, Lockable>(mValue, mMutex); |
| } |
| |
| UniqueLockPtr<T, Lockable> defer_synchronize() noexcept |
| { |
| return UniqueLockPtr<T, Lockable>(mValue, mMutex, std::defer_lock); |
| } |
| ConstUniqueLockPtr<T, Lockable> defer_synchronize() const noexcept |
| { |
| return ConstUniqueLockPtr<T, Lockable>(mValue, mMutex, std::defer_lock); |
| } |
| |
| UniqueLockPtr<T, Lockable> try_to_synchronize() noexcept |
| { |
| return UniqueLockPtr<T, Lockable>(mValue, mMutex, std::try_to_lock); |
| } |
| ConstUniqueLockPtr<T, Lockable> try_to_synchronize() const noexcept |
| { |
| return ConstUniqueLockPtr<T, Lockable>(mValue, mMutex, std::try_to_lock); |
| } |
| |
| UniqueLockPtr<T, Lockable> adopt_synchronize() noexcept |
| { |
| return UniqueLockPtr<T, Lockable>(mValue, mMutex, std::adopt_lock); |
| } |
| ConstUniqueLockPtr<T, Lockable> adopt_synchronize() const noexcept |
| { |
| return ConstUniqueLockPtr<T, Lockable>(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<Lockable> 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<Lockable> mLock; |
| const T &mValue; |
| |
| friend class SynchronizedValue; |
| }; |
| |
| DerefValue operator*() { return DerefValue(*this); } |
| ConstDerefValue operator*() const { return ConstDerefValue(*this); } |
| |
| template <typename OStream> |
| void save(OStream &os) const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| os << mValue; |
| } |
| |
| template <typename IStream> |
| void load(IStream &is) |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| is >> mValue; |
| } |
| |
| bool operator==(const SynchronizedValue &other) const |
| { |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); |
| std::lock(lock1, lock2); |
| return mValue == other.mValue; |
| } |
| |
| bool operator!=(const SynchronizedValue &other) const |
| { |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); |
| std::lock(lock1, lock2); |
| return mValue != other.mValue; |
| } |
| |
| bool operator<(const SynchronizedValue &other) const |
| { |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); |
| std::lock(lock1, lock2); |
| return mValue < other.mValue; |
| } |
| |
| bool operator>(const SynchronizedValue &other) const |
| { |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); |
| std::lock(lock1, lock2); |
| return mValue > other.mValue; |
| } |
| |
| bool operator<=(const SynchronizedValue &other) const |
| { |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); |
| std::lock(lock1, lock2); |
| return mValue <= other.mValue; |
| } |
| |
| bool operator>=(const SynchronizedValue &other) const |
| { |
| std::unique_lock<Lockable> lock1(mMutex, std::defer_lock); |
| std::unique_lock<Lockable> lock2(other.mMutex, std::defer_lock); |
| std::lock(lock1, lock2); |
| return mValue >= other.mValue; |
| } |
| |
| bool operator==(const T &other) const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| return mValue == other; |
| } |
| |
| bool operator!=(const T &other) const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| return mValue != other; |
| } |
| |
| bool operator<(const T &other) const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| return mValue < other; |
| } |
| |
| bool operator>(const T &other) const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| return mValue > other; |
| } |
| |
| bool operator<=(const T &other) const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| return mValue <= other; |
| } |
| |
| bool operator>=(const T &other) const |
| { |
| std::lock_guard<Lockable> lock(mMutex); |
| return mValue >= other; |
| } |
| |
| private: |
| T mValue; |
| mutable Lockable mMutex; |
| }; |
| |
| template <typename OStream, typename T, typename L> |
| inline OStream &operator<<(OStream &os, SynchronizedValue<T, L> const &sv) |
| { |
| sv.save(os); |
| return os; |
| } |
| |
| template <typename IStream, typename T, typename L> |
| inline IStream &operator>>(IStream &is, SynchronizedValue<T, L> &sv) |
| { |
| sv.load(is); |
| return is; |
| } |
| |
| template <typename T, typename L> |
| bool operator==(const T &lhs, const SynchronizedValue<T, L> &rhs) |
| { |
| return rhs == lhs; |
| } |
| |
| template <typename T, typename L> |
| bool operator!=(const T &lhs, const SynchronizedValue<T, L> &rhs) |
| { |
| return rhs != lhs; |
| } |
| |
| template <typename T, typename L> |
| bool operator<(const T &lhs, const SynchronizedValue<T, L> &rhs) |
| { |
| return rhs < lhs; |
| } |
| |
| template <typename T, typename L> |
| bool operator>(const T &lhs, const SynchronizedValue<T, L> &rhs) |
| { |
| return rhs > lhs; |
| } |
| |
| template <typename T, typename L> |
| bool operator<=(const T &lhs, const SynchronizedValue<T, L> &rhs) |
| { |
| return rhs <= lhs; |
| } |
| |
| template <typename T, typename L> |
| bool operator>=(const T &lhs, const SynchronizedValue<T, L> &rhs) |
| { |
| return rhs >= lhs; |
| } |
| |
| } // namespace angle |
| |
| #endif // COMMON_SYNCHRONIZEDVALUE_H_ |