doc ru, printf() before assert in containers

This commit is contained in:
2022-04-12 23:17:05 +03:00
parent 486fdf3dcd
commit 00830958df
18 changed files with 802 additions and 350 deletions

View File

@@ -2,8 +2,5 @@
void _() { void _() {
//! [main] //! [main]
mutex.lock();
// ... your code here
mutex.unlock();
//! [main] //! [main]
} }

View File

@@ -440,6 +440,11 @@ public:
} }
inline PIDeque<T> & insert(size_t index, const PIDeque<T> & other) { inline PIDeque<T> & insert(size_t index, const PIDeque<T> & other) {
if (other.isEmpty()) return *this; 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); assert(&other != this);
bool dir = pid_rsize <= 2 ? true : (index >= pid_rsize / 2 ? true : false); bool dir = pid_rsize <= 2 ? true : (index >= pid_rsize / 2 ? true : false);
if (dir) { if (dir) {
@@ -544,6 +549,11 @@ public:
inline PIDeque<T> & append(const T & e) {return push_back(e);} inline PIDeque<T> & append(const T & e) {return push_back(e);}
inline PIDeque<T> & append(T && e) {return push_back(std::move(e));} inline PIDeque<T> & append(T && e) {return push_back(std::move(e));}
inline PIDeque<T> & append(const PIDeque<T> & v) { inline PIDeque<T> & append(const PIDeque<T> & v) {
#ifdef USE_DEBUG
if (&v == this) {
printf("error with PIDeque<%s>::append\n", __PIP_TYPENAME__(T));
}
#endif
assert(&v != this); assert(&v != this);
size_t ps = pid_size; size_t ps = pid_size;
alloc(pid_size + v.pid_size, true); alloc(pid_size + v.pid_size, true);
@@ -615,6 +625,11 @@ public:
inline PIDeque<PIDeque<T>> reshape(size_t rows, size_t cols, int order = byRow) const { inline PIDeque<PIDeque<T>> reshape(size_t rows, size_t cols, int order = byRow) const {
PIDeque<PIDeque<T>> ret; PIDeque<PIDeque<T>> ret;
if (isEmpty()) return 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); assert(rows*cols == pid_size);
ret.resize(rows); ret.resize(rows);
if (order == byRow) { if (order == byRow) {
@@ -762,6 +777,11 @@ private:
if (as != pid_rsize) { if (as != pid_rsize) {
PIINTROSPECTION_CONTAINER_ALLOC(T, (as-pid_rsize)) PIINTROSPECTION_CONTAINER_ALLOC(T, (as-pid_rsize))
T * p_d = (T*)(realloc((void*)(pid_data), as*sizeof(T))); 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); assert(p_d);
pid_data = p_d; pid_data = p_d;
pid_rsize = as; pid_rsize = as;

View File

@@ -208,6 +208,11 @@ public:
const T at(const Key & key) const {return (*this)[key];} const T at(const Key & key) const {return (*this)[key];}
PIMap<Key, T> & operator <<(const PIMap<Key, T> & other) { PIMap<Key, T> & operator <<(const PIMap<Key, T> & other) {
#ifdef USE_DEBUG
if (&other == this) {
printf("error with PIMap<%s, %s>::<<\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T));
}
#endif
assert(&other != this); assert(&other != this);
if (other.isEmpty()) return *this; if (other.isEmpty()) return *this;
if (other.size() == 1) {insert(other.pim_index[0].key, other.pim_content[0]); return *this;} if (other.size() == 1) {insert(other.pim_index[0].key, other.pim_content[0]); return *this;}

View File

@@ -1199,6 +1199,11 @@ public:
//! \~\sa \a append(), \a prepend(), \a remove() //! \~\sa \a append(), \a prepend(), \a remove()
inline PIVector<T> & insert(size_t index, const PIVector<T> & v) { inline PIVector<T> & insert(size_t index, const PIVector<T> & v) {
if (v.isEmpty()) return *this; 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); assert(&v != this);
ssize_t os = piv_size - index; ssize_t os = piv_size - index;
alloc(piv_size + v.piv_size); alloc(piv_size + v.piv_size);
@@ -1385,6 +1390,11 @@ public:
} }
inline PIVector<T> & push_back(const PIVector<T> & v) { inline PIVector<T> & push_back(const PIVector<T> & v) {
#ifdef USE_DEBUG
if (&v == this) {
printf("error with PIVector<%s>::push_back\n", __PIP_TYPENAME__(T));
}
#endif
assert(&v != this); assert(&v != this);
size_t ps = piv_size; size_t ps = piv_size;
alloc(piv_size + v.piv_size); alloc(piv_size + v.piv_size);
@@ -1485,6 +1495,11 @@ public:
inline PIVector<PIVector<T>> reshape(size_t rows, size_t cols, ReshapeOrder order = byRow) const { inline PIVector<PIVector<T>> reshape(size_t rows, size_t cols, ReshapeOrder order = byRow) const {
PIVector<PIVector<T>> ret; PIVector<PIVector<T>> ret;
if (isEmpty()) return 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); assert(rows*cols == piv_size);
ret.resize(rows); ret.resize(rows);
if (order == byRow) { if (order == byRow) {
@@ -1609,6 +1624,11 @@ private:
if (as == piv_rsize) return; if (as == piv_rsize) return;
PIINTROSPECTION_CONTAINER_ALLOC(T, (as-piv_rsize)) PIINTROSPECTION_CONTAINER_ALLOC(T, (as-piv_rsize))
T * p_d = (T*)(realloc((void*)(piv_data), as*sizeof(T))); 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); assert(p_d);
piv_data = p_d; piv_data = p_d;
piv_rsize = as; piv_rsize = as;

View File

@@ -248,6 +248,8 @@
#ifdef NDEBUG #ifdef NDEBUG
# undef NDEBUG # undef NDEBUG
#else
# define USE_DEBUG
#endif #endif
#ifndef assert #ifndef assert
# define assert(x) # define assert(x)
@@ -256,6 +258,12 @@
# define assertm(exp, msg) assert(((void)msg, exp)) # define assertm(exp, msg) assert(((void)msg, exp))
#endif #endif
#ifdef MICRO_PIP
# define __PIP_TYPENAME__(T) "?"
#else
# define __PIP_TYPENAME__(T) typeid(T).name()
#endif
#ifdef CC_GCC #ifdef CC_GCC
# undef DEPRECATED # undef DEPRECATED
# define DEPRECATED __attribute__((deprecated)) # define DEPRECATED __attribute__((deprecated))

View File

@@ -32,12 +32,6 @@
#include "pivector2d.h" #include "pivector2d.h"
#include <stdio.h> #include <stdio.h>
#ifdef MICRO_PIP
# define _TYPENAME_(T) "?"
#else
# define _TYPENAME_(T) typeid(T).name()
#endif
class PIString; class PIString;
class PIByteArray; class PIByteArray;
@@ -394,7 +388,7 @@ inline PIByteArray & operator >>(PIByteArray & s, uchar & v) {assert(s.size() >=
template<typename T, typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0> template<typename T, typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0>
inline PIByteArray::StreamRef operator >>(PIByteArray & s, T & v) { inline PIByteArray::StreamRef operator >>(PIByteArray & s, T & v) {
if (s.size() < sizeof(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)); assert(s.size() >= sizeof(v));
} }
memcpy((void*)(&v), s.data(), sizeof(v)); memcpy((void*)(&v), s.data(), sizeof(v));
@@ -430,7 +424,7 @@ template<typename T,
typename std::enable_if< std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0> typename std::enable_if< std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) {
if (s.size_s() < 4) { 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); assert(s.size_s() >= 4);
} }
int sz; s >> sz; int sz; s >> sz;
@@ -446,7 +440,7 @@ template<typename T,
typename std::enable_if<!std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0> typename std::enable_if<!std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) {
if (s.size_s() < 4) { 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); assert(s.size_s() >= 4);
} }
int sz; s >> sz; int sz; s >> sz;
@@ -463,7 +457,7 @@ template<typename T,
typename std::enable_if< std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0> typename std::enable_if< std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) {
if (s.size_s() < 4) { 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); assert(s.size_s() >= 4);
} }
int sz; s >> sz; int sz; s >> sz;
@@ -479,7 +473,7 @@ template<typename T,
typename std::enable_if<!std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0> typename std::enable_if<!std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) {
if (s.size_s() < 4) { 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); assert(s.size_s() >= 4);
} }
int sz; s >> sz; int sz; s >> sz;
@@ -496,7 +490,7 @@ template<typename T,
typename std::enable_if< std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0> typename std::enable_if< std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIVector2D<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIVector2D<T> & v) {
if (s.size_s() < 8) { 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); assert(s.size_s() >= 8);
} }
int r, c; s >> r >> c; int r, c; s >> r >> c;
@@ -513,7 +507,7 @@ template<typename T,
typename std::enable_if<!std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0> typename std::enable_if<!std::is_same<decltype(std::declval<PIByteArray&>() << std::declval<const T &>()), PIByteArray::StreamRef>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIVector2D<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIVector2D<T> & v) {
if (s.size_s() < 8) { 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); assert(s.size_s() >= 8);
} }
int r,c; int r,c;
@@ -581,7 +575,7 @@ inline PIByteArray & operator <<(PIByteArray & s, const PIVector2D<T> & v) {
template<typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0> template<typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) {
if (s.size_s() < 4) { 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); assert(s.size_s() >= 4);
} }
int sz; s >> sz; int sz; s >> sz;
@@ -596,7 +590,7 @@ inline PIByteArray & operator >>(PIByteArray & s, PIVector<T> & v) {
template<typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0> template<typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) {
if (s.size_s() < 4) { 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); assert(s.size_s() >= 4);
} }
int sz; s >> sz; int sz; s >> sz;
@@ -611,7 +605,7 @@ inline PIByteArray & operator >>(PIByteArray & s, PIDeque<T> & v) {
template<typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0> template<typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
inline PIByteArray & operator >>(PIByteArray & s, PIVector2D<T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIVector2D<T> & v) {
if (s.size_s() < 8) { 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); assert(s.size_s() >= 8);
} }
int r,c; int r,c;
@@ -646,7 +640,7 @@ inline PIByteArray & operator <<(PIByteArray & s, const PIMap<Key, T> & v) {
template <typename Key, typename T> template <typename Key, typename T>
inline PIByteArray & operator >>(PIByteArray & s, PIMap<Key, T> & v) { inline PIByteArray & operator >>(PIByteArray & s, PIMap<Key, T> & v) {
if (s.size_s() < 4) { 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); assert(s.size_s() >= 4);
} }
int sz; s >> sz; v.pim_index.resize(sz); int sz; s >> sz; v.pim_index.resize(sz);
@@ -711,7 +705,4 @@ template <typename T> T piDeserialize(const PIByteArray & data) {
} }
#undef _TYPENAME_
#endif // PIBYTEARRAY_H #endif // PIBYTEARRAY_H

View File

@@ -17,22 +17,67 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** \class PIMutex //! \addtogroup Thread
* \brief Mutex //! \{
* \details //! \class PIMutex pimutex.h
* \section PIMutex_sec0 Synopsis //!
* %PIMutex provides synchronization blocks between several threads. //! \~\brief
* Using mutex guarantees execution of some code only one of threads. //! \~english Simple mutex
* Mutex contains logic state and functions to change it: \a lock(), //! \~russian Простой мьютекс
* \a unlock() and \a tryLock(). //!
* //!
* \section PIMutex_sec1 Usage //! \~\details
* Block of code that should to be executed only one thread simultaniously //! \~english \section PIMutex_sec0 Synopsis
* should to be started with \a lock() and ended with \a unlock(). //! \~russian \section PIMutex_sec0 Краткий обзор
* \snippet pimutex.cpp main //!
* "mutex" in this example is one for all threads. //! \~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 "pimutex.h"
#include "piincludes_p.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() { void PIMutex::lock() {
#if defined(WINDOWS) #if defined(WINDOWS)
EnterCriticalSection(&(PRIVATE->mutex)); EnterCriticalSection(&(PRIVATE->mutex));
@@ -79,6 +129,10 @@ void PIMutex::lock() {
} }
//! \~\details
//! \~english
//! In any case this function returns immediate
//! \~russian
void PIMutex::unlock() { void PIMutex::unlock() {
#if defined(WINDOWS) #if defined(WINDOWS)
LeaveCriticalSection(&(PRIVATE->mutex)); LeaveCriticalSection(&(PRIVATE->mutex));
@@ -90,6 +144,11 @@ 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 PIMutex::tryLock() {
bool ret = bool ret =
#if defined(WINDOWS) #if defined(WINDOWS)

View File

@@ -34,25 +34,25 @@ class PIP_EXPORT PIMutex
public: public:
NO_COPY_CLASS(PIMutex) NO_COPY_CLASS(PIMutex)
//! Constructs unlocked mutex //! \~english Constructs unlocked mutex
//! \~russian Создает незаблокированный мьютекс
explicit PIMutex(); explicit PIMutex();
//! Destroy mutex //! \~english Destroy mutex
//! \~russian Деструктор мьютекса
~PIMutex(); ~PIMutex();
//! \brief Lock mutex //! \~english Lock mutex
//! \details If mutex is unlocked it set to locked state and returns immediate. //! \~russian Блокирует мьютекс
//! If mutex is already locked function blocks until mutex will be unlocked
void lock(); void lock();
//! \brief Unlock mutex //! \~english Unlock mutex
//! \details In any case this function returns immediate //! \~russian Разблокирует мьютекс
void unlock() ; void unlock();
//! \brief Try to lock mutex //! \~english Try to lock mutex
//! \details If mutex is unlocked it set to locked state and returns "true" immediate. //! \~russian Пробует заблокировать мьютекс
//! If mutex is already locked function returns immediate an returns "false"
bool tryLock(); bool tryLock();
void * handle(); void * handle();
@@ -66,22 +66,24 @@ private:
}; };
//! \brief PIMutexLocker
//! \details Same as std::lock_guard<std::mutex>.
//! 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 class PIP_EXPORT PIMutexLocker
{ {
public: public:
NO_COPY_CLASS(PIMutexLocker) 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();} 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();} ~PIMutexLocker() {if (cond) mutex.unlock();}
private: private:
PIMutex & mutex; PIMutex & mutex;
bool cond; bool cond;
}; };
#endif // PIMUTEX_H #endif // PIMUTEX_H

View File

@@ -17,14 +17,49 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** \class PISpinlock //! \addtogroup Thread
* \brief Spinlock //! \{
* \details //! \class PISpinlock pispinlock.h
* \section PISpinlock_sec0 Synopsis //!
* %PISpinlock provides synchronization blocks between several threads. //! \~\brief
* PISpinlock functionality similar to PIMutex, but working on atomic //! \~english Fast and full-load lock
* type and \a lock() method wait with 100% CPU load. //! \~russian Быстрая блокировка с полной нагрузкой
* \note //!
* Use this type instead of PIMutex when less waiting time is more //! \~\details
* important than CPU load! //! \~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
//!
//! \}

View File

@@ -35,20 +35,30 @@ class PIP_EXPORT PISpinlock
public: public:
NO_COPY_CLASS(PISpinlock) NO_COPY_CLASS(PISpinlock)
//! Constructs unlocked spinlock //! \~english Constructs unlocked spinlock
//! \~russian
explicit PISpinlock() {flag.clear();} explicit PISpinlock() {flag.clear();}
//! Destroy spinlock //! \~english Destroy spinlock
//! \~russian
~PISpinlock() {} ~PISpinlock() {}
//! \brief Lock spinlock //! \~english Lock spinlock
//! \details If spinlock is unlocked it set to locked state and returns immediate. //! \~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 //! If spinlock is already locked function blocks until spinlock will be unlocked
//! \~russian
void lock() {while (flag.test_and_set(std::memory_order_acquire));} void lock() {while (flag.test_and_set(std::memory_order_acquire));}
//! \brief Unlock spinlock //! \~english Unlock spinlock
//! \details In any case this function returns immediate //! \~russian
//! \~\details
//! \~english
//! In any case this function returns immediate
//! \~russian
void unlock() {flag.clear(std::memory_order_release);} void unlock() {flag.clear(std::memory_order_release);}
private: 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 class PIP_EXPORT PISpinlockLocker
{ {
public: public:
NO_COPY_CLASS(PISpinlockLocker) 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();} 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();} ~PISpinlockLocker() {if (cond) spinlock.unlock();}
private: private:
PISpinlock & spinlock; PISpinlock & spinlock;
bool cond; bool cond;
}; };
#endif // PISPINLOCK_H #endif // PISPINLOCK_H

