/*********************************************************************** * * 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 #include namespace libguarded { /** \headerfile 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 > class shared_guarded { private: class deleter; class shared_deleter; public: using handle = std::unique_ptr; using shared_handle = std::unique_ptr; template shared_guarded(Us &&... data); // exclusive access [[nodiscard]] handle lock(); [[nodiscard]] handle try_lock(); template [[nodiscard]] handle try_lock_for(const Duration &duration); template [[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 [[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const; template [[nodiscard]] shared_handle try_lock_shared_until(const TimePoint &timepoint) const; private: T m_obj; mutable M m_mutex; }; template class shared_guarded::deleter { public: using pointer = T *; deleter(std::unique_lock lock); void operator()(T *ptr); private: std::unique_lock m_lock; }; template shared_guarded::deleter::deleter(std::unique_lock lock) : m_lock(std::move(lock)) { } template void shared_guarded::deleter::operator()(T *) { if (m_lock.owns_lock()) { m_lock.unlock(); } } template class shared_guarded::shared_deleter { public: using pointer = const T *; shared_deleter(L lock); void operator()(const T *ptr); private: L m_lock; }; template shared_guarded::shared_deleter::shared_deleter(L lock) : m_lock(std::move(lock)) { } template void shared_guarded::shared_deleter::operator()(const T *) { if (m_lock.owns_lock()) { m_lock.unlock(); } } template template shared_guarded::shared_guarded(Us &&... data) : m_obj(std::forward(data)...) { } template auto shared_guarded::lock() -> handle { std::unique_lock lock(m_mutex); return handle(&m_obj, deleter(std::move(lock))); } template auto shared_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 shared_guarded::try_lock_for(const Duration &duration) -> handle { std::unique_lock 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 template auto shared_guarded::try_lock_until(const TimePoint &timepoint) -> handle { std::unique_lock 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 auto shared_guarded::lock_shared() const -> shared_handle { L lock(m_mutex); return shared_handle(&m_obj, shared_deleter(std::move(lock))); } template auto shared_guarded::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 template auto shared_guarded::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 template auto shared_guarded::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