diff --git a/doc/examples/pimutex.cpp b/doc/examples/pimutex.cpp index f0defdc4..bb78f95c 100644 --- a/doc/examples/pimutex.cpp +++ b/doc/examples/pimutex.cpp @@ -2,8 +2,5 @@ void _() { //! [main] -mutex.lock(); -// ... your code here -mutex.unlock(); //! [main] } diff --git a/libs/main/containers/pideque.h b/libs/main/containers/pideque.h index 53e86851..a24b8a02 100644 --- a/libs/main/containers/pideque.h +++ b/libs/main/containers/pideque.h @@ -440,6 +440,11 @@ public: } inline PIDeque & insert(size_t index, const PIDeque & other) { if (other.isEmpty()) return *this; +#ifdef USE_DEBUG + if (&other == this) { + printf("error with PIDeque<%s>::insert\n", __PIP_TYPENAME__(T)); + } +#endif assert(&other != this); bool dir = pid_rsize <= 2 ? true : (index >= pid_rsize / 2 ? true : false); if (dir) { @@ -544,6 +549,11 @@ public: inline PIDeque & append(const T & e) {return push_back(e);} inline PIDeque & append(T && e) {return push_back(std::move(e));} inline PIDeque & append(const PIDeque & v) { +#ifdef USE_DEBUG + if (&v == this) { + printf("error with PIDeque<%s>::append\n", __PIP_TYPENAME__(T)); + } +#endif assert(&v != this); size_t ps = pid_size; alloc(pid_size + v.pid_size, true); @@ -615,6 +625,11 @@ public: inline PIDeque> reshape(size_t rows, size_t cols, int order = byRow) const { PIDeque> ret; if (isEmpty()) return ret; +#ifdef USE_DEBUG + if (rows*cols != pid_size) { + printf("error with PIDeque<%s>::reshape\n", __PIP_TYPENAME__(T)); + } +#endif assert(rows*cols == pid_size); ret.resize(rows); if (order == byRow) { @@ -762,6 +777,11 @@ private: if (as != pid_rsize) { PIINTROSPECTION_CONTAINER_ALLOC(T, (as-pid_rsize)) T * p_d = (T*)(realloc((void*)(pid_data), as*sizeof(T))); +#ifdef USE_DEBUG + if (!p_d) { + printf("error with PIDeque<%s>::alloc\n", __PIP_TYPENAME__(T)); + } +#endif assert(p_d); pid_data = p_d; pid_rsize = as; diff --git a/libs/main/containers/pimap.h b/libs/main/containers/pimap.h index 0ceed77b..8b75a678 100644 --- a/libs/main/containers/pimap.h +++ b/libs/main/containers/pimap.h @@ -208,6 +208,11 @@ public: const T at(const Key & key) const {return (*this)[key];} PIMap & operator <<(const PIMap & other) { +#ifdef USE_DEBUG + if (&other == this) { + printf("error with PIMap<%s, %s>::<<\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); + } +#endif assert(&other != this); if (other.isEmpty()) return *this; if (other.size() == 1) {insert(other.pim_index[0].key, other.pim_content[0]); return *this;} diff --git a/libs/main/containers/pivector.h b/libs/main/containers/pivector.h index 4108ec6d..879ce7bf 100644 --- a/libs/main/containers/pivector.h +++ b/libs/main/containers/pivector.h @@ -1199,6 +1199,11 @@ public: //! \~\sa \a append(), \a prepend(), \a remove() inline PIVector & insert(size_t index, const PIVector & v) { if (v.isEmpty()) return *this; +#ifdef USE_DEBUG + if (&v == this) { + printf("error with PIVector<%s>::insert\n", __PIP_TYPENAME__(T)); + } +#endif assert(&v != this); ssize_t os = piv_size - index; alloc(piv_size + v.piv_size); @@ -1385,6 +1390,11 @@ public: } inline PIVector & push_back(const PIVector & v) { +#ifdef USE_DEBUG + if (&v == this) { + printf("error with PIVector<%s>::push_back\n", __PIP_TYPENAME__(T)); + } +#endif assert(&v != this); size_t ps = piv_size; alloc(piv_size + v.piv_size); @@ -1485,6 +1495,11 @@ public: inline PIVector> reshape(size_t rows, size_t cols, ReshapeOrder order = byRow) const { PIVector> ret; if (isEmpty()) return ret; +#ifdef USE_DEBUG + if (rows*cols != piv_size) { + printf("error with PIVector<%s>::reshape\n", __PIP_TYPENAME__(T)); + } +#endif assert(rows*cols == piv_size); ret.resize(rows); if (order == byRow) { @@ -1609,6 +1624,11 @@ private: if (as == piv_rsize) return; PIINTROSPECTION_CONTAINER_ALLOC(T, (as-piv_rsize)) T * p_d = (T*)(realloc((void*)(piv_data), as*sizeof(T))); +#ifdef USE_DEBUG + if (!p_d) { + printf("error with PIVector<%s>::alloc\n", __PIP_TYPENAME__(T)); + } +#endif assert(p_d); piv_data = p_d; piv_rsize = as; diff --git a/libs/main/core/pibase.h b/libs/main/core/pibase.h index e8ab90da..befa525e 100644 --- a/libs/main/core/pibase.h +++ b/libs/main/core/pibase.h @@ -248,6 +248,8 @@ #ifdef NDEBUG # undef NDEBUG +#else +# define USE_DEBUG #endif #ifndef assert # define assert(x) @@ -256,6 +258,12 @@ # define assertm(exp, msg) assert(((void)msg, exp)) #endif +#ifdef MICRO_PIP +# define __PIP_TYPENAME__(T) "?" +#else +# define __PIP_TYPENAME__(T) typeid(T).name() +#endif + #ifdef CC_GCC # undef DEPRECATED # define DEPRECATED __attribute__((deprecated)) diff --git a/libs/main/core/pibytearray.h b/libs/main/core/pibytearray.h index e7a93976..dee38aa3 100644 --- a/libs/main/core/pibytearray.h +++ b/libs/main/core/pibytearray.h @@ -32,12 +32,6 @@ #include "pivector2d.h" #include -#ifdef MICRO_PIP -# define _TYPENAME_(T) "?" -#else -# define _TYPENAME_(T) typeid(T).name() -#endif - class PIString; class PIByteArray; @@ -394,7 +388,7 @@ inline PIByteArray & operator >>(PIByteArray & s, uchar & v) {assert(s.size() >= template::value, int>::type = 0> inline PIByteArray::StreamRef operator >>(PIByteArray & s, T & v) { if (s.size() < sizeof(v)) { - printf("error with %s\n", _TYPENAME_(T)); + printf("error with %s\n", __PIP_TYPENAME__(T)); assert(s.size() >= sizeof(v)); } memcpy((void*)(&v), s.data(), sizeof(v)); @@ -430,7 +424,7 @@ template() << std::declval()), PIByteArray::StreamRef>::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIVector & v) { if (s.size_s() < 4) { - printf("error with PIVector<%s>\n", _TYPENAME_(T)); + printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 4); } int sz; s >> sz; @@ -446,7 +440,7 @@ template() << std::declval()), PIByteArray::StreamRef>::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIVector & v) { if (s.size_s() < 4) { - printf("error with PIVector<%s>\n", _TYPENAME_(T)); + printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 4); } int sz; s >> sz; @@ -463,7 +457,7 @@ template() << std::declval()), PIByteArray::StreamRef>::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIDeque & v) { if (s.size_s() < 4) { - printf("error with PIDeque<%s>\n", _TYPENAME_(T)); + printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 4); } int sz; s >> sz; @@ -479,7 +473,7 @@ template() << std::declval()), PIByteArray::StreamRef>::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIDeque & v) { if (s.size_s() < 4) { - printf("error with PIDeque<%s>\n", _TYPENAME_(T)); + printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 4); } int sz; s >> sz; @@ -496,7 +490,7 @@ template() << std::declval()), PIByteArray::StreamRef>::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIVector2D & v) { if (s.size_s() < 8) { - printf("error with PIVecto2Dr<%s>\n", _TYPENAME_(T)); + printf("error with PIVecto2Dr<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 8); } int r, c; s >> r >> c; @@ -513,7 +507,7 @@ template() << std::declval()), PIByteArray::StreamRef>::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIVector2D & v) { if (s.size_s() < 8) { - printf("error with PIVecto2Dr<%s>\n", _TYPENAME_(T)); + printf("error with PIVecto2Dr<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 8); } int r,c; @@ -581,7 +575,7 @@ inline PIByteArray & operator <<(PIByteArray & s, const PIVector2D & v) { template::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIVector & v) { if (s.size_s() < 4) { - printf("error with PIVector<%s>\n", _TYPENAME_(T)); + printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 4); } int sz; s >> sz; @@ -596,7 +590,7 @@ inline PIByteArray & operator >>(PIByteArray & s, PIVector & v) { template::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIDeque & v) { if (s.size_s() < 4) { - printf("error with PIDeque<%s>\n", _TYPENAME_(T)); + printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 4); } int sz; s >> sz; @@ -611,7 +605,7 @@ inline PIByteArray & operator >>(PIByteArray & s, PIDeque & v) { template::value, int>::type = 0> inline PIByteArray & operator >>(PIByteArray & s, PIVector2D & v) { if (s.size_s() < 8) { - printf("error with PIVecto2Dr<%s>\n", _TYPENAME_(T)); + printf("error with PIVecto2Dr<%s>\n", __PIP_TYPENAME__(T)); assert(s.size_s() >= 8); } int r,c; @@ -646,7 +640,7 @@ inline PIByteArray & operator <<(PIByteArray & s, const PIMap & v) { template inline PIByteArray & operator >>(PIByteArray & s, PIMap & v) { if (s.size_s() < 4) { - printf("error with PIMap<%s, %s>\n", _TYPENAME_(Key), _TYPENAME_(T)); + printf("error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); assert(s.size_s() >= 4); } int sz; s >> sz; v.pim_index.resize(sz); @@ -711,7 +705,4 @@ template T piDeserialize(const PIByteArray & data) { } -#undef _TYPENAME_ - - #endif // PIBYTEARRAY_H diff --git a/libs/main/thread/pimutex.cpp b/libs/main/thread/pimutex.cpp index bea01ef2..ea5f13a0 100644 --- a/libs/main/thread/pimutex.cpp +++ b/libs/main/thread/pimutex.cpp @@ -17,22 +17,67 @@ along with this program. If not, see . */ -/** \class PIMutex - * \brief Mutex - * \details - * \section PIMutex_sec0 Synopsis - * %PIMutex provides synchronization blocks between several threads. - * Using mutex guarantees execution of some code only one of threads. - * Mutex contains logic state and functions to change it: \a lock(), - * \a unlock() and \a tryLock(). - * - * \section PIMutex_sec1 Usage - * Block of code that should to be executed only one thread simultaniously - * should to be started with \a lock() and ended with \a unlock(). - * \snippet pimutex.cpp main - * "mutex" in this example is one for all threads. - * - * */ +//! \addtogroup Thread +//! \{ +//! \class PIMutex pimutex.h +//! +//! \~\brief +//! \~english Simple mutex +//! \~russian Простой мьютекс +//! +//! +//! \~\details +//! \~english \section PIMutex_sec0 Synopsis +//! \~russian \section PIMutex_sec0 Краткий обзор +//! +//! \~english +//! %PIMutex provides synchronization blocks between several threads. +//! Using mutex guarantees execution of some code only one of threads. +//! Mutex contains logic state and functions to change it: \a lock(), +//! \a unlock() and \a tryLock(). +//! +//! \~russian +//! +//! +//! \~english \section PIMutex_sec0 Usage +//! \~russian \section PIMutex_sec0 Использование +//! +//! \~english +//! Block of code that should to be executed only one thread simultaniously +//! should to be started with \a lock() and finished with \a unlock(). +//! +//! \~russian +//! +//! \~\code +//! mutex.lock(); +//! // ... your code here +//! mutex.unlock(); +//! \endcode +//! \} + + +//! \addtogroup Thread +//! \{ +//! \class PIMutexLocker pimutex.h +//! +//! \~\brief +//! \~english %PIMutex autolocker +//! \~russian Автоблокировщик %PIMutex +//! +//! +//! \~\details +//! +//! \~english +//! When a PIMutexLocker object is created, it attempts to lock the mutex it is given, if "condition" true. +//! When control leaves the scope in which the PIMutexLocker object was created, +//! the PIMutexLocker is destructed and the mutex is released, if "condition" true. +//! If "condition" false this class do nothing. +//! The PIMutexLocker class is non-copyable. +//! +//! \~russian +//! +//! \} + #include "pimutex.h" #include "piincludes_p.h" @@ -68,6 +113,11 @@ PIMutex::~PIMutex() { } +//! \~\details +//! \~english +//! If mutex is unlocked it set to locked state and returns immediate. +//! If mutex is already locked function blocks until mutex will be unlocked +//! \~russian void PIMutex::lock() { #if defined(WINDOWS) EnterCriticalSection(&(PRIVATE->mutex)); @@ -79,6 +129,10 @@ void PIMutex::lock() { } +//! \~\details +//! \~english +//! In any case this function returns immediate +//! \~russian void PIMutex::unlock() { #if defined(WINDOWS) LeaveCriticalSection(&(PRIVATE->mutex)); @@ -90,12 +144,17 @@ void PIMutex::unlock() { } +//! \~\details +//! \~english +//! If mutex is unlocked it set to locked state and returns "true" immediate. +//! If mutex is already locked function returns immediate an returns "false" +//! \~russian bool PIMutex::tryLock() { bool ret = #if defined(WINDOWS) (TryEnterCriticalSection(&(PRIVATE->mutex)) != 0); #elif defined(FREERTOS) - xSemaphoreTake(PRIVATE->mutex, 0); + xSemaphoreTake(PRIVATE->mutex, 0); #else (pthread_mutex_trylock(&(PRIVATE->mutex)) == 0); #endif diff --git a/libs/main/thread/pimutex.h b/libs/main/thread/pimutex.h index 7736a414..3438da43 100644 --- a/libs/main/thread/pimutex.h +++ b/libs/main/thread/pimutex.h @@ -34,25 +34,25 @@ class PIP_EXPORT PIMutex public: NO_COPY_CLASS(PIMutex) - //! Constructs unlocked mutex + //! \~english Constructs unlocked mutex + //! \~russian Создает незаблокированный мьютекс explicit PIMutex(); - //! Destroy mutex + //! \~english Destroy mutex + //! \~russian Деструктор мьютекса ~PIMutex(); - //! \brief Lock mutex - //! \details If mutex is unlocked it set to locked state and returns immediate. - //! If mutex is already locked function blocks until mutex will be unlocked + //! \~english Lock mutex + //! \~russian Блокирует мьютекс void lock(); - //! \brief Unlock mutex - //! \details In any case this function returns immediate - void unlock() ; + //! \~english Unlock mutex + //! \~russian Разблокирует мьютекс + void unlock(); - //! \brief Try to lock mutex - //! \details If mutex is unlocked it set to locked state and returns "true" immediate. - //! If mutex is already locked function returns immediate an returns "false" + //! \~english Try to lock mutex + //! \~russian Пробует заблокировать мьютекс bool tryLock(); void * handle(); @@ -66,22 +66,24 @@ private: }; -//! \brief PIMutexLocker -//! \details Same as std::lock_guard. -//! When a PIMutexLocker object is created, it attempts to lock the mutex it is given, if "condition" true. -//! When control leaves the scope in which the PIMutexLocker object was created, -//! the PIMutexLocker is destructed and the mutex is released, if "condition" true. -//! If "condition" false this class do nothing. -//! The PIMutexLocker class is non-copyable. class PIP_EXPORT PIMutexLocker { public: NO_COPY_CLASS(PIMutexLocker) + + //! \~english Constructs and lock "m" if "condition" is \c true + //! \~russian PIMutexLocker(PIMutex & m, bool condition = true): mutex(m), cond(condition) {if (cond) mutex.lock();} + + //! \~english Unlock "m" if "condition" was \c true + //! \~russian ~PIMutexLocker() {if (cond) mutex.unlock();} + private: PIMutex & mutex; bool cond; + }; + #endif // PIMUTEX_H diff --git a/libs/main/thread/pispinlock.cpp b/libs/main/thread/pispinlock.cpp index 3262f39b..57de73c8 100644 --- a/libs/main/thread/pispinlock.cpp +++ b/libs/main/thread/pispinlock.cpp @@ -17,14 +17,49 @@ along with this program. If not, see . */ -/** \class PISpinlock - * \brief Spinlock - * \details - * \section PISpinlock_sec0 Synopsis - * %PISpinlock provides synchronization blocks between several threads. - * PISpinlock functionality similar to PIMutex, but working on atomic - * type and \a lock() method wait with 100% CPU load. - * \note - * Use this type instead of PIMutex when less waiting time is more - * important than CPU load! - * */ +//! \addtogroup Thread +//! \{ +//! \class PISpinlock pispinlock.h +//! +//! \~\brief +//! \~english Fast and full-load lock +//! \~russian Быстрая блокировка с полной нагрузкой +//! +//! \~\details +//! \~english +//! %PISpinlock provides synchronization blocks between several threads. +//! PISpinlock functionality similar to PIMutex, but working on atomic +//! type and \a lock() method wait with 100% CPU load. +//! +//! \~russian +//! +//! \~\note +//! \~english +//! Use this type instead of PIMutex when less waiting time is more +//! important than CPU load! +//! +//! \~russian +//! +//! \} + + +//! \addtogroup Thread +//! \{ +//! \class PISpinlockLocker pispinlock.h +//! +//! \~\brief +//! \~english %PISpinlock autolocker +//! \~russian +//! +//! +//! \~\details +//! \~english +//! When a PISpinlockLocker object is created, it attempts to lock the spinlock it is given, if "condition" true. +//! When control leaves the scope in which the PISpinlockLocker object was created, +//! the PISpinlockLocker is destructed and the spinlock is released, if "condition" true. +//! If "condition" false this class do nothing. +//! The PISpinlockLocker class is non-copyable. +//! +//! \~russian +//! +//! \} diff --git a/libs/main/thread/pispinlock.h b/libs/main/thread/pispinlock.h index e1bb6f3e..258dd202 100644 --- a/libs/main/thread/pispinlock.h +++ b/libs/main/thread/pispinlock.h @@ -35,20 +35,30 @@ class PIP_EXPORT PISpinlock public: NO_COPY_CLASS(PISpinlock) - //! Constructs unlocked spinlock + //! \~english Constructs unlocked spinlock + //! \~russian explicit PISpinlock() {flag.clear();} - //! Destroy spinlock + //! \~english Destroy spinlock + //! \~russian ~PISpinlock() {} - //! \brief Lock spinlock - //! \details If spinlock is unlocked it set to locked state and returns immediate. + //! \~english Lock spinlock + //! \~russian + //! \~\details + //! \~english + //! If spinlock is unlocked it set to locked state and returns immediate. //! If spinlock is already locked function blocks until spinlock will be unlocked + //! \~russian void lock() {while (flag.test_and_set(std::memory_order_acquire));} - //! \brief Unlock spinlock - //! \details In any case this function returns immediate + //! \~english Unlock spinlock + //! \~russian + //! \~\details + //! \~english + //! In any case this function returns immediate + //! \~russian void unlock() {flag.clear(std::memory_order_release);} private: @@ -57,22 +67,24 @@ private: }; -//! \brief PISpinlockLocker -//! \details -//! When a PISpinlockLocker object is created, it attempts to lock the spinlock it is given, if "condition" true. -//! When control leaves the scope in which the PISpinlockLocker object was created, -//! the PISpinlockLocker is destructed and the spinlock is released, if "condition" true. -//! If "condition" false this class do nothing. -//! The PISpinlockLocker class is non-copyable. class PIP_EXPORT PISpinlockLocker { public: NO_COPY_CLASS(PISpinlockLocker) + + //! \~english Constructs and lock "s" if "condition" is \c true + //! \~russian PISpinlockLocker(PISpinlock & s, bool condition = true): spinlock(s), cond(condition) {if (cond) spinlock.lock();} + + //! \~english Unlock "s" if "condition" was \c true + //! \~russian ~PISpinlockLocker() {if (cond) spinlock.unlock();} + private: PISpinlock & spinlock; bool cond; + }; + #endif // PISPINLOCK_H diff --git a/libs/main/thread/pithread.cpp b/libs/main/thread/pithread.cpp index 11e6531e..0bba3e57 100644 --- a/libs/main/thread/pithread.cpp +++ b/libs/main/thread/pithread.cpp @@ -54,54 +54,85 @@ __THREAD_FUNC_RET__ thread_function_once(void * t) {((PIThread*)t)->__thread_fun # define UNREGISTER_THREAD(t) #endif -/*! \class PIThread - * \brief Thread class - * \details This class allow you exec your code in separate thread. - * - * \section PIThread_sec0 Synopsis - * Multithreading allow you to write program which will be executed - * in several threads simultaneously. This trend allow you to use all - * cores of modern processors, but there are many dangers. - * - * This class provide virtual functions \a begin(), \a run() and \a end(), - * which describes start, execution and finish work of some process. - * These functions executes in \b separate thread. When you execute - * \a start(), %PIThread create separate system thread and sequentially - * executes function \a begin(), \a run() and \a end(). You can - * reimplement each function and write your own code to execute. - * Scheme of functions executing: -\code{.cpp} -begin(); -event started(); -while (isRunning()) { - run(); - ThreadFunc(); - piMSleep(timer_delay); -} -event stopped(); -end(); -\endcode - * Unlike from directly using "pthread" or some similar you doesn`t need - * to write your own main thread cycle and sleep at every cycle end. - * %PIThread make it for you, and your job is to set sleep value from - * contructor or when starting thread, and reimplement \a begin(), \a run() - * and \a end() functions. - * - * \section PIThread_sec1 Using without subclassing - * You can use %PIThread without subclassing by using "ThreadFunc" pointer - * that can be set from constructor or by overloaded function \a start(ThreadFunc func, int timer_delay). - * If "func" if not null this function will be executed as \a run(). ThreadFunc is any static - * function with format void func(void * data). "Data" is custom data set from constructor or - * with function \a setData(). \n Also you can connect to event \a started(), but - * in this case you should to white your thread main cycle, because this event raised only one time. - * - * \section PIThread_sec2 Locking - * %PIThread has inrternal mutex that can be locked and unlocked every \a run() if you set this flag - * with function \a needLockRun(bool). Also you can access to this mutex by functions \a lock(), \a unlock() - * and \a mutex(). Using this functions together with needLockRun(true) can guarantee one-thread access to - * some data. - * - */ +//! \addtogroup Thread +//! \{ +//! \class PIThread pithread.h +//! \~\brief +//! \~english Thread class +//! \~russian Класс потока +//! +//! \~\details +//! \~english +//! This class allow you exec your code in separate thread. +//! +//! \~russian +//! Этот класс позволяет выполнять код из параллельного потока. +//! +//! +//! \~english \section PIThread_sec0 Synopsis +//! \~russian \section PIThread_sec0 Краткий обзор +//! \~english +//! Multithreading allow you to write program which will be executed +//! in several threads simultaneously. This trend allow you to use all +//! cores of modern processors, but there are many dangers. +//! +//! This class provide virtual functions \a begin(), \a run() and \a end(), +//! which describes start, execution and finish work of some process. +//! These functions executes in \b separate thread. When you execute +//! \a start(), %PIThread create separate system thread and sequentially +//! executes function \a begin(), \a run() and \a end(). You can +//! reimplement each function and write your own code to execute. +//! Scheme of functions executing: +//! +//! \~russian +//! +//! \~\code{.cpp} +//! begin(); +//! event started(); +//! while (isRunning()) { +//! run(); +//! ThreadFunc(); +//! piMSleep(timer_delay); +//! } +//! event stopped(); +//! end(); +//! \endcode +//! +//! \~english +//! Unlike from directly using "pthread" or some similar you doesn`t need +//! to write your own main thread cycle and sleep at every cycle end. +//! %PIThread make it for you, and your job is to set sleep value from +//! contructor or when starting thread, and reimplement \a begin(), \a run() +//! and \a end() functions. +//! +//! \~russian +//! +//! +//! \~english \section PIThread_sec1 Using without subclassing +//! \~russian \section PIThread_sec1 Использование без наследования +//! \~english +//! You can use %PIThread without subclassing by using "ThreadFunc" pointer +//! that can be set from constructor or by overloaded function \a start(ThreadFunc func, int timer_delay). +//! If "func" if not null this function will be executed as \a run(). ThreadFunc is any static +//! function with format void func(void * data). "Data" is custom data set from constructor or +//! with function \a setData(). \n Also you can connect to event \a started(), but +//! in this case you should to white your thread main cycle, because this event raised only one time. +//! +//! \~russian +//! +//! +//! \~english \section PIThread_sec2 Locking +//! \~russian \section PIThread_sec2 Блокировки +//! \~english +//! %PIThread has inrternal mutex that can be locked and unlocked every \a run() if you set this flag +//! with function \a needLockRun(bool). Also you can access to this mutex by functions \a lock(), \a unlock() +//! and \a mutex(). Using this functions together with needLockRun(true) can guarantee one-thread access to +//! some data. +//! +//! \~russian +//! +//! \} +//! #ifndef MICRO_PIP diff --git a/libs/main/thread/pithread.h b/libs/main/thread/pithread.h index c842a0d7..87554068 100644 --- a/libs/main/thread/pithread.h +++ b/libs/main/thread/pithread.h @@ -65,6 +65,7 @@ static __PIThreadCollection_Initializer__ __PIThreadCollection_initializer__; typedef std::function ThreadFunc; + class PIP_EXPORT PIThread: public PIObject { PIOBJECT_SUBCLASS(PIThread, PIObject) @@ -74,24 +75,28 @@ class PIP_EXPORT PIThread: public PIObject public: NO_COPY_CLASS(PIThread) - //! Contructs thread with custom data "data", external function "func" and main loop delay "loop_delay". + //! \~english Contructs thread with custom data "data", external function "func" and main loop delay "loop_delay" + //! \~russian Создает поток с данными "data", функцией "func" и задержкой цикла "loop_delay" PIThread(void * data, ThreadFunc func, bool startNow = false, int loop_delay = -1); - //! Contructs thread with external function "func" and main loop delay "loop_delay". + //! \~english Contructs thread with external function "func" and main loop delay "loop_delay" + //! \~russian Создает поток с функцией "func" и задержкой цикла "loop_delay" PIThread(std::function func, bool startNow = false, int loop_delay = -1); - //! Contructs thread with main loop delay "loop_delay". + //! \~english Contructs thread with main loop delay "loop_delay" + //! \~russian Создает поток с задержкой цикла "loop_delay" PIThread(bool startNow = false, int loop_delay = -1); virtual ~PIThread(); - //! Priority of thread + //! \~english Priority of thread + //! \~russian Приоритет потока enum Priority { - piLowerst /** Lowest */, - piLow /** Low */, - piNormal /** Normal, this is default priority of threads and timers */, - piHigh /** High */, - piHighest /** Highest */ + piLowerst /** \~english Lowest \~russian Низший */ , + piLow /** \~english Low \~russian Низкий */ , + piNormal /** \~english Normal, this is default priority of threads and timers \~russian Нормальный, это приоритет по умолчанию для потоков и таймеров */ , + piHigh /** \~english High \~russian Высокий */ , + piHighest /** \~english Highest \~russian Высший */ }; EVENT_HANDLER0(bool, start) {return start(-1);} @@ -106,27 +111,36 @@ public: EVENT_HANDLER1(void, stop, bool, wait); EVENT_HANDLER0(void, terminate); - //! \brief Set common data passed to external function + //! \~english Set common data passed to external function + //! \~russian Устанавливает данные, передаваемые в функцию потока void setData(void * d) {data_ = d;} - //! \brief Set external function that will be executed after every \a run() + //! \~english Set external function that will be executed after every \a run() + //! \~russian Устанавливает функцию потока, вызываемую после каждого \a run() void setSlot(ThreadFunc func) {ret_func = func;} - //! \brief Set external function that will be executed after every \a run() + //! \~english Set external function that will be executed after every \a run() + //! \~russian Устанавливает функцию потока, вызываемую после каждого \a run() void setSlot(std::function func) {ret_func = [func](void*){func();};} - //! \brief Set priority of thread + //! \~english Set thread priority + //! \~russian Устанавливает приоритет потока void setPriority(PIThread::Priority prior); - //! \brief Returns common data passed to external function + //! \~english Returns common data passed to external function + //! \~russian Возвращает данные, передаваемые в функцию потока void * data() const {return data_;} - //! \brief Return priority of thread + //! \~english Return thread priority + //! \~russian Возвращает приоритет потока PIThread::Priority priority() const {return priority_;} - //! \brief Return \c true if thread is running + //! \~english Return if thread is running + //! \~russian Возвращает исполняется ли поток bool isRunning() const {return running_;} + //! \~english Return if thread is stopping + //! \~russian Возвращает останавливается ли поток bool isStopping() const {return running_ && terminating;} EVENT_HANDLER0(bool, waitForStart) {return waitForStart(-1);} @@ -134,19 +148,19 @@ public: EVENT_HANDLER0(bool, waitForFinish) {return waitForFinish(-1);} EVENT_HANDLER1(bool, waitForFinish, int, timeout_msecs); - //! \brief Set necessity of lock every \a run with internal mutex + //! \~english Set necessity of lock every \a run() with internal mutex + //! \~russian Устанавливает необходимость блокировки внутреннего мьютекса каждый \a run() void needLockRun(bool need) {lockRun = need;} - //! \brief Lock internal mutex EVENT_HANDLER0(void, lock) const {thread_mutex.lock();} - - //! \brief Unlock internal mutex EVENT_HANDLER0(void, unlock) const {thread_mutex.unlock();} - //! \brief Returns internal mutex + //! \~english Returns internal mutex + //! \~russian Возвращает внутренний мьютекс PIMutex & mutex() const {return thread_mutex;} - //! \brief Returns thread ID + //! \~english Returns thread ID + //! \~russian Возвращает ID потока llong tid() const {return tid_;} void __thread_func__(); @@ -155,93 +169,129 @@ public: EVENT(started) EVENT(stopped) - //! \brief Start event handler with name \"handler\" of object \"object\" - //! in separate thread with name \"name\" + //! \~english + //! Start event handler with name "handler" of object "object" + //! in separate thread with name "name" //! and automatically delete it on function finish + //! + //! \~russian static void runOnce(PIObject * object, const char * handler, const PIString & name = PIString()); - //! \brief Start function \"func\" in separate thread with name \"name\" + //! \~english + //! Start function "func" in separate thread with name "name" //! and automatically delete it on function finish + //! + //! \~russian static void runOnce(std::function func, const PIString & name = PIString()); //! \handlers //! \{ - /** \fn bool start(int timer_delay = -1) - * \brief Start thread - * \details Start execution of \a run() in internal loop with - * "timer_delay" delay in milliseconds. If "timer_delay" <= 0 - * there is no delay in loop. Thread also exec external function - * set by \a setSlot() if it`s not null - * - * \return \c false if thread already started or can`t start thread */ + //! \fn bool start(int timer_delay = -1) + //! \~english Start thread + //! \~russian Запускает поток + //! \~\details + //! \~english + //! Start execution of \a run() in internal loop with + //! "timer_delay" delay in milliseconds. If "timer_delay" <= 0 + //! there is no delay in loop. Thread also exec external function + //! set by \a setSlot() if it`s not null + //! \return \c false if thread already started or can`t start thread + //! \~russian - /** \fn bool startOnce() - * \brief Start thread without internal loop - * \details Start execution of \a run() once. Thread also exec - * external function set by \a setSlot() if it`s not null - * - * \return \c false if thread already started or can`t start thread */ + //! \fn bool startOnce() + //! \~english Start thread without internal loop + //! \~russian Запускает поток без внутреннего цикла + //! \~\details + //! \~english + //! Start execution of \a run() once. Thread also exec + //! external function set by \a setSlot() if it`s not null + //! \return \c false if thread already started or can`t start thread + //! \~russian - /** \fn bool startOnce(ThreadFunc func) - * \brief Start thread without internal loop - * \details Overloaded function. Set external function "func" before start - * - * \return \c false if thread already started or can`t start thread */ + //! \fn bool startOnce(ThreadFunc func) + //! \~english Start thread without internal loop + //! \~russian Запускает поток без внутреннего цикла + //! \~\details + //! \~english + //! Overloaded function. Set external function "func" before start + //! \return \c false if thread already started or can`t start thread + //! \~russian - /** \fn void stop(bool wait = false) - * \brief Stop thread - * \details Stop execution of thread and wait for it finish - * if "wait" is \c true. This function can block for infinite - * time if "wait" is \c true and any of thread function is - * busy forever */ + //! \fn void stop(bool wait = false) + //! \~english Stop thread + //! \~russian Останавливает поток + //! \~\details + //! \~english + //! Stop execution of thread and wait for it finish + //! if "wait" is \c true. This function can block for infinite + //! time if "wait" is \c true and any of thread function is + //! busy forever + //! \~russian - /** \fn void terminate() - * \brief Strongly stop thread - * \details Stop execution of thread immediately */ + //! \fn void terminate() + //! \~english Strongly stop thread + //! \~russian Жестко останавливает поток + //! \~\details + //! \~english + //! Stop execution of thread immediately + //! \~russian - /** \fn bool waitForStart(int timeout_msecs = -1) - * \brief Wait for thread start - * \details This function block until thread start for "timeout_msecs" - * or forever if "timeout_msecs" < 0 - * - * \return \c false if timeout is exceeded */ + //! \fn bool waitForStart(int timeout_msecs = -1) + //! \~english Wait for thread start + //! \~russian Ожидает старта потока + //! \~\details + //! \~english + //! This function block until thread start for "timeout_msecs" + //! or forever if "timeout_msecs" < 0 + //! \return \c false if timeout is exceeded + //! \~russian - /** \fn bool waitForFinish(int timeout_msecs = -1) - * \brief Wait for thread finish - * \details This function block until thread finish for "timeout_msecs" - * or forever if "timeout_msecs" < 0 - * - * \return \c false if timeout is exceeded */ + //! \fn bool waitForFinish(int timeout_msecs = -1) + //! \~english Wait for thread finish + //! \~russian Ожидает завершения потока + //! \~\details + //! \~english + //! This function block until thread finish for "timeout_msecs" + //! or forever if "timeout_msecs" < 0 + //! \return \c false if timeout is exceeded + //! \~russian //! \fn void lock() - //! \brief Lock internal mutex - + //! \~english Lock internal mutex + //! \~russian Блокирует внутренний мьютекс + //! \fn void unlock() - //! \brief Unlock internal mutex - + //! \~english Unlock internal mutex + //! \~russian Разблокирует внутренний мьютекс + //! \} //! \events //! \{ //! \fn void started() - //! \brief Raise on thread start - + //! \~english Raise on thread start + //! \~russian Вызывается при старте потока + //! \fn void stopped() - //! \brief Raise on thread stop - + //! \~english Raise on thread stop + //! \~russian Вызывается при завершении потока + //! \} protected: static int priority2System(PIThread::Priority p); - //! Function executed once at the start of thread. + //! \~english Function executed once at the start of thread + //! \~russian Метод выполняется один раз при старте потока virtual void begin() {;} - //! Function executed at every "timer_delay" msecs until thread was stopped. + //! \~english Function executed at every "timer_delay" msecs until thread was stopped + //! \~russian Метод выполняется каждые "timer_delay" миллисекунд virtual void run() {;} - //! Function executed once at the end of thread. + //! \~english Function executed once at the end of thread + //! \~russian Метод выполняется один раз при остановке потока virtual void end() {;} std::atomic_bool terminating, running_, lockRun; diff --git a/libs/main/thread/pithreadnotifier.cpp b/libs/main/thread/pithreadnotifier.cpp index 6b9ac3b6..38f6e82b 100644 --- a/libs/main/thread/pithreadnotifier.cpp +++ b/libs/main/thread/pithreadnotifier.cpp @@ -19,10 +19,50 @@ #include "pithreadnotifier.h" +//! \addtogroup Thread +//! \{ +//! \class PIThreadNotifier pithreadnotifier.h +//! +//! \~\brief +//! \~english Class for simple notify and wait in different threads +//! \~russian Класс для простого уведомления и ожидания в различных потоках +//! +//! \~\details +//! \~english +//! +//! \~russian +//! +//! +//! \~english \section PIThreadNotifier_sec0 Synopsis +//! \~russian \section PIThreadNotifier_sec0 Краткий обзор +//! \~english +//! +//! \~russian +//! +//! +//! \~english \section PIThreadNotifier_sec1 Usage +//! \~russian \section PIThreadNotifier_sec1 Использование +//! \~english +//! +//! \~russian +//! +//! +//! \} + PIThreadNotifier::PIThreadNotifier() : cnt(0) {} +//! \~\details +//! \~english +//! If \a notifyOnce() has been called before, then returns immediately. +//! If \a notifyOnce() has been called "n" times, then returns immediately "n" times, +//! but only if wait in one thread. +//! If many threads waiting, then if \a notifyOnce() has been called "n" times, +//! all threads total returns "n" times in random sequence. +//! +//! \~russian +//! void PIThreadNotifier::wait() { m.lock(); while (cnt == 0) v.wait(m); @@ -31,6 +71,12 @@ void PIThreadNotifier::wait() { } +//! \~\details +//! \~english +//! If many threads waiting, then notify randomly one. +//! If call this "n" times, then notify any waiting threads totally "n" times. +//! +//! \~russian void PIThreadNotifier::notifyOnce() { m.lock(); cnt++; diff --git a/libs/main/thread/pithreadnotifier.h b/libs/main/thread/pithreadnotifier.h index b1cbd1f6..e2045a9d 100644 --- a/libs/main/thread/pithreadnotifier.h +++ b/libs/main/thread/pithreadnotifier.h @@ -33,17 +33,12 @@ class PIP_EXPORT PIThreadNotifier { public: PIThreadNotifier(); - //! Start waiting, return if other thread call \a notifyOnce(). - //! If \a notifyOnce() has been called before, then returns immediately. - //! If notifyOnce() has been called "n" times, then returns immediately "n" times, - //! but only if wait in one thread. - //! If many threads waiting, then If notifyOnce() has been called "n" times, - //! All threads total returns "n" times in random sequence. + //! \~english Start waiting, return if other thread call \a notifyOnce() + //! \~russian Начать ожидание, продолжает когда другой поток вызовет \a notifyOnce() void wait(); - //! Notify one waiting thread, wich waiting on \a wait() function. - //! If many threads waiting, then notify randomly one. - //! If call this "n" times, then notify any waiting threads totally "n" times. + //! \~english Notify one waiting thread, which waiting on \a wait() function + //! \~russian Уведомить один из ожидающих потоков, которые висят на методе \a wait() void notifyOnce(); private: diff --git a/libs/main/thread/pithreadpoolloop.cpp b/libs/main/thread/pithreadpoolloop.cpp index 050acd15..8611699e 100644 --- a/libs/main/thread/pithreadpoolloop.cpp +++ b/libs/main/thread/pithreadpoolloop.cpp @@ -22,51 +22,82 @@ #include "pithread.h" -/*! \class PIThreadPoolLoop - * \brief Thread class - * \details This class allow you parallelize loop. - * - * \section PIThreadPoolLoop_sec0 Usage - * This class designed to replace "for(;;)" statement in very simple way. - * In constructor several threads created, then by "setFunction()" method - * you should pass body of your loop, and then call "start()" or "exec()". - * Every thread take loop counter and execute your function until all - * counter range is passed. - * - * Example: -\code{.cpp} -PIVector data(10, [](int i)->int{return i;}); - -piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - -PIThreadPoolLoop pool; -pool.exec(0, data.size(), [&](int i){ // parallel analogue "for (int i = 0; i < data.size(); i++)" - data[i] = data[i] + 10; -}); - -piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} -\endcode - * - * Equivalent to: -\code{.cpp} -PIVector data(10, [](int i)->int{return i;}); - -piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - -pool.setFunction([&](int i){ - data[i] = data[i] + 10; -}); -pool.exec(0, data.size()); - -piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} -\endcode - * - * \section PIThreadPoolLoop_sec1 Important - * Due to multithreading it`s very important to protect output data of loop body, use mutex. - * Also remember that execution order is undefined and you shouldn`t use global variables in - * your function. Use local variables and lambda capture. - * - */ +//! \addtogroup Thread +//! \{ +//! \class PIThreadPoolLoop pithreadpoolloop.h +//! \~english Thread pool loop +//! \~russian Пул потоков +//! +//! \~\details +//! \~english +//! This class allow you parallelize loop. +//! +//! \~russian +//! Этот класс позволяет распараллелить цикл +//! +//! +//! \~english \section PIThreadPoolLoop_sec0 Usage +//! \~russian \section PIThreadPoolLoop_sec0 Использование +//! \~english +//! This class designed to parallel "for(;;)" statement in very simple way. +//! In constructor several threads created, then by \a setFunction() method +//! you should pass body of your loop, and then call \a start() or \a exec(). +//! Every thread take loop counter and execute your function until all +//! counter range is passed. +//! +//! Example: +//! \~russian +//! Этот класс предназначен для распараллеливания цикла "for(;;)" максимально простым способом. +//! В конструкторе создается несколько потоков, затем методом \a setFunction() +//! устанавливается функция, представляющая собой тело цикла. Затем вызовом +//! \a start() или \a exec() цикл исполняется параллельно. Каждый поток получает +//! значение переменной цикла и вызывает функцию-тело до тех пор, пока +//! весь диапазон не будет исполнен. +//! +//! Пример: +//! +//! \~\code{.cpp} +//! PIVector data(10, [](int i)->int{return i;}); +//! +//! piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} +//! +//! PIThreadPoolLoop pool; +//! pool.exec(0, data.size(), [&](int i){ // parallel analogue "for (int i = 0; i < data.size(); i++)" +//! data[i] = data[i] + 10; +//! }); +//! +//! piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} +//! \endcode +//! +//! \~english Equivalent to: +//! \~russian Эквивалентно: +//! \~\code{.cpp} +//! PIVector data(10, [](int i)->int{return i;}); +//! +//! piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} +//! +//! pool.setFunction([&](int i){ +//! data[i] = data[i] + 10; +//! }); +//! pool.exec(0, data.size()); +//! +//! piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} +//! \endcode +//! +//! +//! \~english \section PIThreadPoolLoop_sec1 Important +//! \~russian \section PIThreadPoolLoop_sec1 Важно +//! \~english +//! Due to multithreading it`s very important to protect output data of loop body, use mutex. +//! Also remember that execution order is undefined and you shouldn`t use global variables in +//! your function. Use local variables and lambda capture. +//! +//! \~russian +//! В силу многопоточности очень важно защитить выходные данные тела цикла с помощью блокировок (мьютекса). +//! Также стоит помнить, что последовательность выполнения неопределена, и не стоит использовать глобальных +//! переменных в теле цикла. Используйте локальные переменные и захват в лямбде. +//! +//! \} PIThreadPoolLoop::PIThreadPoolLoop(int thread_cnt) { diff --git a/libs/main/thread/pithreadpoolloop.h b/libs/main/thread/pithreadpoolloop.h index e523a064..8231fd47 100644 --- a/libs/main/thread/pithreadpoolloop.h +++ b/libs/main/thread/pithreadpoolloop.h @@ -33,30 +33,50 @@ class PIThread; class PIP_EXPORT PIThreadPoolLoop { public: + //! \~english //! Contructs thread pool with threads count "thread_cnt". //! If "thread_cnt" = -1 then system processors count used + //! \~russian + //! Создает пул из "thread_cnt" потоков. Если "thread_cnt" = -1 + //! то используется количество процессоров системы PIThreadPoolLoop(int thread_cnt = -1); virtual ~PIThreadPoolLoop(); - //! Set threads function to "f" with format [](int){...} + //! \~english Set threads function to "f" with format [ ](int){ ... } + //! \~russian Устанавливает функцию потоков на "f" в формате [ ](int){ ... } void setFunction(std::function f); - //! Wait for all threads stop + //! \~english Wait for all threads stop + //! \~russian Ожидает завершения всех потоков void wait(); + //! \~english //! Start functions execution with integer argument range //! from "index_start" to "index_start + index_count - 1" + //! \~russian + //! Начинает исполнение потоков с аргументами по диапазону + //! от "index_start" до "index_start + index_count - 1" void start(int index_start, int index_count); + //! \~english //! Start functions execution with integer argument range //! from "index_start" to "index_start + index_count - 1" //! and wait for finish + //! \~russian + //! Начинает исполнение потоков с аргументами по диапазону + //! от "index_start" до "index_start + index_count - 1" + //! и ожидает завершения void exec(int index_start, int index_count); + //! \~english //! Start functions "f" execution with integer argument range //! from "index_start" to "index_start + index_count - 1" //! and wait for finish + //! \~russian + //! Начинает исполнение потоками функции "f" с аргументами по диапазону + //! от "index_start" до "index_start + index_count - 1" + //! и ожидает завершения void exec(int index_start, int index_count, std::function f); private: diff --git a/libs/main/thread/pitimer.cpp b/libs/main/thread/pitimer.cpp index b2a1edac..1a5c1d2f 100644 --- a/libs/main/thread/pitimer.cpp +++ b/libs/main/thread/pitimer.cpp @@ -24,31 +24,94 @@ #endif -/*! \class PITimer - * \brief Timer - * - * \section PITimer_sec0 Synopsis - * This class implements timer function. PIP timers supports 3 way to tick notify, - * frequency delimiters and time measurements. - * \section PITimer_sec1 Notify variants - * Notify variants: - * * "slot" - static function with format void func(void * data, int delimiter); - * * event - \a tickEvent(); - * * virtual function - \a tick(). - * - * All these variants are equivalent, use most applicable. - * \section PITimer_sec2 Frequency delimiters - * Frequency delimiter is an integer number and "slot" function. If "slot" function is null - * timer main "slot" will be used. Each delimiter numbers tick timer will be execute - * delimiters or timer main "slot" function with \b delimiter value = delimiter number. - * Example: \snippet pitimer.cpp delimiter - * \section PITimer_sec3 Time measurements - * PITimer can be used as time measurer. Function \a reset() set time mark to current - * system time, then functions double elapsed_*() returns time elapsed from this mark. - * These functions can returns nano-, micro-, milli- and seconds with suffixes "n", "u", "m" - * and "s" - * Example: \snippet pitimer.cpp elapsed -*/ +//! \addtogroup Thread +//! \{ +//! \class PITimer pitimer.h +//! +//! \~\brief +//! \~english Timer +//! \~russian Таймер +//! +//! \~\details +//! +//! \~english \section PITimer_sec0 Synopsis +//! \~russian \section PITimer_sec0 Краткий обзор +//! \~english +//! This class implements timer function. PIP timers supports 3 way to tick notify, +//! deferred start and frequency delimiters. +//! +//! \~russian +//! Этот класс реализует таймер. Таймер PIP поддерживает 3 варианта уведомления, +//! отложенный старт и делители частоты. +//! +//! +//! \~english \section PITimer_sec1 Notify variants +//! \~russian \section PITimer_sec1 Варианты уведомления +//! \~english +//! Notify variants: +//! * "slot" - static function with format void func(void * data, int delimiter) or lambda-function; +//! * event - \a tickEvent(); +//! * virtual function - \a tick(). +//! +//! Lambda should be [ ]( ){ } or [ ](void*){ } format. +//! +//! All these variants are equivalent, use most applicable. +//! \~russian +//! Варианты уведомления: +//! * "slot" - статический метод в формате void func(void * data, int delimiter) или лямбда-функция; +//! * event - \a tickEvent(); +//! * виртуальный метод - \a tick(). +//! +//! Лямбда-функция должна быть в формате [ ]( ){ } или [ ](void*){ }. +//! +//! Все варианты аналогичны друг другу, используйте самый удобный. +//! +//! +//! \~english \section PITimer_sec2 Frequency delimiters +//! \~russian \section PITimer_sec2 Делители частоты +//! \~english +//! Frequency delimiter is an integer number and "slot" function. If "slot" function is null +//! timer main "slot" will be used. Each delimiter numbers tick timer will be execute +//! delimiters or timer main "slot" function with \b delimiter value = delimiter number. +//! +//! Example: +//! +//! \~russian +//! Делитель частоты это целое число и необязательный метод "slot". Если метод не указан, +//! то будет использован основной "slot". Каждые \b delimiter тиков будет дополнительно вызван +//! "slot" делителя либо основной "slot" с аргументом \b delimiter равным значению делителя. +//! +//! Пример: +//! +//! \~\code +//! void tfunc(void * , int delim) { +//! piCout << "tick with delimiter" << delim; +//! }; +//! void tfunc4(void * , int delim) { +//! piCout << "tick4 with delimiter" << delim; +//! }; +//! int main() { +//! PITimer timer(tfunc); +//! timer.addDelimiter(2); +//! timer.addDelimiter(4, tfunc4); +//! timer.start(50); +//! piMSleep(200); +//! timer.stop(); +//! timer.waitForFinish(); +//! return 0; +//! }; +//! /* Result: +//! tick with delimiter 1 +//! tick with delimiter 1 +//! tick with delimiter 2 +//! tick with delimiter 1 +//! tick with delimiter 1 +//! tick with delimiter 2 +//! tick4 with delimiter 4 +//! */ +//! \endcode +//! +//! \} _PITimerBase::_PITimerBase() { @@ -603,23 +666,43 @@ bool PITimer::start(int interval_ms_i) { } +//! \~\details +//! \~english +//! Timer wait "delay_msecs" milliseconds and then normally starts with \a interval() loop delay +//! \~russian +//! Таймер ожидает "delay_msecs" миллисекунд, а затем стартует с интервалом \a interval() void PITimer::startDeferred(double delay_ms) { init(); imp->startDeferred(delay_ms); } +//! \~\details +//! \~english +//! Timer wait "delay_msecs" milliseconds and then normally starts with "interval_msecs" loop delay +//! \~russian +//! Таймер ожидает "delay_msecs" миллисекунд, а затем стартует с интервалом "interval_msecs" void PITimer::startDeferred(double interval_ms, double delay_ms) { init(); imp->startDeferred(interval_ms, delay_ms); } +//! \~\details +//! \~english +//! Timer wait until "start_datetime" and then normally starts with \a interval() loop delay +//! \~russian +//! Таймер ожидает наступления "start_datetime", а затем стартует с интервалом \a interval() void PITimer::startDeferred(PIDateTime start_datetime) { startDeferred(imp->interval_, start_datetime); } +//! \~\details +//! \~english +//! Timer wait until "start_datetime" and then normally starts with "interval_msecs" loop delay +//! \~russian +//! Таймер ожидает наступления "start_datetime", а затем стартует с интервалом "interval_msecs" void PITimer::startDeferred(double interval_ms, PIDateTime start_datetime) { init(); imp->startDeferred(interval_ms, start_datetime); diff --git a/libs/main/thread/pitimer.h b/libs/main/thread/pitimer.h index ee894182..07983d2f 100644 --- a/libs/main/thread/pitimer.h +++ b/libs/main/thread/pitimer.h @@ -42,7 +42,6 @@ public: double interval() const {return interval_;} void setInterval(double i); - //! \brief Return \c true if thread is running bool isRunning() const {return running_;} bool isStopped() const {return !running_;} @@ -79,45 +78,64 @@ class PIP_EXPORT PITimer: public PIObject { public: NO_COPY_CLASS(PITimer) - //! \brief Constructs timer with PITimer::Thread implementation + //! \~english Constructs timer with PITimer::Thread implementation + //! \~russian Создает таймер с реализацией PITimer::Thread explicit PITimer(); - //! \brief Timer implementations + //! \~english Timer implementations + //! \~russian Реализация таймера enum TimerImplementation { - Thread /*! Timer works in his own thread. Intervals are measured by the system time */ = 0x01, - ThreadRT /*! Using POSIX timer with SIGEV_THREAD notification. \attention Doesn`t support on Windows and Mac OS! */ = 0x02, - Pool /*! Using single TimerPool for all timers with this implementation. TimerPool works as Thread implementation and - * sequentially executes all timers. \attention Use this implementation with care! */ = 0x04 + Thread /*! + \~english Timer works in his own thread. Intervals are measured by the system time + \~russian Таймер работает в собственном потоке. Интервалы измеряются с помощью системного времени + */ = 0x01, + ThreadRT /*! + \~english Using POSIX timer with SIGEV_THREAD notification. \attention Doesn`t support on Windows and Mac OS! + \~russian Использовать таймер POSIX с SIGEV_THREAD уведомлением. \attention Не поддерживается на Windows и Mac OS! + */ = 0x02, + Pool /*! + \~english Using single TimerPool for all timers with this implementation. TimerPool works as Thread implementation and + sequentially executes all timers. \attention Use this implementation with care! + \~russian Использовать единый TimerPool для всех таймеров с этой реализацией. TimerPool реализован через Thread и + последовательно исполняет все таймеры. \attention Осторожнее с этой реализацией! + */ = 0x04 }; - //! \brief Constructs timer with "ti" implementation + //! \~english Constructs timer with "ti" implementation + //! \~russian Создает таймер с реализацией "ti" explicit PITimer(TimerImplementation ti); - //! \brief Constructs timer with "slot" slot void(void *,int), "data" data and "ti" implementation + //! \~english Constructs timer with "slot" slot void(void *,int), "data" data and "ti" implementation + //! \~russian Создает таймер со слотом "slot", данными "data" и реализацией "ti" explicit PITimer(TimerEvent slot, void * data = 0, TimerImplementation ti = Thread); - //! \brief Constructs timer with "slot" slot void(), and "ti" implementation + //! \~english Constructs timer with "slot" slot void(), and "ti" implementation + //! \~russian Создает таймер со слотом "slot" и реализацией "ti" explicit PITimer(std::function slot, TimerImplementation ti = Thread); - //! \brief Constructs timer with "slot" slot void(void *), "data" data and "ti" implementation + //! \~english Constructs timer with "slot" slot void(void *), "data" data and "ti" implementation + //! \~russian Создает таймер со слотом "slot", данными "data" и реализацией "ti" explicit PITimer(std::function slot, void * data, TimerImplementation ti = Thread); virtual ~PITimer(); - //! \brief Returns timer implementation + //! \~english Returns timer implementation + //! \~russian Возвращает реализацию таймера PITimer::TimerImplementation implementation() const {return imp_mode;} - //! \brief Returns timer loop delay in milliseconds + //! \~english Returns timer loop delay in milliseconds + //! \~russian Возвращает задержку цикла таймера в миллисекундах double interval() const; - //! \brief Set timer loop delay in milliseconds EVENT_HANDLER1(void, setInterval, double, ms); - //! \brief Returns if timer is started + //! \~english Returns if timer is started + //! \~russian Возвращает работает ли таймер bool isRunning() const; - //! \brief Returns if timer is not started + //! \~english Returns if timer is not started + //! \~russian Возвращает остановлен ли таймер bool isStopped() const; EVENT_HANDLER0(bool, start); @@ -126,24 +144,20 @@ public: EVENT_HANDLER0(bool, restart); - /** \brief Start timer with \b interval() loop delay after \b delay_msecs delay. - * \details Timer wait \b delay_msecs milliseconds and then normally starts with - * \b interval() loop delay. */ + //! \~english Start timer with \a interval() loop delay after "delay_msecs" delay + //! \~russian Запускает таймер с интервалом \a interval() после ожидания "delay_msecs" void startDeferred(double delay_ms); - /** \brief Start timer with \b interval_msecs loop delay after \b delay_msecs delay. - * \details Timer wait \b delay_msecs milliseconds and then normally starts with - * \b interval_msecs loop delay. */ + //! \~english Start timer with "interval_msecs" loop delay after "delay_msecs" delay + //! \~russian Запускает таймер с интервалом "interval_msecs" после ожидания "delay_msecs" void startDeferred(double interval_ms, double delay_ms); - /** \brief Start timer with \b interval() loop delay after \b start_datetime date and time. - * \details Timer wait until \b start_datetime and then normally starts with - * \b interval() loop delay. */ + //! \~english Start timer with \a interval() loop delay after "start_datetime" date and time + //! \~russian Запускает таймер с интервалом \a interval() после наступления "start_datetime" void startDeferred(PIDateTime start_datetime); - /** \brief Start timer with \b interval_msecs loop delay after \b start_datetime date and time. - * \details Timer wait until \b start_datetime and then normally starts with - * \b interval_msecs loop delay. */ + //! \~english Start timer with "interval_msecs" loop delay after "start_datetime" date and time + //! \~russian Запускает таймер с интервалом "interval_msecs" после наступления "start_datetime" void startDeferred(double interval_ms, PIDateTime start_datetime); EVENT_HANDLER0(bool, stop); @@ -151,43 +165,52 @@ public: bool waitForFinish() {return waitForFinish(-1);} bool waitForFinish(int timeout_msecs); - //! \brief Set custom data + //! \~english Set custom data + //! \~russian Установить данные, передаваемые в метод таймера void setData(void * data_) {data_t = data_;} - - //! \brief Set timer tick function + + //! \~english Returns common data passed to tick functions + //! \~russian Возвращает данные, передаваемые в метод таймера + void * data() const {return data_t;} + + //! \~english Set timer tick function + //! \~russian Установить вызываемый метод void setSlot(TimerEvent slot) {ret_func = slot;} - //! \brief Set timer tick function + //! \~english Set timer tick function + //! \~russian Установить вызываемый метод void setSlot(std::function slot) {ret_func = [slot](void *, int){slot();};} - //! \brief Set timer tick function + //! \~english Set timer tick function + //! \~russian Установить вызываемый метод void setSlot(std::function slot) {ret_func = [slot](void *d, int){slot(d);};} - //! \brief Returns common data passed to tick functions - void * data() const {return data_t;} - void needLockRun(bool need) {lockRun = need;} EVENT_HANDLER0(void, lock) {mutex_.lock();} EVENT_HANDLER0(void, unlock) {mutex_.unlock();} - //! \brief Returns if timer should exec \a maybeCallQueuedEvents() at every tick. - //! By default \b true + //! \~english Returns if timer should exec \a maybeCallQueuedEvents() at every tick. By default \b true + //! \~russian Возвращает должен ли таймер вызывать \a maybeCallQueuedEvents() каждый тик. По умолчанию \b true bool isCallQueuedEvents() const {return callEvents;} - //! \brief If set timer exec \a maybeCallQueuedEvents() at every tick. - //! By default \b true + //! \~english Set timer exec \a maybeCallQueuedEvents() at every tick + //! \~russian Установает должен ли таймер вызывать \a maybeCallQueuedEvents() каждый тик void setCallQueuedEvents(bool yes) {callEvents = yes;} - //! \brief Add frequency delimiter \b delim with optional delimiter slot \b slot. + //! \~english Add frequency delimiter "delim" with optional delimiter slot "slot" + //! \~russian Добавляет делитель частоты "delim" с необязательным методом "slot" void addDelimiter(int delim, TimerEvent slot = 0) {delims << Delimiter(slot, delim);} - //! \brief Add frequency delimiter \b delim with optional delimiter slot \b slot. + //! \~english Add frequency delimiter "delim" with optional delimiter slot "slot" + //! \~russian Добавляет делитель частоты "delim" с необязательным методом "slot" void addDelimiter(int delim, std::function slot) {delims << Delimiter([slot](void *, int){slot();}, delim);} - //! \brief Add frequency delimiter \b delim with optional delimiter slot \b slot. + //! \~english Add frequency delimiter "delim" with optional delimiter slot "slot" + //! \~russian Добавляет делитель частоты "delim" с необязательным методом "slot" void addDelimiter(int delim, std::function slot) {delims << Delimiter([slot](void *d, int){slot(d);}, delim);} - //! \brief Remove all frequency delimiters \b delim. + //! \~english Remove all frequency delimiters "delim" + //! \~russian Удаляет все делители частоты "delim" void removeDelimiter(int delim) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].delim == delim) {delims.remove(i); i--;}} EVENT_HANDLER0(void, clearDelimiters) {delims.clear();} @@ -197,33 +220,58 @@ public: //! \handlers //! \{ - /** \fn bool start() - * \brief Start timer with \a interval() loop delay - * \details Start execution of timer functions with frequency = 1 / msecs Hz. */ - - /** \fn bool start(double msecs) - * \brief Start timer with \b msecs loop delay - * \details Start execution of timer functions with frequency = 1. / msecs Hz. - * Instead of \a start(int msecs) function this variant allow start timer - * with frequencies more than 1 kHz */ - + //! \fn void setInterval(double ms) + //! \~english Set timer loop delay in milliseconds + //! \~russian Установить интервал таймера "ms" миллисекунд + + //! \fn bool start() + //! \~english Start timer with \a interval() loop delay + //! \~russian Запустить таймер с интервалом \a interval() + //! \~\details + //! \~english + //! Start execution of timer functions with frequency = 1 / msecs Hz + //! \~russian + //! Запускает таймер с частотой = 1 / msecs Гц + + //! \fn bool start(double msecs) + //! \~english Start timer with "msecs" loop delay + //! \~russian Запустить таймер с интервалом "msecs" + //! \~\details + //! \~english + //! Start execution of timer functions with frequency = 1. / msecs Hz. + //! Instead of \a start(int msecs) this function allow start timer + //! with frequencies more than 1 kHz + //! \~russian + //! Запускает таймер с частотой = 1 / msecs Гц. В отличии от + //! \a start(int msecs) этот метод позволяет запустить таймер с частотой + //! более 1 кГц + //! \fn bool restart() - //! \brief Stop and start timer with \a interval() loop delay + //! \~english Stop and start timer with \a interval() loop delay + //! \~russian Остановить и запустить таймер с интервалом \a interval() //! \fn bool stop(bool wait = true) - //! \brief Stop timer and wait for it finish if "wait" + //! \~english Stop timer and wait for it finish if "wait" + //! \~russian Остановить таймер и если "wait" то дождаться остановки //! \fn void clearDelimiters() - //! \brief Remove all frequency delimiters + //! \~english Remove all frequency delimiters + //! \~russian Удаляет все делители частоты //! \} //! \events //! \{ - /** \fn void tickEvent(void * data, int delimiter) - * \brief Raise on timer tick - * \details \b Data can be set with function \a setData(void * data) or from constructor. - * \b Delimiter is frequency delimiter, 1 for main loop. */ + //! \fn void tickEvent(void * data, int delimiter) + //! \~english Raise on timer tick + //! \~russian Вызывается каждый тик таймера + //! \~\details + //! \~english + //! "data" can be set with function \a setData() or from constructor. + //! "delimiter" is frequency delimiter, 1 for main loop. + //! \~russian + //! "data" устанавливается методом \a setData() или в конструкторе. + //! "delimiter" - делитель частоты, 1 для основного цикла //! \} @@ -243,8 +291,7 @@ protected: static void tickImpS(PITimer * t) {t->tickImp();} void tickImp(); - //! Virtual timer execution function, similar to "slot" or event \a void timeout(void * data, int delimiter). - //! By default is empty. + //! Timer execution function, similar to "slot" or event \a timeout(). By default does nothing virtual void tick(void * data_, int delimiter) {} void * data_t;