1
0
Fork 0
Univerxel/deps/libguarded/shared_mutex_guarded.h

235 lines
6.1 KiB
C++

/***********************************************************************
*
* Copyright (c) 2015-2020 Ansel Sermersheim
*
* This file is part of CsLibGuarded.
*
* CsLibGuarded is free software, released under the BSD 2-Clause license.
* For license details refer to LICENSE provided with this project.
*
* CopperSpice is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* https://opensource.org/licenses/BSD-2-Clause
*
***********************************************************************/
#ifndef CSLIBGUARDED_SHARED_GUARDED_H
#define CSLIBGUARDED_SHARED_GUARDED_H
#include <memory>
#include <shared_mutex>
namespace libguarded
{
/**
\headerfile cs_shared_guarded.h <CsLibGuarded/cs_shared_guarded.h>
This templated class wraps an object and allows only one thread at
a time to modify the protected object.
This class will use std::shared_timed_mutex for the internal
locking mechanism by default. In C++17 the std::shared_mutex class
is also available.
The handle returned by the various lock methods is moveable but not
copyable.
*/
template <typename T, typename M = std::shared_timed_mutex, typename L = std::shared_lock<M>>
class shared_guarded
{
private:
class deleter;
class shared_deleter;
public:
using handle = std::unique_ptr<T, deleter>;
using shared_handle = std::unique_ptr<const T, shared_deleter>;
template <typename... Us>
shared_guarded(Us &&... data);
// exclusive access
[[nodiscard]] handle lock();
[[nodiscard]] handle try_lock();
template <class Duration>
[[nodiscard]] handle try_lock_for(const Duration &duration);
template <class TimePoint>
[[nodiscard]] handle try_lock_until(const TimePoint &timepoint);
// shared access, note "shared" in method names
[[nodiscard]] shared_handle lock_shared() const;
[[nodiscard]] shared_handle try_lock_shared() const;
template <class Duration>
[[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const;
template <class TimePoint>
[[nodiscard]] shared_handle try_lock_shared_until(const TimePoint &timepoint) const;
private:
T m_obj;
mutable M m_mutex;
};
template <typename T, typename M, typename L>
class shared_guarded<T, M, L>::deleter
{
public:
using pointer = T *;
deleter(std::unique_lock<M> lock);
void operator()(T *ptr);
private:
std::unique_lock<M> m_lock;
};
template <typename T, typename M, typename L>
shared_guarded<T, M, L>::deleter::deleter(std::unique_lock<M> lock)
: m_lock(std::move(lock))
{
}
template <typename T, typename M, typename L>
void shared_guarded<T, M, L>::deleter::operator()(T *)
{
if (m_lock.owns_lock()) {
m_lock.unlock();
}
}
template <typename T, typename M, typename L>
class shared_guarded<T, M, L>::shared_deleter
{
public:
using pointer = const T *;
shared_deleter(L lock);
void operator()(const T *ptr);
private:
L m_lock;
};
template <typename T, typename M, typename L>
shared_guarded<T, M, L>::shared_deleter::shared_deleter(L lock)
: m_lock(std::move(lock))
{
}
template <typename T, typename M, typename L>
void shared_guarded<T, M, L>::shared_deleter::operator()(const T *)
{
if (m_lock.owns_lock()) {
m_lock.unlock();
}
}
template <typename T, typename M, typename L>
template <typename... Us>
shared_guarded<T, M, L>::shared_guarded(Us &&... data)
: m_obj(std::forward<Us>(data)...)
{
}
template <typename T, typename M, typename L>
auto shared_guarded<T, M, L>::lock() -> handle
{
std::unique_lock<M> lock(m_mutex);
return handle(&m_obj, deleter(std::move(lock)));
}
template <typename T, typename M, typename L>
auto shared_guarded<T, M, L>::try_lock() -> handle
{
std::unique_lock<M> lock(m_mutex, std::try_to_lock);
if (lock.owns_lock()) {
return handle(&m_obj, deleter(std::move(lock)));
} else {
return handle(nullptr, deleter(std::move(lock)));
}
}
template <typename T, typename M, typename L>
template <typename Duration>
auto shared_guarded<T, M, L>::try_lock_for(const Duration &duration) -> handle
{
std::unique_lock<M> lock(m_mutex, duration);
if (lock.owns_lock()) {
return handle(&m_obj, deleter(std::move(lock)));
} else {
return handle(nullptr, deleter(std::move(lock)));
}
}
template <typename T, typename M, typename L>
template <typename TimePoint>
auto shared_guarded<T, M, L>::try_lock_until(const TimePoint &timepoint) -> handle
{
std::unique_lock<M> lock(m_mutex, timepoint);
if (lock.owns_lock()) {
return handle(&m_obj, deleter(std::move(lock)));
} else {
return handle(nullptr, deleter(std::move(lock)));
}
}
template <typename T, typename M, typename L>
auto shared_guarded<T, M, L>::lock_shared() const -> shared_handle
{
L lock(m_mutex);
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
}
template <typename T, typename M, typename L>
auto shared_guarded<T, M, L>::try_lock_shared() const -> shared_handle
{
L lock(m_mutex, std::try_to_lock);
if (lock.owns_lock()) {
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
} else {
return shared_handle(nullptr, shared_deleter(std::move(lock)));
}
}
template <typename T, typename M, typename L>
template <typename Duration>
auto shared_guarded<T, M, L>::try_lock_shared_for(const Duration &d) const -> shared_handle
{
L lock(m_mutex, d);
if (lock.owns_lock()) {
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
} else {
return shared_handle(nullptr, shared_deleter(std::move(lock)));
}
}
template <typename T, typename M, typename L>
template <typename TimePoint>
auto shared_guarded<T, M, L>::try_lock_shared_until(const TimePoint &tp) const -> shared_handle
{
L lock(m_mutex, tp);
if (lock.owns_lock()) {
return shared_handle(&m_obj, shared_deleter(std::move(lock)));
} else {
return shared_handle(nullptr, shared_deleter(std::move(lock)));
}
}
} // namespace libguarded
#endif