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

199 lines
5.6 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_PLAIN_GUARDED_H
#define CSLIBGUARDED_PLAIN_GUARDED_H
#include <memory>
#include <mutex>
namespace libguarded
{
/**
\headerfile cs_plain_guarded.h <CsLibGuarded/cs_plain_guarded.h>
This templated class wraps an object and allows only one thread at a
time to access the protected object.
This class will use std::mutex for the internal locking mechanism by
default. Other classes which are useful for the mutex type are
std::recursive_mutex, std::timed_mutex, and
std::recursive_timed_mutex.
The handle returned by the various lock methods is moveable but not
copyable.
*/
template <typename T, typename M = std::mutex>
class plain_guarded
{
private:
class deleter;
public:
using handle = std::unique_ptr<T, deleter>;
/**
Construct a guarded object. This constructor will accept any
number of parameters, all of which are forwarded to the
constructor of T.
*/
template <typename... Us>
plain_guarded(Us &&... data);
/**
Acquire a handle to the protected object. As a side effect, the
protected object will be locked from access by any other
thread. The lock will be automatically released when the handle
is destroyed.
*/
[[nodiscard]] handle lock();
/**
Attempt to acquire a handle to the protected object. Returns a
null handle if the object is already locked. As a side effect,
the protected object will be locked from access by any other
thread. The lock will be automatically released when the handle
is destroyed.
*/
[[nodiscard]] handle try_lock();
/**
Attempt to acquire a handle to the protected object. As a side
effect, the protected object will be locked from access by any
other thread. The lock will be automatically released when the
handle is destroyed.
Returns a null handle if the object is already locked, and does
not become available for locking before the time duration has
elapsed.
Calling this method requires that the underlying mutex type M
supports the try_lock_for method. This is not true if M is the
default std::mutex.
*/
template <class Duration>
[[nodiscard]] handle try_lock_for(const Duration &duration);
/**
Attempt to acquire a handle to the protected object. As a side
effect, the protected object will be locked from access by any other
thread. The lock will be automatically released when the handle is
destroyed.
Returns a null handle if the object is already locked, and does not
become available for locking before reaching the specified timepoint.
Calling this method requires that the underlying mutex type M
supports the try_lock_until method. This is not true if M is the
default std::mutex.
*/
template <class TimePoint>
[[nodiscard]] handle try_lock_until(const TimePoint &timepoint);
private:
T m_obj;
M m_mutex;
};
template <typename T, typename M>
class plain_guarded<T, M>::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>
plain_guarded<T, M>::deleter::deleter(std::unique_lock<M> lock)
: m_lock(std::move(lock))
{
}
template <typename T, typename M>
void plain_guarded<T, M>::deleter::operator()(T *)
{
if (m_lock.owns_lock()) {
m_lock.unlock();
}
}
template <typename T, typename M>
template <typename... Us>
plain_guarded<T, M>::plain_guarded(Us &&... data)
: m_obj(std::forward<Us>(data)...)
{
}
template <typename T, typename M>
auto plain_guarded<T, M>::lock() -> handle
{
std::unique_lock<M> lock(m_mutex);
return handle(&m_obj, deleter(std::move(lock)));
}
template <typename T, typename M>
auto plain_guarded<T, M>::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>
template <typename Duration>
auto plain_guarded<T, M>::try_lock_for(const Duration &d) -> handle
{
std::unique_lock<M> lock(m_mutex, d);
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>
template <typename TimePoint>
auto plain_guarded<T, M>::try_lock_until(const TimePoint &tp) -> handle
{
std::unique_lock<M> lock(m_mutex, tp);
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 = std::mutex>
using guarded [[deprecated("renamed to plain_guarded")]] = plain_guarded<T, M>;
} // namespace libguarded
#endif