View File

@@ -54,54 +54,85 @@ __THREAD_FUNC_RET__ thread_function_once(void * t) {((PIThread*)t)->__thread_fun
# define UNREGISTER_THREAD(t) # define UNREGISTER_THREAD(t)
#endif #endif
/*! \class PIThread //! \addtogroup Thread
* \brief Thread class //! \{
* \details This class allow you exec your code in separate thread. //! \class PIThread pithread.h
* //! \~\brief
* \section PIThread_sec0 Synopsis //! \~english Thread class
* Multithreading allow you to write program which will be executed //! \~russian Класс потока
* in several threads simultaneously. This trend allow you to use all //!
* cores of modern processors, but there are many dangers. //! \~\details
* //! \~english
* This class provide virtual functions \a begin(), \a run() and \a end(), //! This class allow you exec your code in separate thread.
* which describes start, execution and finish work of some process. //!
* These functions executes in \b separate thread. When you execute //! \~russian
* \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: //! \~english \section PIThread_sec0 Synopsis
\code{.cpp} //! \~russian \section PIThread_sec0 Краткий обзор
begin(); //! \~english
event started(); //! Multithreading allow you to write program which will be executed
while (isRunning()) { //! in several threads simultaneously. This trend allow you to use all
run(); //! cores of modern processors, but there are many dangers.
ThreadFunc(); //!
piMSleep(timer_delay); //! This class provide virtual functions \a begin(), \a run() and \a end(),
} //! which describes start, execution and finish work of some process.
event stopped(); //! These functions executes in \b separate thread. When you execute
end(); //! \a start(), %PIThread create separate system thread and sequentially
\endcode //! executes function \a begin(), \a run() and \a end(). You can
* Unlike from directly using "pthread" or some similar you doesn`t need //! reimplement each function and write your own code to execute.
* to write your own main thread cycle and sleep at every cycle end. //! Scheme of functions executing:
* %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() //! \~russian
* and \a end() functions. //!
* //! \~\code{.cpp}
* \section PIThread_sec1 Using without subclassing //! begin();
* You can use %PIThread without subclassing by using "ThreadFunc" pointer //! event started();
* that can be set from constructor or by overloaded function \a start(ThreadFunc func, int timer_delay). //! while (isRunning()) {
* If "func" if not null this function will be executed as \a run(). ThreadFunc is any static //! run();
* function with format void func(void * data). "Data" is custom data set from constructor or //! ThreadFunc();
* with function \a setData(). \n Also you can connect to event \a started(), but //! piMSleep(timer_delay);
* in this case you should to white your thread main cycle, because this event raised only one time. //! }
* //! event stopped();
* \section PIThread_sec2 Locking //! end();
* %PIThread has inrternal mutex that can be locked and unlocked every \a run() if you set this flag //! \endcode
* 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 //! \~english
* some data. //! 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 #ifndef MICRO_PIP

View File

@@ -65,6 +65,7 @@ static __PIThreadCollection_Initializer__ __PIThreadCollection_initializer__;
typedef std::function<void(void *)> ThreadFunc; typedef std::function<void(void *)> ThreadFunc;
class PIP_EXPORT PIThread: public PIObject class PIP_EXPORT PIThread: public PIObject
{ {
PIOBJECT_SUBCLASS(PIThread, PIObject) PIOBJECT_SUBCLASS(PIThread, PIObject)
@@ -74,24 +75,28 @@ class PIP_EXPORT PIThread: public PIObject
public: public:
NO_COPY_CLASS(PIThread) 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); 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<void()> func, bool startNow = false, int loop_delay = -1); PIThread(std::function<void()> 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); PIThread(bool startNow = false, int loop_delay = -1);
virtual ~PIThread(); virtual ~PIThread();
//! Priority of thread //! \~english Priority of thread
//! \~russian Приоритет потока
enum Priority { enum Priority {
piLowerst /** Lowest */, piLowerst /** \~english Lowest \~russian Низший */ ,
piLow /** Low */, piLow /** \~english Low \~russian Низкий */ ,
piNormal /** Normal, this is default priority of threads and timers */, piNormal /** \~english Normal, this is default priority of threads and timers \~russian Нормальный, это приоритет по умолчанию для потоков и таймеров */ ,
piHigh /** High */, piHigh /** \~english High \~russian Высокий */ ,
piHighest /** Highest */ piHighest /** \~english Highest \~russian Высший */
}; };
EVENT_HANDLER0(bool, start) {return start(-1);} EVENT_HANDLER0(bool, start) {return start(-1);}
@@ -106,27 +111,36 @@ public:
EVENT_HANDLER1(void, stop, bool, wait); EVENT_HANDLER1(void, stop, bool, wait);
EVENT_HANDLER0(void, terminate); 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;} 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;} 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<void()> func) {ret_func = [func](void*){func();};} void setSlot(std::function<void()> func) {ret_func = [func](void*){func();};}
//! \brief Set priority of thread //! \~english Set thread priority
//! \~russian Устанавливает приоритет потока
void setPriority(PIThread::Priority prior); 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_;} void * data() const {return data_;}
//! \brief Return priority of thread //! \~english Return thread priority
//! \~russian Возвращает приоритет потока
PIThread::Priority priority() const {return priority_;} 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_;} bool isRunning() const {return running_;}
//! \~english Return if thread is stopping
//! \~russian Возвращает останавливается ли поток
bool isStopping() const {return running_ && terminating;} bool isStopping() const {return running_ && terminating;}
EVENT_HANDLER0(bool, waitForStart) {return waitForStart(-1);} EVENT_HANDLER0(bool, waitForStart) {return waitForStart(-1);}
@@ -134,19 +148,19 @@ public:
EVENT_HANDLER0(bool, waitForFinish) {return waitForFinish(-1);} EVENT_HANDLER0(bool, waitForFinish) {return waitForFinish(-1);}
EVENT_HANDLER1(bool, waitForFinish, int, timeout_msecs); 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;} void needLockRun(bool need) {lockRun = need;}
//! \brief Lock internal mutex
EVENT_HANDLER0(void, lock) const {thread_mutex.lock();} EVENT_HANDLER0(void, lock) const {thread_mutex.lock();}
//! \brief Unlock internal mutex
EVENT_HANDLER0(void, unlock) const {thread_mutex.unlock();} EVENT_HANDLER0(void, unlock) const {thread_mutex.unlock();}
//! \brief Returns internal mutex //! \~english Returns internal mutex
//! \~russian Возвращает внутренний мьютекс
PIMutex & mutex() const {return thread_mutex;} PIMutex & mutex() const {return thread_mutex;}
//! \brief Returns thread ID //! \~english Returns thread ID
//! \~russian Возвращает ID потока
llong tid() const {return tid_;} llong tid() const {return tid_;}
void __thread_func__(); void __thread_func__();
@@ -155,93 +169,129 @@ public:
EVENT(started) EVENT(started)
EVENT(stopped) EVENT(stopped)
//! \brief Start event handler with name \"handler\" of object \"object\" //! \~english
//! in separate thread with name \"name\" //! Start event handler with name "handler" of object "object"
//! in separate thread with name "name"
//! and automatically delete it on function finish //! and automatically delete it on function finish
//!
//! \~russian
static void runOnce(PIObject * object, const char * handler, const PIString & name = PIString()); 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 //! and automatically delete it on function finish
//!
//! \~russian
static void runOnce(std::function<void()> func, const PIString & name = PIString()); static void runOnce(std::function<void()> func, const PIString & name = PIString());
//! \handlers //! \handlers
//! \{ //! \{
/** \fn bool start(int timer_delay = -1) //! \fn bool start(int timer_delay = -1)
* \brief Start thread //! \~english Start thread
* \details Start execution of \a run() in internal loop with //! \~russian Запускает поток
* "timer_delay" delay in milliseconds. If "timer_delay" <= 0 //! \~\details
* there is no delay in loop. Thread also exec external function //! \~english
* set by \a setSlot() if it`s not null //! Start execution of \a run() in internal loop with
* //! "timer_delay" delay in milliseconds. If "timer_delay" <= 0
* \return \c false if thread already started or can`t start thread */ //! 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() //! \fn bool startOnce()
* \brief Start thread without internal loop //! \~english Start thread without internal loop
* \details Start execution of \a run() once. Thread also exec //! \~russian Запускает поток без внутреннего цикла
* external function set by \a setSlot() if it`s not null //! \~\details
* //! \~english
* \return \c false if thread already started or can`t start thread */ //! 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) //! \fn bool startOnce(ThreadFunc func)
* \brief Start thread without internal loop //! \~english Start thread without internal loop
* \details Overloaded function. Set external function "func" before start //! \~russian Запускает поток без внутреннего цикла
* //! \~\details
* \return \c false if thread already started or can`t start thread */ //! \~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) //! \fn void stop(bool wait = false)
* \brief Stop thread //! \~english Stop thread
* \details Stop execution of thread and wait for it finish //! \~russian Останавливает поток
* if "wait" is \c true. This function can block for infinite //! \~\details
* time if "wait" is \c true and any of thread function is //! \~english
* busy forever */ //! 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() //! \fn void terminate()
* \brief Strongly stop thread //! \~english Strongly stop thread
* \details Stop execution of thread immediately */ //! \~russian Жестко останавливает поток
//! \~\details
//! \~english
//! Stop execution of thread immediately
//! \~russian
/** \fn bool waitForStart(int timeout_msecs = -1) //! \fn bool waitForStart(int timeout_msecs = -1)
* \brief Wait for thread start //! \~english Wait for thread start
* \details This function block until thread start for "timeout_msecs" //! \~russian Ожидает старта потока
* or forever if "timeout_msecs" < 0 //! \~\details
* //! \~english
* \return \c false if timeout is exceeded */ //! 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) //! \fn bool waitForFinish(int timeout_msecs = -1)
* \brief Wait for thread finish //! \~english Wait for thread finish
* \details This function block until thread finish for "timeout_msecs" //! \~russian Ожидает завершения потока
* or forever if "timeout_msecs" < 0 //! \~\details
* //! \~english
* \return \c false if timeout is exceeded */ //! 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() //! \fn void lock()
//! \brief Lock internal mutex //! \~english Lock internal mutex
//! \~russian Блокирует внутренний мьютекс
//! \fn void unlock() //! \fn void unlock()
//! \brief Unlock internal mutex //! \~english Unlock internal mutex
//! \~russian Разблокирует внутренний мьютекс
//! \} //! \}
//! \events //! \events
//! \{ //! \{
//! \fn void started() //! \fn void started()
//! \brief Raise on thread start //! \~english Raise on thread start
//! \~russian Вызывается при старте потока
//! \fn void stopped() //! \fn void stopped()
//! \brief Raise on thread stop //! \~english Raise on thread stop
//! \~russian Вызывается при завершении потока
//! \} //! \}
protected: protected:
static int priority2System(PIThread::Priority p); 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() {;} 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() {;} virtual void run() {;}
//! Function executed once at the end of thread. //! \~english Function executed once at the end of thread
//! \~russian Метод выполняется один раз при остановке потока
virtual void end() {;} virtual void end() {;}
std::atomic_bool terminating, running_, lockRun; std::atomic_bool terminating, running_, lockRun;

View File

@@ -19,10 +19,50 @@
#include "pithreadnotifier.h" #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) {} 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() { void PIThreadNotifier::wait() {
m.lock(); m.lock();
while (cnt == 0) v.wait(m); 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() { void PIThreadNotifier::notifyOnce() {
m.lock(); m.lock();
cnt++; cnt++;

View File

@@ -33,17 +33,12 @@ class PIP_EXPORT PIThreadNotifier {
public: public:
PIThreadNotifier(); PIThreadNotifier();
//! Start waiting, return if other thread call \a notifyOnce(). //! \~english Start waiting, return if other thread call \a notifyOnce()
//! If \a notifyOnce() has been called before, then returns immediately. //! \~russian Начать ожидание, продолжает когда другой поток вызовет \a notifyOnce()
//! 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.
void wait(); void wait();
//! Notify one waiting thread, wich waiting on \a wait() function. //! \~english Notify one waiting thread, which waiting on \a wait() function
//! If many threads waiting, then notify randomly one. //! \~russian Уведомить один из ожидающих потоков, которые висят на методе \a wait()
//! If call this "n" times, then notify any waiting threads totally "n" times.
void notifyOnce(); void notifyOnce();
private: private:

View File

@@ -22,51 +22,82 @@
#include "pithread.h" #include "pithread.h"
/*! \class PIThreadPoolLoop //! \addtogroup Thread
* \brief Thread class //! \{
* \details This class allow you parallelize loop. //! \class PIThreadPoolLoop pithreadpoolloop.h
* //! \~english Thread pool loop
* \section PIThreadPoolLoop_sec0 Usage //! \~russian Пул потоков
* This class designed to replace "for(;;)" statement in very simple way. //!
* In constructor several threads created, then by "setFunction()" method //! \~\details
* you should pass body of your loop, and then call "start()" or "exec()". //! \~english
* Every thread take loop counter and execute your function until all //! This class allow you parallelize loop.
* counter range is passed. //!
* //! \~russian
* Example: //! Этот класс позволяет распараллелить цикл
\code{.cpp} //!
PIVector<int> data(10, [](int i)->int{return i;}); //!
//! \~english \section PIThreadPoolLoop_sec0 Usage
piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} //! \~russian \section PIThreadPoolLoop_sec0 Использование
//! \~english
PIThreadPoolLoop pool; //! This class designed to parallel "for(;;)" statement in very simple way.
pool.exec(0, data.size(), [&](int i){ // parallel analogue "for (int i = 0; i < data.size(); i++)" //! In constructor several threads created, then by \a setFunction() method
data[i] = data[i] + 10; //! 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.
piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} //!
\endcode //! Example:
* //! \~russian
* Equivalent to: //! Этот класс предназначен для распараллеливания цикла "for(;;)" максимально простым способом.
\code{.cpp} //! В конструкторе создается несколько потоков, затем методом \a setFunction()
PIVector<int> data(10, [](int i)->int{return i;}); //! устанавливается функция, представляющая собой тело цикла. Затем вызовом
//! \a start() или \a exec() цикл исполняется параллельно. Каждый поток получает
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()); //! \~\code{.cpp}
//! PIVector<int> data(10, [](int i)->int{return i;});
piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} //!
\endcode //! piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
* //!
* \section PIThreadPoolLoop_sec1 Important //! PIThreadPoolLoop pool;
* Due to multithreading it`s very important to protect output data of loop body, use mutex. //! pool.exec(0, data.size(), [&](int i){ // parallel analogue "for (int i = 0; i < data.size(); i++)"
* Also remember that execution order is undefined and you shouldn`t use global variables in //! data[i] = data[i] + 10;
* your function. Use local variables and lambda capture. //! });
* //!
*/ //! piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
//! \endcode
//!
//! \~english Equivalent to:
//! \~russian Эквивалентно:
//! \~\code{.cpp}
//! PIVector<int> 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) { PIThreadPoolLoop::PIThreadPoolLoop(int thread_cnt) {

View File

@@ -33,30 +33,50 @@ class PIThread;
class PIP_EXPORT PIThreadPoolLoop { class PIP_EXPORT PIThreadPoolLoop {
public: public:
//! \~english
//! Contructs thread pool with threads count "thread_cnt". //! Contructs thread pool with threads count "thread_cnt".
//! If "thread_cnt" = -1 then system processors count used //! If "thread_cnt" = -1 then system processors count used
//! \~russian
//! Создает пул из "thread_cnt" потоков. Если "thread_cnt" = -1
//! то используется количество процессоров системы
PIThreadPoolLoop(int thread_cnt = -1); PIThreadPoolLoop(int thread_cnt = -1);
virtual ~PIThreadPoolLoop(); 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<void(int)> f); void setFunction(std::function<void(int)> f);
//! Wait for all threads stop //! \~english Wait for all threads stop
//! \~russian Ожидает завершения всех потоков
void wait(); void wait();
//! \~english
//! Start functions execution with integer argument range //! Start functions execution with integer argument range
//! from "index_start" to "index_start + index_count - 1" //! 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); void start(int index_start, int index_count);
//! \~english
//! Start functions execution with integer argument range //! Start functions execution with integer argument range
//! from "index_start" to "index_start + index_count - 1" //! from "index_start" to "index_start + index_count - 1"
//! and wait for finish //! and wait for finish
//! \~russian
//! Начинает исполнение потоков с аргументами по диапазону
//! от "index_start" до "index_start + index_count - 1"
//! и ожидает завершения
void exec(int index_start, int index_count); void exec(int index_start, int index_count);
//! \~english
//! Start functions "f" execution with integer argument range //! Start functions "f" execution with integer argument range
//! from "index_start" to "index_start + index_count - 1" //! from "index_start" to "index_start + index_count - 1"
//! and wait for finish //! and wait for finish
//! \~russian
//! Начинает исполнение потоками функции "f" с аргументами по диапазону
//! от "index_start" до "index_start + index_count - 1"
//! и ожидает завершения
void exec(int index_start, int index_count, std::function<void(int)> f); void exec(int index_start, int index_count, std::function<void(int)> f);
private: private:

View File

@@ -24,31 +24,94 @@
#endif #endif
/*! \class PITimer //! \addtogroup Thread
* \brief Timer //! \{
* //! \class PITimer pitimer.h
* \section PITimer_sec0 Synopsis //!
* This class implements timer function. PIP timers supports 3 way to tick notify, //! \~\brief
* frequency delimiters and time measurements. //! \~english Timer
* \section PITimer_sec1 Notify variants //! \~russian Таймер
* Notify variants: //!
* * "slot" - static function with format void func(void * data, int delimiter); //! \~\details
* * event - \a tickEvent(); //!
* * virtual function - \a tick(). //! \~english \section PITimer_sec0 Synopsis
* //! \~russian \section PITimer_sec0 Краткий обзор
* All these variants are equivalent, use most applicable. //! \~english
* \section PITimer_sec2 Frequency delimiters //! This class implements timer function. PIP timers supports 3 way to tick notify,
* Frequency delimiter is an integer number and "slot" function. If "slot" function is null //! deferred start and frequency delimiters.
* 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. //! \~russian
* Example: \snippet pitimer.cpp delimiter //! Этот класс реализует таймер. Таймер PIP поддерживает 3 варианта уведомления,
* \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" //! \~english \section PITimer_sec1 Notify variants
* and "s" //! \~russian \section PITimer_sec1 Варианты уведомления
* Example: \snippet pitimer.cpp elapsed //! \~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() { _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) { void PITimer::startDeferred(double delay_ms) {
init(); init();
imp->startDeferred(delay_ms); 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) { void PITimer::startDeferred(double interval_ms, double delay_ms) {
init(); init();
imp->startDeferred(interval_ms, delay_ms); 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) { void PITimer::startDeferred(PIDateTime start_datetime) {
startDeferred(imp->interval_, 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) { void PITimer::startDeferred(double interval_ms, PIDateTime start_datetime) {
init(); init();
imp->startDeferred(interval_ms, start_datetime); imp->startDeferred(interval_ms, start_datetime);

View File

@@ -42,7 +42,6 @@ public:
double interval() const {return interval_;} double interval() const {return interval_;}
void setInterval(double i); void setInterval(double i);
//! \brief Return \c true if thread is running
bool isRunning() const {return running_;} bool isRunning() const {return running_;}
bool isStopped() const {return !running_;} bool isStopped() const {return !running_;}
@@ -79,45 +78,64 @@ class PIP_EXPORT PITimer: public PIObject {
public: public:
NO_COPY_CLASS(PITimer) NO_COPY_CLASS(PITimer)
//! \brief Constructs timer with PITimer::Thread implementation //! \~english Constructs timer with PITimer::Thread implementation
//! \~russian Создает таймер с реализацией PITimer::Thread
explicit PITimer(); explicit PITimer();
//! \brief Timer implementations //! \~english Timer implementations
//! \~russian Реализация таймера
enum TimerImplementation { enum TimerImplementation {
Thread /*! Timer works in his own thread. Intervals are measured by the system time */ = 0x01, Thread /*!
ThreadRT /*! Using POSIX timer with SIGEV_THREAD notification. \attention Doesn`t support on Windows and Mac OS! */ = 0x02, \~english Timer works in his own thread. Intervals are measured by the system time
Pool /*! Using single TimerPool for all timers with this implementation. TimerPool works as Thread implementation and \~russian Таймер работает в собственном потоке. Интервалы измеряются с помощью системного времени
* sequentially executes all timers. \attention Use this implementation with care! */ = 0x04 */ = 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); 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); 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<void ()> slot, TimerImplementation ti = Thread); explicit PITimer(std::function<void ()> 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<void (void *)> slot, void * data, TimerImplementation ti = Thread); explicit PITimer(std::function<void (void *)> slot, void * data, TimerImplementation ti = Thread);
virtual ~PITimer(); virtual ~PITimer();
//! \brief Returns timer implementation //! \~english Returns timer implementation
//! \~russian Возвращает реализацию таймера
PITimer::TimerImplementation implementation() const {return imp_mode;} 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; double interval() const;
//! \brief Set timer loop delay in milliseconds
EVENT_HANDLER1(void, setInterval, double, ms); EVENT_HANDLER1(void, setInterval, double, ms);
//! \brief Returns if timer is started //! \~english Returns if timer is started
//! \~russian Возвращает работает ли таймер
bool isRunning() const; bool isRunning() const;
//! \brief Returns if timer is not started //! \~english Returns if timer is not started
//! \~russian Возвращает остановлен ли таймер
bool isStopped() const; bool isStopped() const;
EVENT_HANDLER0(bool, start); EVENT_HANDLER0(bool, start);
@@ -126,24 +144,20 @@ public:
EVENT_HANDLER0(bool, restart); EVENT_HANDLER0(bool, restart);
/** \brief Start timer with \b interval() loop delay after \b delay_msecs delay. //! \~english Start timer with \a interval() loop delay after "delay_msecs" delay
* \details Timer wait \b delay_msecs milliseconds and then normally starts with //! \~russian Запускает таймер с интервалом \a interval() после ожидания "delay_msecs"
* \b interval() loop delay. */
void startDeferred(double delay_ms); void startDeferred(double delay_ms);
/** \brief Start timer with \b interval_msecs loop delay after \b delay_msecs delay. //! \~english Start timer with "interval_msecs" loop delay after "delay_msecs" delay
* \details Timer wait \b delay_msecs milliseconds and then normally starts with //! \~russian Запускает таймер с интервалом "interval_msecs" после ожидания "delay_msecs"
* \b interval_msecs loop delay. */
void startDeferred(double interval_ms, double delay_ms); void startDeferred(double interval_ms, double delay_ms);
/** \brief Start timer with \b interval() loop delay after \b start_datetime date and time. //! \~english Start timer with \a interval() loop delay after "start_datetime" date and time
* \details Timer wait until \b start_datetime and then normally starts with //! \~russian Запускает таймер с интервалом \a interval() после наступления "start_datetime"
* \b interval() loop delay. */
void startDeferred(PIDateTime start_datetime); void startDeferred(PIDateTime start_datetime);
/** \brief Start timer with \b interval_msecs loop delay after \b start_datetime date and time. //! \~english Start timer with "interval_msecs" loop delay after "start_datetime" date and time
* \details Timer wait until \b start_datetime and then normally starts with //! \~russian Запускает таймер с интервалом "interval_msecs" после наступления "start_datetime"
* \b interval_msecs loop delay. */
void startDeferred(double interval_ms, PIDateTime start_datetime); void startDeferred(double interval_ms, PIDateTime start_datetime);
EVENT_HANDLER0(bool, stop); EVENT_HANDLER0(bool, stop);
@@ -151,43 +165,52 @@ public:
bool waitForFinish() {return waitForFinish(-1);} bool waitForFinish() {return waitForFinish(-1);}
bool waitForFinish(int timeout_msecs); bool waitForFinish(int timeout_msecs);
//! \brief Set custom data //! \~english Set custom data
//! \~russian Установить данные, передаваемые в метод таймера
void setData(void * data_) {data_t = data_;} 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;} void setSlot(TimerEvent slot) {ret_func = slot;}
//! \brief Set timer tick function //! \~english Set timer tick function
//! \~russian Установить вызываемый метод
void setSlot(std::function<void ()> slot) {ret_func = [slot](void *, int){slot();};} void setSlot(std::function<void ()> slot) {ret_func = [slot](void *, int){slot();};}
//! \brief Set timer tick function //! \~english Set timer tick function
//! \~russian Установить вызываемый метод
void setSlot(std::function<void (void *)> slot) {ret_func = [slot](void *d, int){slot(d);};} void setSlot(std::function<void (void *)> 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;} void needLockRun(bool need) {lockRun = need;}
EVENT_HANDLER0(void, lock) {mutex_.lock();} EVENT_HANDLER0(void, lock) {mutex_.lock();}
EVENT_HANDLER0(void, unlock) {mutex_.unlock();} EVENT_HANDLER0(void, unlock) {mutex_.unlock();}
//! \brief Returns if timer should exec \a maybeCallQueuedEvents() at every tick. //! \~english Returns if timer should exec \a maybeCallQueuedEvents() at every tick. By default \b true
//! By default \b true //! \~russian Возвращает должен ли таймер вызывать \a maybeCallQueuedEvents() каждый тик. По умолчанию \b true
bool isCallQueuedEvents() const {return callEvents;} bool isCallQueuedEvents() const {return callEvents;}
//! \brief If set timer exec \a maybeCallQueuedEvents() at every tick. //! \~english Set timer exec \a maybeCallQueuedEvents() at every tick
//! By default \b true //! \~russian Установает должен ли таймер вызывать \a maybeCallQueuedEvents() каждый тик
void setCallQueuedEvents(bool yes) {callEvents = yes;} 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);} 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<void ()> slot) {delims << Delimiter([slot](void *, int){slot();}, delim);} void addDelimiter(int delim, std::function<void ()> 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<void (void *)> slot) {delims << Delimiter([slot](void *d, int){slot(d);}, delim);} void addDelimiter(int delim, std::function<void (void *)> 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--;}} 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();} EVENT_HANDLER0(void, clearDelimiters) {delims.clear();}
@@ -197,33 +220,58 @@ public:
//! \handlers //! \handlers
//! \{ //! \{
/** \fn bool start() //! \fn void setInterval(double ms)
* \brief Start timer with \a interval() loop delay //! \~english Set timer loop delay in milliseconds
* \details Start execution of timer functions with frequency = 1 / msecs Hz. */ //! \~russian Установить интервал таймера "ms" миллисекунд
/** \fn bool start(double msecs) //! \fn bool start()
* \brief Start timer with \b msecs loop delay //! \~english Start timer with \a interval() loop delay
* \details Start execution of timer functions with frequency = 1. / msecs Hz. //! \~russian Запустить таймер с интервалом \a interval()
* Instead of \a start(int msecs) function this variant allow start timer //! \~\details
* with frequencies more than 1 kHz */ //! \~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() //! \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) //! \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() //! \fn void clearDelimiters()
//! \brief Remove all frequency delimiters //! \~english Remove all frequency delimiters
//! \~russian Удаляет все делители частоты
//! \} //! \}
//! \events //! \events
//! \{ //! \{
/** \fn void tickEvent(void * data, int delimiter) //! \fn void tickEvent(void * data, int delimiter)
* \brief Raise on timer tick //! \~english Raise on timer tick
* \details \b Data can be set with function \a setData(void * data) or from constructor. //! \~russian Вызывается каждый тик таймера
* \b Delimiter is frequency delimiter, 1 for main loop. */ //! \~\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();} static void tickImpS(PITimer * t) {t->tickImp();}
void tickImp(); void tickImp();
//! Virtual timer execution function, similar to "slot" or event \a void timeout(void * data, int delimiter). //! Timer execution function, similar to "slot" or event \a timeout(). By default does nothing
//! By default is empty.
virtual void tick(void * data_, int delimiter) {} virtual void tick(void * data_, int delimiter) {}
void * data_t; void * data_t;