235 lines
6.1 KiB
C++
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
|