176 lines
5.4 KiB
C++
176 lines
5.4 KiB
C++
/*//////////////////////////////////////////////////////////////////////////////
|
|
// 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 <mutex>
|
|
#include <condition_variable>
|
|
#include <chrono>
|
|
#include <utility>
|
|
|
|
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 Monitor class provides a base for objects that need thread-safe access.
|
|
*/
|
|
template <typename T>
|
|
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 <typename F>
|
|
auto Access(F f) -> decltype(f(*m_Resource)) {
|
|
Mutex::ScopedLock lock(m_Mutex);
|
|
return f(*m_Resource);
|
|
}
|
|
};
|
|
|
|
} // namespace uLib
|
|
|
|
#endif // U_CORE_MONITOR_H
|