mirror of https://github.com/tstack/lnav.git
360 lines
12 KiB
C++
360 lines
12 KiB
C++
/**
|
|
* @file safe.h
|
|
* @author L.-C. C.
|
|
* @brief
|
|
* @version 0.1
|
|
* @date 2018-09-21
|
|
*
|
|
* @copyright Copyright (c) 2018
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "accessmode.h"
|
|
#include "defaulttypes.h"
|
|
#include "mutableref.h"
|
|
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#if __cplusplus >= 201703L
|
|
#define EXPLICIT_IF_CPP17 explicit
|
|
#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17 ReturnType
|
|
#else
|
|
#define EXPLICIT_IF_CPP17
|
|
#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17
|
|
#endif
|
|
|
|
namespace safe
|
|
{
|
|
/**
|
|
* @brief Use this tag to default construct the mutex when constructing a
|
|
* Safe object.
|
|
*/
|
|
struct DefaultConstructMutex {};
|
|
static constexpr DefaultConstructMutex default_construct_mutex;
|
|
|
|
/**
|
|
* @brief Wraps a value together with a mutex.
|
|
*
|
|
* @tparam ValueType The type of the value to protect.
|
|
* @tparam MutexType The type of the mutex.
|
|
*/
|
|
template<typename ValueType, typename MutexType = DefaultMutex>
|
|
class Safe
|
|
{
|
|
private:
|
|
/// Type ValueType with reference removed, if present
|
|
using RemoveRefValueType = typename std::remove_reference<ValueType>::type;
|
|
/// Type MutexType with reference removed, if present
|
|
using RemoveRefMutexType = typename std::remove_reference<MutexType>::type;
|
|
|
|
/**
|
|
* @brief Manages a mutex and gives pointer-like access to a value
|
|
* object.
|
|
*
|
|
* @tparam LockType The type of the lock object that manages the
|
|
* mutex, example: std::lock_guard.
|
|
* @tparam Mode Determines the access mode of the Access
|
|
* object. Can be either AccessMode::ReadOnly or
|
|
* AccessMode::ReadWrite.
|
|
*/
|
|
template<template<typename> class LockType, AccessMode Mode>
|
|
class Access
|
|
{
|
|
// Make sure AccessMode is ReadOnly if a read-only lock is used
|
|
static_assert(!(AccessTraits<LockType<RemoveRefMutexType>>::IsReadOnly && Mode==AccessMode::ReadWrite), "Cannot have ReadWrite access mode with ReadOnly lock. Check the value of AccessTraits<LockType>::IsReadOnly if it exists.");
|
|
|
|
/// ValueType with const qualifier if AccessMode is ReadOnly.
|
|
using ConstIfReadOnlyValueType = typename std::conditional<Mode==AccessMode::ReadOnly, const RemoveRefValueType, RemoveRefValueType>::type;
|
|
|
|
public:
|
|
/// Pointer-to-const ValueType
|
|
using ConstPointerType = const ConstIfReadOnlyValueType*;
|
|
/// Pointer-to-const ValueType if Mode is ReadOnly, pointer to ValueType otherwise.
|
|
using PointerType = ConstIfReadOnlyValueType*;
|
|
/// Reference-to-const ValueType
|
|
using ConstReferenceType = const ConstIfReadOnlyValueType&;
|
|
/// Reference-to-const ValueType if Mode is ReadOnly, reference to ValueType otherwise.
|
|
using ReferenceType = ConstIfReadOnlyValueType&;
|
|
|
|
/**
|
|
* @brief Construct an Access object from a possibly const
|
|
* reference to the value object and any additionnal argument
|
|
* needed to construct the Lock object.
|
|
*
|
|
* @tparam LockArgs Deduced from lockArgs.
|
|
* @param value Reference to the value.
|
|
* @param lockArgs Arguments needed to construct the lock object.
|
|
*/
|
|
template<typename... OtherLockArgs>
|
|
EXPLICIT_IF_CPP17
|
|
Access(ReferenceType value, MutexType& mutex, OtherLockArgs&&... otherLockArgs):
|
|
lock(mutex, std::forward<OtherLockArgs>(otherLockArgs)...),
|
|
m_value(value)
|
|
{}
|
|
|
|
/**
|
|
* @brief Construct a read-only Access object from a const
|
|
* safe::Safe object and any additionnal argument needed to
|
|
* construct the Lock object.
|
|
*
|
|
* If needed, you can provide additionnal arguments to construct
|
|
* the lock object (such as std::adopt_lock). The mutex from the
|
|
* safe::Locakble object is already passed to the lock object's
|
|
* constructor though, you must not provide it.
|
|
*
|
|
* @tparam OtherLockArgs Deduced from otherLockArgs.
|
|
* @param safe The const Safe object to give protected access to.
|
|
* @param otherLockArgs Other arguments needed to construct the lock
|
|
* object.
|
|
*/
|
|
template<typename... OtherLockArgs>
|
|
EXPLICIT_IF_CPP17
|
|
Access(const Safe& safe, OtherLockArgs&&... otherLockArgs):
|
|
Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)
|
|
{}
|
|
|
|
/**
|
|
* @brief Construct a read-write Access object from a
|
|
* safe::Safe object and any additionnal argument needed to
|
|
* construct the Lock object.
|
|
*
|
|
* If needed, you can provide additionnal arguments to construct
|
|
* the lock object (such as std::adopt_lock). The mutex from the
|
|
* safe object is already passed to the lock object's constructor
|
|
* though, you must not provide it.
|
|
*
|
|
* @tparam OtherLockArgs Deduced from otherLockArgs.
|
|
* @param safe The Safe object to give protected access to.
|
|
* @param otherLockArgs Other arguments needed to construct the lock
|
|
* object.
|
|
*/
|
|
template<typename... OtherLockArgs>
|
|
EXPLICIT_IF_CPP17
|
|
Access(Safe& safe, OtherLockArgs&&... otherLockArgs):
|
|
Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)
|
|
{}
|
|
|
|
/**
|
|
* @brief Construct an Access object from another one.
|
|
* OtherLockType must implement release() like std::unique_lock
|
|
* does.
|
|
*
|
|
* @tparam OtherLockType Deduced from otherAccess.
|
|
* @tparam OtherMode Deduced from otherAccess.
|
|
* @tparam OtherLockArgs Deduced from otherLockArgs.
|
|
* @param otherAccess The Access object to construct from.
|
|
* @param otherLockArgs Other arguments needed to construct the lock
|
|
* object.
|
|
*/
|
|
template<template<typename> class OtherLockType, AccessMode OtherMode, typename... OtherLockArgs>
|
|
EXPLICIT_IF_CPP17
|
|
Access(Access<OtherLockType, OtherMode>& otherAccess, OtherLockArgs&&... otherLockArgs):
|
|
Access(*otherAccess, *otherAccess.lock.release(), std::adopt_lock, std::forward<OtherLockArgs>(otherLockArgs)...)
|
|
{
|
|
static_assert(OtherMode == AccessMode::ReadWrite || OtherMode == Mode, "Cannot construct a ReadWrite Access object from a ReadOnly one!");
|
|
}
|
|
|
|
/**
|
|
* @brief Const accessor to the value.
|
|
* @return ConstPointerType Const pointer to the protected value.
|
|
*/
|
|
ConstPointerType operator->() const noexcept
|
|
{
|
|
return &m_value;
|
|
}
|
|
|
|
/**
|
|
* @brief Accessor to the value.
|
|
* @return ValuePointerType Pointer to the protected value.
|
|
*/
|
|
PointerType operator->() noexcept
|
|
{
|
|
return &m_value;
|
|
}
|
|
|
|
/**
|
|
* @brief Const accessor to the value.
|
|
* @return ConstValueReferenceType Const reference to the protected
|
|
* value.
|
|
*/
|
|
ConstReferenceType operator*() const noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
/**
|
|
* @brief Accessor to the value.
|
|
* @return ValueReferenceType Reference to the protected.
|
|
*/
|
|
ReferenceType operator*() noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
/// The lock that manages the mutex.
|
|
mutable LockType<RemoveRefMutexType> lock;
|
|
|
|
private:
|
|
/// The protected value.
|
|
ReferenceType m_value;
|
|
};
|
|
|
|
/// Reference-to-const ValueType.
|
|
using ConstValueReferenceType = const RemoveRefValueType&;
|
|
/// Reference to ValueType.
|
|
using ValueReferenceType = RemoveRefValueType&;
|
|
/// Reference to MutexType.
|
|
using MutexReferenceType = RemoveRefMutexType&;
|
|
|
|
public:
|
|
/// Aliases to ReadAccess and WriteAccess classes for this Safe class.
|
|
template<template<typename> class LockType=DefaultReadOnlyLock>
|
|
using ReadAccess = Access<LockType, AccessMode::ReadOnly>;
|
|
template<template<typename> class LockType=DefaultReadWriteLock>
|
|
using WriteAccess = Access<LockType, AccessMode::ReadWrite>;
|
|
|
|
/**
|
|
* @brief Construct a Safe object
|
|
*/
|
|
Safe() = default;
|
|
|
|
/**
|
|
* @brief Construct a Safe object with default construction of
|
|
* the mutex and perfect forwarding of the other arguments to
|
|
* construct the value object.
|
|
*
|
|
* @tparam ValueArgs Deduced from valueArgs.
|
|
* @param valueArgs Perfect forwarding arguments to construct the value object.
|
|
* @param tag Indicates that the mutex should be default constructed.
|
|
*/
|
|
template<typename... ValueArgs>
|
|
explicit Safe(DefaultConstructMutex, ValueArgs&&... valueArgs):
|
|
m_mutex(),
|
|
m_value(std::forward<ValueArgs>(valueArgs)...)
|
|
{}
|
|
/**
|
|
* @brief Construct a Safe object, forwarding the first
|
|
* argument to construct the mutex and the other arguments to
|
|
* construct the value object.
|
|
*
|
|
* @tparam MutexArg Deduced from mutexArg.
|
|
* @tparam ValueArgs Deduced from valueArgs.
|
|
* @param valueArgs Perfect forwarding arguments to construct the
|
|
* value object.
|
|
* @param mutexArg Perfect forwarding argument to construct the
|
|
* mutex object.
|
|
*/
|
|
template<typename MutexArg, typename... ValueArgs>
|
|
explicit Safe(MutexArg&& mutexArg, ValueArgs&&... valueArgs):
|
|
m_mutex{std::forward<MutexArg>(mutexArg)},
|
|
m_value(std::forward<ValueArgs>(valueArgs)...)
|
|
{}
|
|
|
|
/// Delete all copy/move construction/assignment, as these operations
|
|
/// require locking the mutex under the covers.
|
|
/// Use copy(), assign() and other defined constructors to get the behavior
|
|
/// you need with an explicit syntax.
|
|
Safe(const Safe&) = delete;
|
|
Safe(Safe&&) = delete;
|
|
Safe& operator =(const Safe&) = delete;
|
|
Safe& operator =(Safe&&) = delete;
|
|
|
|
template<template<typename> class LockType=DefaultReadOnlyLock, typename... LockArgs>
|
|
ReadAccess<LockType> readAccess(LockArgs&&... lockArgs) const
|
|
{
|
|
// using ReturnType = ReadAccess<LockType>;
|
|
return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};
|
|
}
|
|
|
|
template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
|
|
WriteAccess<LockType> writeAccess(LockArgs&&... lockArgs)
|
|
{
|
|
// using ReturnType = WriteAccess<LockType>;
|
|
return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};
|
|
}
|
|
|
|
template<template<typename> class LockType=DefaultReadOnlyLock, typename... LockArgs>
|
|
RemoveRefValueType copy(LockArgs&&... lockArgs) const
|
|
{
|
|
return *readAccess<LockType>(std::forward<LockArgs>(lockArgs)...);
|
|
}
|
|
|
|
template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
|
|
void assign(ConstValueReferenceType value, LockArgs&&... lockArgs)
|
|
{
|
|
*writeAccess<LockType>(std::forward<LockArgs>(lockArgs)...) = value;
|
|
}
|
|
template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
|
|
void assign(RemoveRefValueType&& value, LockArgs&&... lockArgs)
|
|
{
|
|
*writeAccess<LockType>(std::forward<LockArgs>(lockArgs)...) = std::move(value);
|
|
}
|
|
|
|
/**
|
|
* @brief Unsafe const accessor to the value. If you use this
|
|
* function, you exit the realm of safe!
|
|
*
|
|
* @return ConstValueReferenceType Const reference to the value
|
|
* object.
|
|
*/
|
|
ConstValueReferenceType unsafe() const noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
/**
|
|
* @brief Unsafe accessor to the value. If you use this function,
|
|
* you exit the realm of safe!
|
|
*
|
|
* @return ValueReferenceType Reference to the value object.
|
|
*/
|
|
ValueReferenceType unsafe() noexcept
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
/**
|
|
* @brief Accessor to the mutex.
|
|
*
|
|
* @return MutexReferenceType Reference to the mutex.
|
|
*/
|
|
MutexReferenceType mutex() const noexcept
|
|
{
|
|
return m_mutex.get;
|
|
}
|
|
|
|
private:
|
|
/// The helper object that holds the mutable mutex, or a reference to a mutex.
|
|
impl::MutableIfNotReference<MutexType> m_mutex;
|
|
/// The value to protect.
|
|
ValueType m_value;
|
|
};
|
|
|
|
/**
|
|
* @brief Type alias for read-only Access.
|
|
*
|
|
* @tparam SafeType The type of Safe object to give read-only access to.
|
|
* @tparam LockType=DefaultReadOnlyLock The type of lock.
|
|
*/
|
|
template<
|
|
typename SafeType,
|
|
template<typename> class LockType=DefaultReadOnlyLock>
|
|
using ReadAccess = typename SafeType::template ReadAccess<LockType>;
|
|
|
|
/**
|
|
* @brief Type alias for read-write Access.
|
|
*
|
|
* @tparam SafeType The type of Safe object to give read-write access to.
|
|
* @tparam LockType=DefaultReadWriteLock The type of lock.
|
|
*/
|
|
template<
|
|
typename SafeType,
|
|
template<typename> class LockType=DefaultReadWriteLock>
|
|
using WriteAccess = typename SafeType::template WriteAccess<LockType>;
|
|
} // namespace safe
|
|
|
|
#undef EXPLICIT_IF_CPP17
|
|
#undef EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17
|