|
#pragma once |
|
#include <mutex> |
|
#include <shared_mutex> |
|
#include <type_traits> |
|
#include <tuple> |
|
|
|
template<typename Mutex = std::shared_timed_mutex> |
|
struct Shared |
|
{ |
|
typedef Mutex MutexType; |
|
typedef std::shared_lock<MutexType> ReadLock; |
|
typedef std::unique_lock<MutexType> WriteLock; |
|
}; |
|
|
|
template<typename Mutex = std::mutex> |
|
struct Unique |
|
{ |
|
typedef Mutex MutexType; |
|
typedef std::unique_lock<MutexType> ReadLock; |
|
typedef std::unique_lock<MutexType> WriteLock; |
|
}; |
|
|
|
namespace detail |
|
{ |
|
struct AccessSharedResourceImpl; |
|
} |
|
|
|
template<typename Resource, typename ShareType = Shared<>> |
|
class shared_resource |
|
{ |
|
private: |
|
mutable typename ShareType::MutexType mutex; |
|
Resource resource; |
|
|
|
shared_resource(const shared_resource& other, typename ShareType::ReadLock other_lock) : |
|
resource(other.resource) |
|
{ |
|
static_cast<void>(other_lock); |
|
} |
|
|
|
shared_resource(shared_resource&& other, typename ShareType::WriteLock other_lock) : |
|
resource(std::move(other.resource)) |
|
{ |
|
static_cast<void>(other_lock); |
|
} |
|
|
|
friend class detail::AccessSharedResourceImpl; |
|
public: |
|
typedef ShareType share_type; |
|
|
|
shared_resource(Resource&& resource) : |
|
resource(std::move(resource)) |
|
{ |
|
|
|
} |
|
|
|
shared_resource(const Resource& resource) : |
|
resource(resource) |
|
{ |
|
|
|
} |
|
|
|
shared_resource(const shared_resource& other) : |
|
shared_resource(other, typename ShareType::ReadLock(other.mutex)) |
|
{ |
|
|
|
} |
|
|
|
shared_resource(shared_resource&& other) : |
|
shared_resource(std::move(other), typename ShareType::WriteLock(other.mutex)) |
|
{ |
|
|
|
} |
|
|
|
shared_resource() |
|
{ |
|
|
|
} |
|
|
|
shared_resource& operator=(const shared_resource& other) |
|
{ |
|
if(this != &other) |
|
{ |
|
typename ShareType::WriteLock lhs_lk(this->mutex, std::defer_lock); |
|
typename ShareType::ReadLock rhs_lk(other.mutex, std::defer_lock); |
|
std::lock(lhs_lk, rhs_lk); |
|
using std::swap; |
|
swap(this->resource, other.resource); |
|
} |
|
} |
|
|
|
shared_resource& operator=(shared_resource&& other) |
|
{ |
|
if(this != &other) |
|
{ |
|
typename ShareType::WriteLock lhs_lk(this->mutex, std::defer_lock); |
|
typename ShareType::WriteLock rhs_lk(other.mutex, std::defer_lock); |
|
std::lock(lhs_lk, rhs_lk); |
|
this->resource = std::move(other.resource); |
|
} |
|
} |
|
}; |
|
|
|
namespace detail |
|
{ |
|
template<typename T> |
|
struct is_shared_resource : std::false_type {}; |
|
|
|
template<typename Resource, typename ShareType> |
|
struct is_shared_resource<shared_resource<Resource, ShareType>> : std::true_type {}; |
|
|
|
template<typename T> |
|
struct ReadAccess |
|
{ |
|
T& ref; |
|
typedef typename T::share_type::ReadLock lock_type; |
|
}; |
|
|
|
template<typename T> |
|
struct WriteAccess |
|
{ |
|
T& ref; |
|
typedef typename T::share_type::WriteLock lock_type; |
|
}; |
|
|
|
template<std::size_t Size> |
|
struct LockTupleImpl |
|
{ |
|
template<typename Tup, std::size_t... index> |
|
static void run(Tup&& tup, std::index_sequence<index...>) |
|
{ |
|
std::lock(std::get<index>(std::forward<Tup>(tup))...); |
|
} |
|
}; |
|
|
|
template<> |
|
struct LockTupleImpl<1> |
|
{ |
|
template<typename Tup, std::size_t... index> |
|
static void run(Tup&& tup, std::index_sequence<index...>) |
|
{ |
|
std::get<0>(std::forward<Tup>(tup)).lock(); |
|
} |
|
}; |
|
|
|
struct AccessSharedResourceImpl |
|
{ |
|
template<typename... SharedResourceAccess, typename Function> |
|
static auto access_shared_resource(Function&& f, SharedResourceAccess... shared_access) -> decltype(std::forward<Function>(f)(shared_access.ref.resource...)) |
|
{ |
|
std::tuple<typename SharedResourceAccess::lock_type...> locks( |
|
typename SharedResourceAccess::lock_type(shared_access.ref.mutex, std::defer_lock)...); |
|
detail::LockTupleImpl<sizeof...(shared_access)>::run(locks, std::make_index_sequence<sizeof...(shared_access)>()); |
|
return std::forward<Function>(f)(shared_access.ref.resource...); |
|
} |
|
}; |
|
} |
|
|
|
template<typename T> |
|
detail::ReadAccess<const T> read(const T& shared_resource) |
|
{ |
|
static_assert(detail::is_shared_resource<T>::value, "the type passed must be a shared_resource"); |
|
return { shared_resource }; |
|
} |
|
|
|
template<typename T> |
|
detail::WriteAccess<T> write(T& shared_resource) |
|
{ |
|
static_assert(detail::is_shared_resource<T>::value, "the type passed must be a shared_resource"); |
|
return { shared_resource }; |
|
} |
|
|
|
template<typename... Args> |
|
auto access_shared_resource(Args&&... args) |
|
{ |
|
return detail::AccessSharedResourceImpl::access_shared_resource(std::forward<Args>(args)...); |
|
} |