Edit

kc3-lang/angle/src/common/SynchronizedValue.h

Branch :

  • Show log

    Commit

  • Author : John Plate
    Date : 2021-06-17 09:29:29
    Hash : 44a5c913
    Message : CL: Make CL front end and back end thread-safe Add locking to all mutable variables of the CL objects in the front end and pass-through back end to make them thread-safe. This fixes a crash in a multi-threaded CTS test. Bug: angleproject:6015 Change-Id: I1d6471c851217639411c434c82acd32d14035291 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2967468 Commit-Queue: John Plate <jplate@google.com> Reviewed-by: Cody Northrop <cnorthrop@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/common/SynchronizedValue.h
  • //
    // 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_