//
// 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) : mValu
e() {}
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_
Messung V0.5 C=98 H=100 G=98
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland