/*********************************************************************** * * 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 #include namespace libguarded { /** \headerfile 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 class plain_guarded { private: class deleter; public: using handle = std::unique_ptr; /** Construct a guarded object. This constructor will accept any number of parameters, all of which are forwarded to the constructor of T. */ template 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 [[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 [[nodiscard]] handle try_lock_until(const TimePoint &timepoint); private: T m_obj; M m_mutex; }; template class plain_guarded::deleter { public: using pointer = T *; deleter(std::unique_lock lock); void operator()(T *ptr); private: std::unique_lock m_lock; }; template plain_guarded::deleter::deleter(std::unique_lock lock) : m_lock(std::move(lock)) { } template void plain_guarded::deleter::operator()(T *) { if (m_lock.owns_lock()) { m_lock.unlock(); } } template template plain_guarded::plain_guarded(Us &&... data) : m_obj(std::forward(data)...) { } template auto plain_guarded::lock() -> handle { std::unique_lock lock(m_mutex); return handle(&m_obj, deleter(std::move(lock))); } template auto plain_guarded::try_lock() -> handle { std::unique_lock 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 template auto plain_guarded::try_lock_for(const Duration &d) -> handle { std::unique_lock 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 template auto plain_guarded::try_lock_until(const TimePoint &tp) -> handle { std::unique_lock 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 using guarded [[deprecated("renamed to plain_guarded")]] = plain_guarded; } // namespace libguarded #endif