/*////////////////////////////////////////////////////////////////////////////// // CMT Cosmic Muon Tomography project ////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova All rights reserved Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it > ------------------------------------------------------------------ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. This library 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. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. //////////////////////////////////////////////////////////////////////////////*/ #ifndef U_CORE_MONITOR_H #define U_CORE_MONITOR_H #include #include #include #include namespace uLib { /** * @brief Mutex class wraps std::timed_mutex and is used for thread synchronization. */ class Mutex { public: Mutex() = default; ~Mutex() = default; /** @brief Locks the mutex, blocking if necessary. */ void Lock() { m_Mutex.lock(); } /** @brief Unlocks the mutex. */ void Unlock() { m_Mutex.unlock(); } /** @brief Tries to lock the mutex without blocking. */ bool TryLock() { return m_Mutex.try_lock(); } /** @brief Tries to lock the mutex within a timeout in milliseconds. */ bool TryLockFor(int timeout_ms) { if (timeout_ms < 0) { Lock(); return true; } return m_Mutex.try_lock_for(std::chrono::milliseconds(timeout_ms)); } /** @brief RAII helper for scoped locking. */ class ScopedLock { public: ScopedLock(Mutex &mutex) : m_Mutex(mutex) { m_Mutex.Lock(); } ~ScopedLock() { m_Mutex.Unlock(); } private: Mutex &m_Mutex; // Non-copyable ScopedLock(const ScopedLock&) = delete; ScopedLock& operator=(const ScopedLock&) = delete; }; /** @brief Returns the underlying std::timed_mutex. */ std::timed_mutex& GetNative() { return m_Mutex; } private: std::timed_mutex m_Mutex; // Non-copyable Mutex(const Mutex &) = delete; Mutex &operator=(const Mutex &) = delete; }; namespace detail { /** @brief Internal implementation for the ULIB_MUTEX_LOCK macros. */ class ScopedTimedLock { public: ScopedTimedLock(Mutex& mutex, int timeout_ms) : m_RawMutex(nullptr), m_MutexWrapper(&mutex), m_Locked(false) { m_Locked = m_MutexWrapper->TryLockFor(timeout_ms); } ScopedTimedLock(std::timed_mutex& mutex, int timeout_ms) : m_RawMutex(&mutex), m_MutexWrapper(nullptr), m_Locked(false) { if (timeout_ms < 0) { m_RawMutex->lock(); m_Locked = true; } else m_Locked = m_RawMutex->try_lock_for(std::chrono::milliseconds(timeout_ms)); } ~ScopedTimedLock() { if (m_Locked) { if (m_RawMutex) m_RawMutex->unlock(); else if (m_MutexWrapper) m_MutexWrapper->Unlock(); } } operator bool() const { return m_Locked; } void unlock() { if (m_Locked) { if (m_RawMutex) m_RawMutex->unlock(); else if (m_MutexWrapper) m_MutexWrapper->Unlock(); m_Locked = false; } } private: std::timed_mutex* m_RawMutex = nullptr; Mutex* m_MutexWrapper = nullptr; bool m_Locked; // Non-copyable/movable to be safe in the 'for' loop ScopedTimedLock(const ScopedTimedLock&) = delete; ScopedTimedLock& operator=(const ScopedTimedLock&) = delete; ScopedTimedLock(ScopedTimedLock&&) = default; }; inline ScopedTimedLock makeScopedMutexLock(Mutex& mutex, int timeout_ms) { return ScopedTimedLock(mutex, timeout_ms); } inline ScopedTimedLock makeScopedMutexLock(std::timed_mutex& mutex, int timeout_ms) { return ScopedTimedLock(mutex, timeout_ms); } } // namespace detail /** * @brief Macro for block-scoped locking of a static mutex. * @param timeout Timeout in ms (-1 for infinite). */ #define ULIB_STATIC_LOCK(timeout) \ static std::timed_mutex __ulib_static_mutex; \ for (auto __ulib_lock = uLib::detail::makeScopedMutexLock(__ulib_static_mutex, timeout); \ __ulib_lock; \ __ulib_lock.unlock()) /** * @brief Macro for block-scoped locking of a provided mutex. * @param mutex The uLib::Mutex or std::timed_mutex to lock. * @param timeout Timeout in ms (-1 for infinite). */ #define ULIB_MUTEX_LOCK(mutex, timeout) \ for (auto __ulib_lock = uLib::detail::makeScopedMutexLock(mutex, timeout); \ __ulib_lock; \ __ulib_lock.unlock()) /** * @brief RecursiveMutex class wraps std::recursive_timed_mutex. */ class RecursiveMutex { public: RecursiveMutex() = default; ~RecursiveMutex() = default; /** @brief Locks the mutex, blocking if necessary. */ void Lock() { m_Mutex.lock(); } /** @brief Unlocks the mutex. */ void Unlock() { m_Mutex.unlock(); } /** @brief Tries to lock the mutex without blocking. */ bool TryLock() { return m_Mutex.try_lock(); } /** @brief Tries to lock the mutex within a timeout in milliseconds. */ bool TryLockFor(int timeout_ms) { if (timeout_ms < 0) { Lock(); return true; } return m_Mutex.try_lock_for(std::chrono::milliseconds(timeout_ms)); } /** @brief RAII helper for scoped locking. */ class ScopedLock { public: ScopedLock(RecursiveMutex &mutex) : m_Mutex(mutex) { m_Mutex.Lock(); } ~ScopedLock() { m_Mutex.Unlock(); } private: RecursiveMutex &m_Mutex; ScopedLock(const ScopedLock&) = delete; ScopedLock& operator=(const ScopedLock&) = delete; }; private: std::recursive_timed_mutex m_Mutex; RecursiveMutex(const RecursiveMutex &) = delete; RecursiveMutex &operator=(const RecursiveMutex &) = delete; }; /** * @brief Monitor class provides a base for objects that need thread-safe access. */ template class Monitor { protected: T* m_Resource; Mutex m_Mutex; public: Monitor(T* resource) : m_Resource(resource) {} virtual ~Monitor() { delete m_Resource; } /** @brief Thread-safe access to the resource through a lambda. */ template auto Access(F f) -> decltype(f(*m_Resource)) { Mutex::ScopedLock lock(m_Mutex); return f(*m_Resource); } }; } // namespace uLib #endif // U_CORE_MONITOR_H