threads and monitor
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
#include "HEP/Detectors/DetectorChamber.h"
|
#include "HEP/Detectors/DetectorChamber.h"
|
||||||
#include "Vtk/HEP/Detectors/vtkDetectorChamber.h"
|
#include "Vtk/HEP/Detectors/vtkDetectorChamber.h"
|
||||||
|
|
||||||
#include <Vtk/vtkContainerBox.h>
|
#include <Vtk/Math/vtkContainerBox.h>
|
||||||
#include <Vtk/vtkQViewport.h>
|
#include <Vtk/vtkQViewport.h>
|
||||||
|
|
||||||
#include "Core/ObjectsContext.h"
|
#include "Core/ObjectsContext.h"
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ set(HEADERS
|
|||||||
SmartPointer.h
|
SmartPointer.h
|
||||||
StaticInterface.h
|
StaticInterface.h
|
||||||
StringReader.h
|
StringReader.h
|
||||||
|
Threads.h
|
||||||
|
Monitor.h
|
||||||
Types.h
|
Types.h
|
||||||
Uuid.h
|
Uuid.h
|
||||||
Vector.h
|
Vector.h
|
||||||
@@ -34,6 +36,7 @@ set(SOURCES
|
|||||||
Serializable.cpp
|
Serializable.cpp
|
||||||
Signal.cpp
|
Signal.cpp
|
||||||
Uuid.cpp
|
Uuid.cpp
|
||||||
|
Threads.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBRARIES Boost::program_options Boost::serialization)
|
set(LIBRARIES Boost::program_options Boost::serialization)
|
||||||
|
|||||||
175
src/Core/Monitor.h
Normal file
175
src/Core/Monitor.h
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/*//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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
|
||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
Vector<Slot> slov;
|
Vector<Slot> slov;
|
||||||
std::vector<PropertyBase*> m_Properties;
|
std::vector<PropertyBase*> m_Properties;
|
||||||
std::vector<PropertyBase*> m_DynamicProperties;
|
std::vector<PropertyBase*> m_DynamicProperties;
|
||||||
|
bool m_SignalsBlocked;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Implementations of Property methods
|
// Implementations of Property methods
|
||||||
@@ -114,9 +115,14 @@ template void Object::serialize(Archive::log_archive &, const unsigned int);
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// OBJECT IMPLEMENTATION
|
// OBJECT IMPLEMENTATION
|
||||||
|
|
||||||
Object::Object() : d(new ObjectPrivate) {}
|
Object::Object() : d(new ObjectPrivate) {
|
||||||
|
d->m_SignalsBlocked = false;
|
||||||
|
}
|
||||||
Object::Object(const Object ©) : d(new ObjectPrivate) {
|
Object::Object(const Object ©) : d(new ObjectPrivate) {
|
||||||
if (copy.d) d->m_InstanceName = copy.d->m_InstanceName;
|
if (copy.d) {
|
||||||
|
d->m_InstanceName = copy.d->m_InstanceName;
|
||||||
|
d->m_SignalsBlocked = copy.d->m_SignalsBlocked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::~Object() {
|
Object::~Object() {
|
||||||
@@ -209,6 +215,16 @@ void Object::SetInstanceName(const std::string& name) {
|
|||||||
d->m_InstanceName = name;
|
d->m_InstanceName = name;
|
||||||
this->Updated();
|
this->Updated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Object::blockSignals(bool block) {
|
||||||
|
bool old = d->m_SignalsBlocked;
|
||||||
|
d->m_SignalsBlocked = block;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Object::signalsBlocked() const {
|
||||||
|
return d->m_SignalsBlocked;
|
||||||
|
}
|
||||||
|
|
||||||
// std::ostream &
|
// std::ostream &
|
||||||
// operator << (std::ostream &os, uLib::Object &ob)
|
// operator << (std::ostream &os, uLib::Object &ob)
|
||||||
|
|||||||
@@ -81,6 +81,12 @@ public:
|
|||||||
|
|
||||||
const std::string& GetInstanceName() const;
|
const std::string& GetInstanceName() const;
|
||||||
void SetInstanceName(const std::string& name);
|
void SetInstanceName(const std::string& name);
|
||||||
|
|
||||||
|
/** @brief Temporarily blocks all signal emissions from this object. Returns previous state. */
|
||||||
|
bool blockSignals(bool block);
|
||||||
|
|
||||||
|
/** @brief Checks if signals are currently blocked. */
|
||||||
|
bool signalsBlocked() const;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// PROPERTIES //
|
// PROPERTIES //
|
||||||
@@ -138,24 +144,22 @@ public:
|
|||||||
|
|
||||||
// Qt5 style connector //
|
// Qt5 style connector //
|
||||||
template <typename Func1, typename Func2>
|
template <typename Func1, typename Func2>
|
||||||
static bool
|
static Connection
|
||||||
connect(typename FunctionPointer<Func1>::Object *sender, Func1 sigf,
|
connect(typename FunctionPointer<Func1>::Object *sender, Func1 sigf,
|
||||||
typename FunctionPointer<Func2>::Object *receiver, Func2 slof) {
|
typename FunctionPointer<Func2>::Object *receiver, Func2 slof) {
|
||||||
SignalBase *sigb = sender->findOrAddSignal(sigf);
|
SignalBase *sigb = sender->findOrAddSignal(sigf);
|
||||||
ConnectSignal<typename FunctionPointer<Func1>::SignalSignature>(sigb, slof,
|
return ConnectSignal<typename FunctionPointer<Func1>::SignalSignature>(sigb, slof,
|
||||||
receiver);
|
receiver);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lambda/Function object connector //
|
// Lambda/Function object connector //
|
||||||
template <typename Func1, typename SlotT>
|
template <typename Func1, typename SlotT>
|
||||||
static bool connect(typename FunctionPointer<Func1>::Object *sender,
|
static Connection connect(typename FunctionPointer<Func1>::Object *sender,
|
||||||
Func1 sigf, SlotT slof) {
|
Func1 sigf, SlotT slof) {
|
||||||
SignalBase *sigb = sender->findOrAddSignal(sigf);
|
SignalBase *sigb = sender->findOrAddSignal(sigf);
|
||||||
typedef typename FunctionPointer<Func1>::SignalSignature SigSignature;
|
typedef typename FunctionPointer<Func1>::SignalSignature SigSignature;
|
||||||
typedef typename Signal<SigSignature>::type SigT;
|
typedef typename Signal<SigSignature>::type SigT;
|
||||||
reinterpret_cast<SigT *>(sigb)->connect(slof);
|
return reinterpret_cast<SigT *>(sigb)->connect(slof);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Func1, typename Func2>
|
template <typename Func1, typename Func2>
|
||||||
@@ -167,10 +171,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FuncT>
|
template <typename FuncT>
|
||||||
static inline bool connect(SignalBase *sigb, FuncT slof, Object *receiver) {
|
static inline Connection connect(SignalBase *sigb, FuncT slof, Object *receiver) {
|
||||||
ConnectSignal<typename FunctionPointer<FuncT>::SignalSignature>(sigb, slof,
|
return ConnectSignal<typename FunctionPointer<FuncT>::SignalSignature>(sigb, slof,
|
||||||
receiver);
|
receiver);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FuncT>
|
template <typename FuncT>
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
#include <boost/signals2/signal.hpp>
|
#include <boost/signals2/signal.hpp>
|
||||||
#include <boost/signals2/signal_type.hpp>
|
#include <boost/signals2/signal_type.hpp>
|
||||||
#include <boost/signals2/slot.hpp>
|
#include <boost/signals2/slot.hpp>
|
||||||
|
#include <boost/signals2/connection.hpp>
|
||||||
|
#include <boost/signals2/shared_connection_block.hpp>
|
||||||
|
|
||||||
#include "Function.h"
|
#include "Function.h"
|
||||||
#include <boost/bind/bind.hpp>
|
#include <boost/bind/bind.hpp>
|
||||||
@@ -63,9 +65,11 @@ using namespace boost::placeholders;
|
|||||||
|
|
||||||
#define _ULIB_DETAIL_SIGNAL_EMIT(_name, ...) \
|
#define _ULIB_DETAIL_SIGNAL_EMIT(_name, ...) \
|
||||||
do { \
|
do { \
|
||||||
BOOST_AUTO(sig, this->findOrAddSignal(&_name)); \
|
if (!this->signalsBlocked()) { \
|
||||||
if (sig) \
|
BOOST_AUTO(sig, this->findOrAddSignal(&_name)); \
|
||||||
sig->operator()(__VA_ARGS__); \
|
if (sig) \
|
||||||
|
sig->operator()(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,6 +94,7 @@ namespace uLib {
|
|||||||
// TODO ...
|
// TODO ...
|
||||||
|
|
||||||
typedef boost::signals2::signal_base SignalBase;
|
typedef boost::signals2::signal_base SignalBase;
|
||||||
|
typedef boost::signals2::connection Connection;
|
||||||
|
|
||||||
template <typename T> struct Signal {
|
template <typename T> struct Signal {
|
||||||
typedef boost::signals2::signal<T> type;
|
typedef boost::signals2::signal<T> type;
|
||||||
@@ -104,57 +109,57 @@ struct ConnectSignal {};
|
|||||||
|
|
||||||
template <typename FuncT, typename SigSignature>
|
template <typename FuncT, typename SigSignature>
|
||||||
struct ConnectSignal<FuncT, SigSignature, 0> {
|
struct ConnectSignal<FuncT, SigSignature, 0> {
|
||||||
static void connect(SignalBase *sigb, FuncT slof,
|
static Connection connect(SignalBase *sigb, FuncT slof,
|
||||||
typename FunctionPointer<FuncT>::Object *receiver) {
|
typename FunctionPointer<FuncT>::Object *receiver) {
|
||||||
typedef typename Signal<SigSignature>::type SigT;
|
typedef typename Signal<SigSignature>::type SigT;
|
||||||
reinterpret_cast<SigT *>(sigb)->connect(slof);
|
return reinterpret_cast<SigT *>(sigb)->connect(slof);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename FuncT, typename SigSignature>
|
template <typename FuncT, typename SigSignature>
|
||||||
struct ConnectSignal<FuncT, SigSignature, 1> {
|
struct ConnectSignal<FuncT, SigSignature, 1> {
|
||||||
static void connect(SignalBase *sigb, FuncT slof,
|
static Connection connect(SignalBase *sigb, FuncT slof,
|
||||||
typename FunctionPointer<FuncT>::Object *receiver) {
|
typename FunctionPointer<FuncT>::Object *receiver) {
|
||||||
typedef typename Signal<SigSignature>::type SigT;
|
typedef typename Signal<SigSignature>::type SigT;
|
||||||
reinterpret_cast<SigT *>(sigb)->connect(boost::bind(slof, receiver));
|
return reinterpret_cast<SigT *>(sigb)->connect(boost::bind(slof, receiver));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename FuncT, typename SigSignature>
|
template <typename FuncT, typename SigSignature>
|
||||||
struct ConnectSignal<FuncT, SigSignature, 2> {
|
struct ConnectSignal<FuncT, SigSignature, 2> {
|
||||||
static void connect(SignalBase *sigb, FuncT slof,
|
static Connection connect(SignalBase *sigb, FuncT slof,
|
||||||
typename FunctionPointer<FuncT>::Object *receiver) {
|
typename FunctionPointer<FuncT>::Object *receiver) {
|
||||||
typedef typename Signal<SigSignature>::type SigT;
|
typedef typename Signal<SigSignature>::type SigT;
|
||||||
reinterpret_cast<SigT *>(sigb)->connect(boost::bind(slof, receiver, _1));
|
return reinterpret_cast<SigT *>(sigb)->connect(boost::bind(slof, receiver, _1));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename FuncT, typename SigSignature>
|
template <typename FuncT, typename SigSignature>
|
||||||
struct ConnectSignal<FuncT, SigSignature, 3> {
|
struct ConnectSignal<FuncT, SigSignature, 3> {
|
||||||
static void connect(SignalBase *sigb, FuncT slof,
|
static Connection connect(SignalBase *sigb, FuncT slof,
|
||||||
typename FunctionPointer<FuncT>::Object *receiver) {
|
typename FunctionPointer<FuncT>::Object *receiver) {
|
||||||
typedef typename Signal<SigSignature>::type SigT;
|
typedef typename Signal<SigSignature>::type SigT;
|
||||||
reinterpret_cast<SigT *>(sigb)->connect(
|
return reinterpret_cast<SigT *>(sigb)->connect(
|
||||||
boost::bind(slof, receiver, _1, _2));
|
boost::bind(slof, receiver, _1, _2));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename FuncT, typename SigSignature>
|
template <typename FuncT, typename SigSignature>
|
||||||
struct ConnectSignal<FuncT, SigSignature, 4> {
|
struct ConnectSignal<FuncT, SigSignature, 4> {
|
||||||
static void connect(SignalBase *sigb, FuncT slof,
|
static Connection connect(SignalBase *sigb, FuncT slof,
|
||||||
typename FunctionPointer<FuncT>::Object *receiver) {
|
typename FunctionPointer<FuncT>::Object *receiver) {
|
||||||
typedef typename Signal<SigSignature>::type SigT;
|
typedef typename Signal<SigSignature>::type SigT;
|
||||||
reinterpret_cast<SigT *>(sigb)->connect(
|
return reinterpret_cast<SigT *>(sigb)->connect(
|
||||||
boost::bind(slof, receiver, _1, _2, _3));
|
boost::bind(slof, receiver, _1, _2, _3));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename FuncT, typename SigSignature>
|
template <typename FuncT, typename SigSignature>
|
||||||
struct ConnectSignal<FuncT, SigSignature, 5> {
|
struct ConnectSignal<FuncT, SigSignature, 5> {
|
||||||
static void connect(SignalBase *sigb, FuncT slof,
|
static Connection connect(SignalBase *sigb, FuncT slof,
|
||||||
typename FunctionPointer<FuncT>::Object *receiver) {
|
typename FunctionPointer<FuncT>::Object *receiver) {
|
||||||
typedef typename Signal<SigSignature>::type SigT;
|
typedef typename Signal<SigSignature>::type SigT;
|
||||||
reinterpret_cast<SigT *>(sigb)->connect(
|
return reinterpret_cast<SigT *>(sigb)->connect(
|
||||||
boost::bind(slof, receiver, _1, _2, _3, _4));
|
boost::bind(slof, receiver, _1, _2, _3, _4));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -167,11 +172,11 @@ template <typename FuncT> SignalBase *NewSignal(FuncT f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename SigSignature, typename FuncT>
|
template <typename SigSignature, typename FuncT>
|
||||||
void ConnectSignal(SignalBase *sigb, FuncT slof,
|
Connection ConnectSignal(SignalBase *sigb, FuncT slof,
|
||||||
typename FunctionPointer<FuncT>::Object *receiver) {
|
typename FunctionPointer<FuncT>::Object *receiver) {
|
||||||
detail::ConnectSignal<FuncT, SigSignature,
|
return detail::ConnectSignal<FuncT, SigSignature,
|
||||||
FunctionPointer<FuncT>::arity>::connect(sigb, slof,
|
FunctionPointer<FuncT>::arity>::connect(sigb, slof,
|
||||||
receiver);
|
receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace uLib
|
} // namespace uLib
|
||||||
|
|||||||
84
src/Core/Threads.cpp
Normal file
84
src/Core/Threads.cpp
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
#include "Threads.h"
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
|
||||||
|
Thread::Thread() : m_Running(false) {}
|
||||||
|
|
||||||
|
Thread::~Thread() {
|
||||||
|
if (m_Thread.joinable()) {
|
||||||
|
m_Thread.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::Start() {
|
||||||
|
Mutex::ScopedLock lock(m_ThreadMutex);
|
||||||
|
if (m_Running) return;
|
||||||
|
|
||||||
|
m_Running = true;
|
||||||
|
m_Thread = std::thread(&Thread::ThreadEntryPoint, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::Join() {
|
||||||
|
if (m_Thread.joinable()) {
|
||||||
|
m_Thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::Detach() {
|
||||||
|
if (m_Thread.joinable()) {
|
||||||
|
m_Thread.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::IsJoinable() const {
|
||||||
|
return m_Thread.joinable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::IsRunning() const {
|
||||||
|
return m_Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::Run() {
|
||||||
|
// Override in subclasses
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::ThreadEntryPoint() {
|
||||||
|
this->Run();
|
||||||
|
m_Running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::Sleep(int milliseconds) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::Yield() {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace uLib
|
||||||
80
src/Core/Threads.h
Normal file
80
src/Core/Threads.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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_THREADS_H
|
||||||
|
#define U_CORE_THREADS_H
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <functional>
|
||||||
|
#include <atomic>
|
||||||
|
#include "Core/Monitor.h"
|
||||||
|
#include "Core/Object.h"
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Thread class wraps std::thread and provides a common interface.
|
||||||
|
*/
|
||||||
|
class Thread : public Object {
|
||||||
|
public:
|
||||||
|
Thread();
|
||||||
|
virtual ~Thread();
|
||||||
|
|
||||||
|
/** @brief Starts the thread by calling Run(). */
|
||||||
|
void Start();
|
||||||
|
|
||||||
|
/** @brief Joins the thread. */
|
||||||
|
void Join();
|
||||||
|
|
||||||
|
/** @brief Detaches the thread. */
|
||||||
|
void Detach();
|
||||||
|
|
||||||
|
/** @brief Returns true if the thread is currently joinable. */
|
||||||
|
bool IsJoinable() const;
|
||||||
|
|
||||||
|
/** @brief Returns true if the thread is currently running. */
|
||||||
|
bool IsRunning() const;
|
||||||
|
|
||||||
|
/** @brief The entry point for the thread. Override this in subclasses. */
|
||||||
|
virtual void Run();
|
||||||
|
|
||||||
|
/** @brief Static helper to sleep the current thread. */
|
||||||
|
static void Sleep(int milliseconds);
|
||||||
|
|
||||||
|
/** @brief Static helper to yield the current thread. */
|
||||||
|
static void Yield();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Internal thread entry point
|
||||||
|
void ThreadEntryPoint();
|
||||||
|
|
||||||
|
std::thread m_Thread;
|
||||||
|
std::atomic<bool> m_Running;
|
||||||
|
mutable Mutex m_ThreadMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace uLib
|
||||||
|
|
||||||
|
#endif // U_CORE_THREADS_H
|
||||||
@@ -23,6 +23,8 @@ set( TESTS
|
|||||||
VectorMetaAllocatorTest
|
VectorMetaAllocatorTest
|
||||||
PropertyTypesTest
|
PropertyTypesTest
|
||||||
HRPTest
|
HRPTest
|
||||||
|
MutexTest
|
||||||
|
ThreadsTest
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBRARIES
|
set(LIBRARIES
|
||||||
|
|||||||
108
src/Core/testing/MutexTest.cpp
Normal file
108
src/Core/testing/MutexTest.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "Core/Monitor.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
void TestBasicLock() {
|
||||||
|
std::cout << "Testing basic Mutex Lock/Unlock..." << std::endl;
|
||||||
|
Mutex m;
|
||||||
|
m.Lock();
|
||||||
|
m.Unlock();
|
||||||
|
assert(m.TryLock());
|
||||||
|
m.Unlock();
|
||||||
|
std::cout << " Passed." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestScopedLock() {
|
||||||
|
std::cout << "Testing Mutex::ScopedLock..." << std::endl;
|
||||||
|
Mutex m;
|
||||||
|
{
|
||||||
|
Mutex::ScopedLock lock(m);
|
||||||
|
assert(!m.TryLock());
|
||||||
|
}
|
||||||
|
assert(m.TryLock());
|
||||||
|
m.Unlock();
|
||||||
|
std::cout << " Passed." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTimedLock() {
|
||||||
|
std::cout << "Testing Mutex TryLockFor..." << std::endl;
|
||||||
|
Mutex m;
|
||||||
|
m.Lock();
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
bool locked = m.TryLockFor(100);
|
||||||
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||||
|
|
||||||
|
assert(!locked);
|
||||||
|
assert(diff >= 100);
|
||||||
|
m.Unlock();
|
||||||
|
std::cout << " Passed (waited " << diff << "ms)." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMacros() {
|
||||||
|
std::cout << "Testing ULIB_STATIC_LOCK and ULIB_MUTEX_LOCK macros..." << std::endl;
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
auto task = [&]() {
|
||||||
|
for(int i=0; i<500; ++i) {
|
||||||
|
ULIB_STATIC_LOCK(-1) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
for(int i=0; i<4; ++i) threads.emplace_back(task);
|
||||||
|
for(auto& t : threads) t.join();
|
||||||
|
|
||||||
|
assert(counter == 2000);
|
||||||
|
|
||||||
|
Mutex m;
|
||||||
|
int counter2 = 0;
|
||||||
|
ULIB_MUTEX_LOCK(m, -1) {
|
||||||
|
counter2++;
|
||||||
|
}
|
||||||
|
assert(counter2 == 1);
|
||||||
|
|
||||||
|
std::cout << " Passed." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMonitor() {
|
||||||
|
std::cout << "Testing Monitor pattern..." << std::endl;
|
||||||
|
struct Resource {
|
||||||
|
int value = 0;
|
||||||
|
void increment() { value++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Monitor<Resource> monitor(new Resource());
|
||||||
|
|
||||||
|
auto task = [&]() {
|
||||||
|
for(int i=0; i<1000; ++i) {
|
||||||
|
monitor.Access([](Resource& r) {
|
||||||
|
r.increment();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
for(int i=0; i<5; ++i) threads.emplace_back(task);
|
||||||
|
for(auto& t : threads) t.join();
|
||||||
|
|
||||||
|
int final_value = monitor.Access([](Resource& r) { return r.value; });
|
||||||
|
assert(final_value == 5000);
|
||||||
|
std::cout << " Passed (final value: " << final_value << ")." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
TestBasicLock();
|
||||||
|
TestScopedLock();
|
||||||
|
TestTimedLock();
|
||||||
|
TestMacros();
|
||||||
|
TestMonitor();
|
||||||
|
std::cout << "All Mutex and Monitor tests passed!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
72
src/Core/testing/ThreadsTest.cpp
Normal file
72
src/Core/testing/ThreadsTest.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "Core/Threads.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
class MyThread : public Thread {
|
||||||
|
public:
|
||||||
|
MyThread() : counter(0) {}
|
||||||
|
void Run() override {
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
counter++;
|
||||||
|
Thread::Sleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::atomic<int> counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestBasicThread() {
|
||||||
|
std::cout << "Testing basic Thread lifecycle..." << std::endl;
|
||||||
|
MyThread t;
|
||||||
|
assert(!t.IsRunning());
|
||||||
|
t.Start();
|
||||||
|
assert(t.IsRunning());
|
||||||
|
t.Join();
|
||||||
|
assert(!t.IsRunning());
|
||||||
|
assert(t.counter == 5);
|
||||||
|
std::cout << " Passed." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestThreadDetach() {
|
||||||
|
std::cout << "Testing Thread Detach..." << std::endl;
|
||||||
|
std::atomic<bool> done(false);
|
||||||
|
|
||||||
|
// Using a lambda or a simple subclass
|
||||||
|
class DetachedThread : public Thread {
|
||||||
|
public:
|
||||||
|
DetachedThread(std::atomic<bool>& d) : m_done(d) {}
|
||||||
|
void Run() override {
|
||||||
|
Thread::Sleep(50);
|
||||||
|
m_done = true;
|
||||||
|
}
|
||||||
|
std::atomic<bool>& m_done;
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
DetachedThread* t = new DetachedThread(done);
|
||||||
|
t->Start();
|
||||||
|
t->Detach();
|
||||||
|
// The thread object 't' is still alive here,
|
||||||
|
// but it will be destroyed soon if we delete it.
|
||||||
|
// For a detached thread using members, we MUST keep it alive.
|
||||||
|
|
||||||
|
int wait_count = 0;
|
||||||
|
while(!done && wait_count < 20) {
|
||||||
|
Thread::Sleep(10);
|
||||||
|
wait_count++;
|
||||||
|
}
|
||||||
|
delete t;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(done);
|
||||||
|
std::cout << " Passed." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
TestBasicThread();
|
||||||
|
TestThreadDetach();
|
||||||
|
std::cout << "All Thread tests passed!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user