git-svn-id: svn://db.shs.com.ru/pip@4 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
This commit is contained in:
59
src/thread/pimutex.cpp
Executable file
59
src/thread/pimutex.cpp
Executable file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Mutex
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pimutex.h"
|
||||
|
||||
|
||||
/** \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.
|
||||
*
|
||||
* */
|
||||
|
||||
|
||||
PIMutex::PIMutex() {
|
||||
#ifdef WINDOWS
|
||||
mutex = CreateMutex(0, false, 0);
|
||||
#else
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutex_init(&mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PIMutex::~PIMutex() {
|
||||
#ifdef WINDOWS
|
||||
if (mutex != 0) CloseHandle(mutex);
|
||||
#else
|
||||
pthread_mutex_destroy(&mutex);
|
||||
#endif
|
||||
}
|
||||
93
src/thread/pimutex.h
Executable file
93
src/thread/pimutex.h
Executable file
@@ -0,0 +1,93 @@
|
||||
/*! \file pimutex.h
|
||||
* \brief Mutex
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Mutex
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIMUTEX_H
|
||||
#define PIMUTEX_H
|
||||
|
||||
#include "piinit.h"
|
||||
|
||||
class PIP_EXPORT PIMutex
|
||||
{
|
||||
public:
|
||||
|
||||
//! Constructs unlocked mutex
|
||||
PIMutex();
|
||||
|
||||
~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
|
||||
void lock() {
|
||||
#ifdef WINDOWS
|
||||
WaitForSingleObject(mutex, INFINITE);
|
||||
#else
|
||||
pthread_mutex_lock(&mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//! \brief Unlock mutex
|
||||
//! \details In any case this function returns immediate
|
||||
void unlock() {
|
||||
#ifdef WINDOWS
|
||||
ReleaseMutex(mutex);
|
||||
#else
|
||||
pthread_mutex_unlock(&mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//! \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"
|
||||
bool tryLock() {
|
||||
#ifdef WINDOWS
|
||||
return (WaitForSingleObject(mutex, 0) == WAIT_OBJECT_0);
|
||||
#else
|
||||
return (pthread_mutex_trylock(&mutex) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef WINDOWS
|
||||
void *
|
||||
#else
|
||||
pthread_mutex_t
|
||||
#endif
|
||||
mutex;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT PIMutexLocker
|
||||
{
|
||||
public:
|
||||
PIMutexLocker(PIMutex * m): mutex(m) {mutex->lock();}
|
||||
PIMutexLocker(PIMutex & m): mutex(&m) {mutex->lock();}
|
||||
~PIMutexLocker() {mutex->unlock();}
|
||||
private:
|
||||
PIMutex * mutex;
|
||||
};
|
||||
|
||||
#endif // PIMUTEX_H
|
||||
339
src/thread/pithread.cpp
Executable file
339
src/thread/pithread.cpp
Executable file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Thread
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pithread.h"
|
||||
#include "pisystemtests.h"
|
||||
|
||||
|
||||
/*! \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();
|
||||
msleep(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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, int timer_delay): PIObject() {
|
||||
piMonitor.threads++;
|
||||
thread = 0;
|
||||
data_ = data;
|
||||
ret_func = func;
|
||||
terminating = running_ = lockRun = false;
|
||||
priority_ = piNormal;
|
||||
delay_ = timer_delay;
|
||||
if (startNow) start(timer_delay);
|
||||
}
|
||||
|
||||
|
||||
PIThread::PIThread(bool startNow, int timer_delay): PIObject() {
|
||||
piMonitor.threads++;
|
||||
thread = 0;
|
||||
ret_func = 0;
|
||||
terminating = running_ = lockRun = false;
|
||||
priority_ = piNormal;
|
||||
delay_ = timer_delay;
|
||||
if (startNow) start(timer_delay);
|
||||
}
|
||||
|
||||
|
||||
PIThread::~PIThread() {
|
||||
piMonitor.threads--;
|
||||
if (!running_ || thread == 0) return;
|
||||
#ifndef WINDOWS
|
||||
# ifdef ANDROID
|
||||
pthread_kill(thread, SIGSTOP);
|
||||
# else
|
||||
pthread_cancel(thread);
|
||||
# endif
|
||||
#else
|
||||
TerminateThread(thread, 0);
|
||||
CloseHandle(thread);
|
||||
#endif
|
||||
terminating = running_ = false;
|
||||
}
|
||||
|
||||
|
||||
bool PIThread::start(int timer_delay) {
|
||||
if (running_) return false;
|
||||
terminating = running_ = false;
|
||||
delay_ = timer_delay;
|
||||
#ifndef WINDOWS
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setschedparam(&attr, &sparam);
|
||||
if (pthread_create(&thread, &attr, thread_function, this) == 0) {
|
||||
setPriority(priority_);
|
||||
running_ = true;
|
||||
return true;
|
||||
#else
|
||||
thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_function, this, 0, 0);
|
||||
if (thread != 0) {
|
||||
setPriority(priority_);
|
||||
running_ = true;
|
||||
return true;
|
||||
#endif
|
||||
} else {
|
||||
thread = 0;
|
||||
piCoutObj << "Error: Can`t start new thread:" << errorString();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool PIThread::startOnce() {
|
||||
if (running_) return false;
|
||||
terminating = running_ = false;
|
||||
#ifndef WINDOWS
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setschedparam(&attr, &sparam);
|
||||
if (pthread_create(&thread, &attr, thread_function_once, this) == 0) {
|
||||
setPriority(priority_);
|
||||
running_ = true;
|
||||
return true;
|
||||
#else
|
||||
thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_function_once, this, 0, 0);
|
||||
if (thread != 0) {
|
||||
setPriority(priority_);
|
||||
running_ = true;
|
||||
return false;
|
||||
#endif
|
||||
} else {
|
||||
thread = 0;
|
||||
piCoutObj << "Error: Can`t start new thread:" << errorString();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIThread::terminate() {
|
||||
if (thread == 0) return;
|
||||
terminating = running_ = false;
|
||||
#ifndef WINDOWS
|
||||
# ifdef ANDROID
|
||||
pthread_kill(thread, SIGKILL);
|
||||
# else
|
||||
//pthread_kill(thread, SIGKILL);
|
||||
pthread_cancel(thread);
|
||||
# endif
|
||||
#else
|
||||
TerminateThread(thread, 0);
|
||||
CloseHandle(thread);
|
||||
#endif
|
||||
thread = 0;
|
||||
end();
|
||||
}
|
||||
|
||||
|
||||
void * PIThread::thread_function(void * t) {
|
||||
#ifndef WINDOWS
|
||||
# ifndef ANDROID
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
||||
# endif
|
||||
#else
|
||||
__PISetTimerResolution();
|
||||
#endif
|
||||
PIThread & ct = *((PIThread * )t);
|
||||
ct.running_ = true;
|
||||
if (ct.lockRun) ct.mutex_.lock();
|
||||
ct.begin();
|
||||
if (ct.lockRun) ct.mutex_.unlock();
|
||||
ct.started();
|
||||
while (!ct.terminating) {
|
||||
if (ct.lockRun) ct.mutex_.lock();
|
||||
ct.run();
|
||||
if (ct.ret_func != 0) ct.ret_func(ct.data_);
|
||||
if (ct.lockRun) ct.mutex_.unlock();
|
||||
if (ct.delay_ > 0) {
|
||||
ct.tmr_.reset();
|
||||
double sl(0.);
|
||||
while (1) {
|
||||
sl = piMind(ct.delay_ - ct.tmr_.elapsed_m(), 2.);
|
||||
if (sl <= 0.) break;
|
||||
piMSleep(sl);
|
||||
if (ct.terminating)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ct.stopped();
|
||||
if (ct.lockRun) ct.mutex_.lock();
|
||||
ct.end();
|
||||
if (ct.lockRun) ct.mutex_.unlock();
|
||||
ct.terminating = ct.running_ = false;
|
||||
//cout << "thread " << t << " exiting ... " << endl;
|
||||
#ifndef WINDOWS
|
||||
pthread_exit(0);
|
||||
#else
|
||||
ExitThread(0);
|
||||
#endif
|
||||
ct.thread = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void * PIThread::thread_function_once(void * t) {
|
||||
#ifndef WINDOWS
|
||||
# ifndef ANDROID
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
||||
# endif
|
||||
#else
|
||||
__PISetTimerResolution();
|
||||
#endif
|
||||
PIThread & ct = *((PIThread * )t);
|
||||
ct.running_ = true;
|
||||
ct.begin();
|
||||
ct.started();
|
||||
if (ct.lockRun) ct.mutex_.lock();
|
||||
ct.run();
|
||||
if (ct.ret_func != 0) ct.ret_func(ct.data_);
|
||||
if (ct.lockRun) ct.mutex_.unlock();
|
||||
ct.stopped();
|
||||
ct.end();
|
||||
ct.terminating = ct.running_ = false;
|
||||
//cout << "thread " << t << " exiting ... " << endl;
|
||||
#ifndef WINDOWS
|
||||
pthread_exit(0);
|
||||
#else
|
||||
ExitThread(0);
|
||||
#endif
|
||||
ct.thread = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int PIThread::priority2System(PIThread::Priority p) {
|
||||
switch (p) {
|
||||
# ifdef QNX
|
||||
case piLowerst: return 8;
|
||||
case piLow: return 9;
|
||||
case piNormal: return 10;
|
||||
case piHigh: return 11;
|
||||
case piHighest: return 12;
|
||||
# else
|
||||
# ifdef WINDOWS
|
||||
case piLowerst: return -2;
|
||||
case piLow: return -1;
|
||||
case piNormal: return 0;
|
||||
case piHigh: return 1;
|
||||
case piHighest: return 2;
|
||||
# else
|
||||
case piLowerst: return 2;
|
||||
case piLow: return 1;
|
||||
case piNormal: return 0;
|
||||
case piHigh: return -1;
|
||||
case piHighest: return -2;
|
||||
# endif
|
||||
# endif
|
||||
default: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void PIThread::setPriority(PIThread::Priority prior) {
|
||||
priority_ = prior;
|
||||
#ifndef WINDOWS
|
||||
if (!running_ && thread != 0) return;
|
||||
pthread_getschedparam(thread, &policy_, &sparam);
|
||||
sparam.
|
||||
# ifndef LINUX
|
||||
sched_priority
|
||||
# else
|
||||
__sched_priority
|
||||
# endif
|
||||
= priority2System(priority_);
|
||||
pthread_setschedparam(thread, policy_, &sparam);
|
||||
#else
|
||||
if (!running_ && thread != 0) return;
|
||||
SetThreadPriority(thread, priority2System(priority_));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool PIThread::waitForFinish(int timeout_msecs) {
|
||||
if (!running_) return true;
|
||||
if (timeout_msecs < 0) {
|
||||
while (running_)
|
||||
msleep(1);
|
||||
return true;
|
||||
}
|
||||
tmf_.reset();
|
||||
while (running_ && tmf_.elapsed_m() < timeout_msecs)
|
||||
msleep(1);
|
||||
return tmf_.elapsed_m() < timeout_msecs;
|
||||
}
|
||||
|
||||
|
||||
bool PIThread::waitForStart(int timeout_msecs) {
|
||||
if (running_) return true;
|
||||
if (timeout_msecs < 0) {
|
||||
while (!running_)
|
||||
msleep(1);
|
||||
return true;
|
||||
}
|
||||
tms_.reset();
|
||||
while (!running_ && tms_.elapsed_m() < timeout_msecs)
|
||||
msleep(1);
|
||||
return tms_.elapsed_m() < timeout_msecs;
|
||||
}
|
||||
208
src/thread/pithread.h
Executable file
208
src/thread/pithread.h
Executable file
@@ -0,0 +1,208 @@
|
||||
/*! \file pithread.h
|
||||
* \brief Thread
|
||||
*
|
||||
* This file declare thread class and some wait functions
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Thread
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PITHREAD_H
|
||||
#define PITHREAD_H
|
||||
|
||||
#include <signal.h>
|
||||
#include "piinit.h"
|
||||
#include "pimutex.h"
|
||||
#include "piobject.h"
|
||||
|
||||
typedef void (*ThreadFunc)(void * );
|
||||
|
||||
class PIP_EXPORT PIThread: public PIObject
|
||||
{
|
||||
PIOBJECT(PIThread)
|
||||
public:
|
||||
|
||||
//! Contructs thread with custom data "data", external function "func" and main loop delay "loop_delay".
|
||||
PIThread(void * data, ThreadFunc func, bool startNow = false, int loop_delay = -1);
|
||||
|
||||
//! Contructs thread with main loop delay "loop_delay".
|
||||
PIThread(bool startNow = false, int loop_delay = -1);
|
||||
|
||||
virtual ~PIThread();
|
||||
|
||||
//! Priority of thread
|
||||
enum Priority {
|
||||
piLowerst /** Lowest */,
|
||||
piLow /** Low */,
|
||||
piNormal /** Normal, this is default priority of threads and timers */,
|
||||
piHigh /** High */,
|
||||
piHighest /** Highest */
|
||||
};
|
||||
|
||||
EVENT_HANDLER0(bool, start) {return start(-1);}
|
||||
EVENT_HANDLER1(bool, start, int, timer_delay);
|
||||
EVENT_HANDLER1(bool, start, ThreadFunc, func) {ret_func = func; return start(-1);}
|
||||
EVENT_HANDLER2(bool, start, ThreadFunc, func, int, timer_delay) {ret_func = func; return start(timer_delay);}
|
||||
EVENT_HANDLER0(bool, startOnce);
|
||||
EVENT_HANDLER1(bool, startOnce, ThreadFunc, func) {ret_func = func; return startOnce();}
|
||||
EVENT_HANDLER0(void, stop) {stop(false);}
|
||||
EVENT_HANDLER1(void, stop, bool, wait) {terminating = true; if (wait) waitForFinish();}
|
||||
EVENT_HANDLER0(void, terminate);
|
||||
|
||||
//! \brief Set common data passed to external function
|
||||
void setData(void * d) {data_ = d;}
|
||||
|
||||
//! \brief Set external function that will be executed after every \a run()
|
||||
void setSlot(ThreadFunc func) {ret_func = func;}
|
||||
|
||||
//! \brief Set priority of thread
|
||||
void setPriority(PIThread::Priority prior);
|
||||
|
||||
//! \brief Returns common data passed to external function
|
||||
void * data() const {return data_;}
|
||||
|
||||
//! \brief Return priority of thread
|
||||
PIThread::Priority priority() const {return priority_;}
|
||||
|
||||
//! \brief Return \c true if thread is running
|
||||
bool isRunning() const {return running_;}
|
||||
|
||||
bool isStopping() const {return running_ && terminating;}
|
||||
|
||||
EVENT_HANDLER0(bool, waitForStart) {return waitForStart(-1);}
|
||||
EVENT_HANDLER1(bool, waitForStart, int, timeout_msecs);
|
||||
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
|
||||
void needLockRun(bool need) {lockRun = need;}
|
||||
EVENT_HANDLER0(void, lock) {mutex_.lock();}
|
||||
EVENT_HANDLER0(void, unlock) {mutex_.unlock();}
|
||||
|
||||
//! \brief Return internal mutex
|
||||
PIMutex & mutex() {return mutex_;}
|
||||
|
||||
EVENT(started)
|
||||
EVENT(stopped)
|
||||
|
||||
//! \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(ThreadFunc func, int timer_delay = -1)
|
||||
* \brief Start thread
|
||||
* \details Overloaded function. Set external function "func" before start
|
||||
*
|
||||
* \return \c false if thread already started or can`t start thread */
|
||||
|
||||
/** \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(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 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 terminate()
|
||||
* \brief Strongly stop thread
|
||||
* \details Stop execution of thread immediately */
|
||||
|
||||
/** \fn bool waitForStart(int timeout_msecs = -1)
|
||||
* \brief Wait for thread start
|
||||
* \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)
|
||||
* \brief Wait for thread finish
|
||||
* \details This function block until thread start for "timeout_msecs"
|
||||
* or forever if "timeout_msecs" < 0
|
||||
*
|
||||
* \return \c false if timeout is exceeded */
|
||||
|
||||
//! \fn void lock()
|
||||
//! \brief Lock internal mutex
|
||||
|
||||
//! \fn void unlock()
|
||||
//! \brief Unlock internal mutex
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void started()
|
||||
//! \brief Raise on thread start
|
||||
|
||||
//! \fn void stopped()
|
||||
//! \brief Raise on thread stop
|
||||
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
static void * thread_function(void * t);
|
||||
static void * thread_function_once(void * t);
|
||||
static int priority2System(PIThread::Priority p);
|
||||
|
||||
|
||||
//! Function executed once at the start of thread.
|
||||
virtual void begin() {;}
|
||||
|
||||
//! Function executed at every "timer_delay" msecs until thread was stopped.
|
||||
virtual void run() {;}
|
||||
|
||||
//! Function executed once at the end of thread.
|
||||
virtual void end() {;}
|
||||
|
||||
volatile bool terminating, running_, lockRun;
|
||||
int delay_, policy_;
|
||||
void * data_;
|
||||
PIMutex mutex_;
|
||||
PITimeMeasurer tmf_, tms_, tmr_;
|
||||
PIThread::Priority priority_;
|
||||
ThreadFunc ret_func;
|
||||
#ifndef WINDOWS
|
||||
pthread_t thread;
|
||||
sched_param sparam;
|
||||
#else
|
||||
void * thread;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // PITHREAD_H
|
||||
428
src/thread/pitimer.cpp
Executable file
428
src/thread/pitimer.cpp
Executable file
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Timer
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pitimer.h"
|
||||
|
||||
|
||||
/*! \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
|
||||
*/
|
||||
|
||||
|
||||
_PITimerBase::_PITimerBase() {
|
||||
interval_ = deferred_delay = 0.;
|
||||
running_ = deferred_ = deferred_mode = false;
|
||||
}
|
||||
|
||||
|
||||
void _PITimerBase::setInterval(double i) {
|
||||
interval_ = i;
|
||||
if (isRunning()) {
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerBase::start(double interval_ms) {
|
||||
if (isRunning()) stop();
|
||||
deferred_ = false;
|
||||
setInterval(interval_ms);
|
||||
running_ = startTimer(interval_ms);
|
||||
return running_;
|
||||
}
|
||||
|
||||
|
||||
void _PITimerBase::startDeferred(double interval_ms, PIDateTime start_datetime) {
|
||||
if (isRunning()) stop();
|
||||
deferred_ = true;
|
||||
deferred_mode = true;
|
||||
deferred_datetime = start_datetime;
|
||||
setInterval(interval_ms);
|
||||
running_ = startTimer(interval_ms);
|
||||
}
|
||||
|
||||
|
||||
void _PITimerBase::startDeferred(double interval_ms, double delay_ms) {
|
||||
if (isRunning()) stop();
|
||||
deferred_ = true;
|
||||
deferred_mode = false;
|
||||
deferred_delay = delay_ms;
|
||||
setInterval(interval_ms);
|
||||
running_ = startTimer(interval_ms);
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerBase::stop() {
|
||||
if (!isRunning()) return true;
|
||||
running_ = !stopTimer();
|
||||
return !running_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
_PITimerImp_Thread::_PITimerImp_Thread() {
|
||||
thread_.setName("__S__PITimerImp_Thread::thread");
|
||||
wait_dt = 100;
|
||||
wait_dd = 200;
|
||||
wait_tick = 10;
|
||||
//piCout << "new _PITimerImp_Thread";
|
||||
}
|
||||
|
||||
|
||||
_PITimerImp_Thread::~_PITimerImp_Thread() {
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void _PITimerImp_Thread::prepareStart(double interval_ms) {
|
||||
st_inc = PISystemTime::fromMilliseconds(interval_ms);
|
||||
st_odt = st_inc * 5;
|
||||
if (st_odt.toSeconds() < 1.) st_odt = PISystemTime::fromSeconds(1.);
|
||||
if (deferred_) {
|
||||
if (!deferred_mode)
|
||||
st_time = PISystemTime::current(true) + PISystemTime::fromMilliseconds(deferred_delay);
|
||||
st_time -= st_inc;
|
||||
} else
|
||||
st_time = PISystemTime::current(true) + st_inc;
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerImp_Thread::startTimer(double interval_ms) {
|
||||
prepareStart(interval_ms);
|
||||
thread_.setData(this);
|
||||
return thread_.start(threadFuncS);
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerImp_Thread::stopTimer() {
|
||||
thread_.stop();
|
||||
if (!thread_.waitForFinish(10))
|
||||
if (thread_.isRunning())
|
||||
thread_.terminate();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerImp_Thread::threadFunc() {
|
||||
if (!running_) return false;
|
||||
if (deferred_) {
|
||||
PISystemTime dwt;
|
||||
int wth(wait_dt);
|
||||
if (deferred_mode) {
|
||||
dwt = deferred_datetime.toSystemTime() - PISystemTime::current();
|
||||
wth = wait_dd;
|
||||
} else
|
||||
dwt = st_time - PISystemTime::current(true);
|
||||
if (wth > 0) {
|
||||
if (dwt.toMilliseconds() > wth + 1.) {
|
||||
piMSleep(wth);
|
||||
return false;
|
||||
} else {
|
||||
dwt.sleep();
|
||||
deferred_ = false;
|
||||
st_time = PISystemTime::current(true);
|
||||
}
|
||||
} else {
|
||||
if (dwt.toMilliseconds() > 0.1)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
st_wait = st_time - PISystemTime::current(true);
|
||||
//piCout << "wait" << this << st_wait;
|
||||
if (st_wait.abs() > st_odt || st_wait.seconds <= -5) {
|
||||
adjustTimes();
|
||||
return true;
|
||||
}
|
||||
if (wait_tick > 0) {
|
||||
if (st_wait.toMilliseconds() > wait_tick + 1.) {
|
||||
piMSleep(wait_tick);
|
||||
return false;
|
||||
} else {
|
||||
st_wait.sleep();
|
||||
}
|
||||
} else {
|
||||
if (st_wait.toMilliseconds() > 0.1)
|
||||
return false;
|
||||
}
|
||||
st_time += st_inc;
|
||||
tfunc(parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void _PITimerImp_Thread::adjustTimes() {
|
||||
PISystemTime cst = PISystemTime::current(true);
|
||||
if (st_time < cst) {
|
||||
while (st_time < cst)
|
||||
st_time += st_inc;
|
||||
} else {
|
||||
cst += st_inc;
|
||||
while (st_time > cst)
|
||||
st_time -= st_inc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef PIP_TIMER_RT
|
||||
|
||||
_PITimerImp_RT::_PITimerImp_RT() {
|
||||
//piCout << "new _PITimerImp_RT";
|
||||
ti = -1;
|
||||
tt = 0;
|
||||
memset(&se, 0, sizeof(se));
|
||||
se.sigev_notify = SIGEV_THREAD;
|
||||
se.sigev_value.sival_ptr = this;
|
||||
se.sigev_notify_function = threadFuncS;
|
||||
se.sigev_notify_attributes = 0;
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerImp_RT::startTimer(double interval_ms) {
|
||||
int flags(0);
|
||||
spec.it_interval.tv_nsec = ((int)(interval_ms * 1000) % 1000000) * 1000;
|
||||
spec.it_interval.tv_sec = (time_t)(interval_ms / 1000);
|
||||
if (deferred_) {
|
||||
if (deferred_mode) {
|
||||
PISystemTime dtm = deferred_datetime.toSystemTime();
|
||||
spec.it_value.tv_nsec = dtm.nanoseconds;
|
||||
spec.it_value.tv_sec = dtm.seconds;
|
||||
flags = TIMER_ABSTIME;
|
||||
} else {
|
||||
spec.it_value.tv_nsec = ((int)(deferred_delay * 1000) % 1000000) * 1000;
|
||||
spec.it_value.tv_sec = (time_t)(deferred_delay / 1000);
|
||||
}
|
||||
} else {
|
||||
spec.it_value = spec.it_interval;
|
||||
}
|
||||
ti = timer_create(CLOCK_REALTIME, &se, &tt);
|
||||
//cout << "***create timer " << msecs << " msecs\n";
|
||||
if (ti == -1) {
|
||||
piCout << "Can`t create RT timer for " << interval_ms << " msecs: " << errorString();
|
||||
return false;
|
||||
}
|
||||
timer_settime(tt, flags, &spec, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerImp_RT::stopTimer() {
|
||||
if (ti < 0) return true;
|
||||
timer_delete(tt);
|
||||
ti = -1;
|
||||
tt = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
_PITimerImp_Pool::_PITimerImp_Pool(): _PITimerImp_Thread() {
|
||||
wait_dt = wait_dd = wait_tick = 0;
|
||||
//piCout << "new _PITimerImp_Pool";
|
||||
}
|
||||
|
||||
|
||||
_PITimerImp_Pool::Pool::Pool(): PIThread() {
|
||||
setName("__S__PITimerImp_Pool::Pool");
|
||||
needLockRun(true);
|
||||
timers.reserve(64);
|
||||
start(2);
|
||||
}
|
||||
|
||||
|
||||
_PITimerImp_Pool::Pool::~Pool() {
|
||||
stop();
|
||||
if (!waitForFinish(100))
|
||||
terminate();
|
||||
timers.clear();
|
||||
}
|
||||
|
||||
|
||||
_PITimerImp_Pool::Pool * _PITimerImp_Pool::Pool::instance() {
|
||||
static Pool pool;
|
||||
return &pool;
|
||||
}
|
||||
|
||||
|
||||
void _PITimerImp_Pool::Pool::add(_PITimerImp_Pool * t) {
|
||||
//piCout << "add ...";
|
||||
lock();
|
||||
if (!timers.contains(t))
|
||||
timers << t;
|
||||
unlock();
|
||||
//piCout << "add done";
|
||||
}
|
||||
|
||||
|
||||
void _PITimerImp_Pool::Pool::remove(_PITimerImp_Pool * t) {
|
||||
//piCout << "remove ...";
|
||||
lock();
|
||||
timers.removeOne(t);
|
||||
unlock();
|
||||
//piCout << "remove done";
|
||||
}
|
||||
|
||||
|
||||
void _PITimerImp_Pool::Pool::run() {
|
||||
piForeach (_PITimerImp_Pool * t, timers)
|
||||
t->threadFunc();
|
||||
//while (t->threadFunc());
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerImp_Pool::startTimer(double interval_ms) {
|
||||
prepareStart(interval_ms);
|
||||
Pool::instance()->add(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool _PITimerImp_Pool::stopTimer() {
|
||||
Pool::instance()->remove(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
PITimer::PITimer(): PIObject() {
|
||||
piMonitor.timers++;
|
||||
imp_mode = PITimer::Thread;
|
||||
initFirst();
|
||||
}
|
||||
|
||||
|
||||
PITimer::PITimer(PITimer::TimerImplementation ti): PIObject() {
|
||||
piMonitor.timers++;
|
||||
imp_mode = ti;
|
||||
initFirst();
|
||||
}
|
||||
|
||||
|
||||
PITimer::PITimer(TimerEvent slot, void * data, PITimer::TimerImplementation ti): PIObject() {
|
||||
piMonitor.timers++;
|
||||
imp_mode = ti;
|
||||
initFirst();
|
||||
data_t = data;
|
||||
ret_func = slot;
|
||||
}
|
||||
|
||||
|
||||
PITimer::~PITimer() {
|
||||
piMonitor.timers--;
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
void PITimer::initFirst() {
|
||||
setProperty("interval", 0.);
|
||||
lockRun = false;
|
||||
data_t = 0;
|
||||
ret_func = 0;
|
||||
imp = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
void PITimer::init() {
|
||||
destroy();
|
||||
switch (imp_mode) {
|
||||
case PITimer::Pool: imp = new _PITimerImp_Pool(); break;
|
||||
case PITimer::ThreadRT:
|
||||
#ifdef PIP_TIMER_RT
|
||||
imp = new _PITimerImp_RT();
|
||||
break;
|
||||
#else
|
||||
piCoutObj << "Warning: \"ThreadRT\" is not available at this system! Using \"Thread\".";
|
||||
#endif
|
||||
case PITimer::Thread: imp = new _PITimerImp_Thread(); break;
|
||||
default: piCout << "Fatal: invalid implementation() of" << this << "!"; abort();
|
||||
}
|
||||
if (imp == 0) return;
|
||||
imp->tfunc = tickImpS;
|
||||
imp->parent = this;
|
||||
}
|
||||
|
||||
|
||||
void PITimer::destroy() {
|
||||
//piCout << "destroy" << this << imp;
|
||||
if (imp == 0) return;
|
||||
delete imp;
|
||||
imp = 0;
|
||||
}
|
||||
|
||||
|
||||
void PITimer::tickImp() {
|
||||
if (!isRunning()) return;
|
||||
if (lockRun) lock();
|
||||
if (ret_func != 0) ret_func(data_t, 1);
|
||||
tick(data_t, 1);
|
||||
tickEvent(data_t, 1);
|
||||
piForeach (Delimiter & i, delims) {
|
||||
if (i.delim > ++(i.tick)) continue;
|
||||
i.tick = 0;
|
||||
if (i.slot != 0) i.slot(data_t, i.delim);
|
||||
else if (ret_func != 0) ret_func(data_t, i.delim);
|
||||
tick(data_t, i.delim);
|
||||
tickEvent(data_t, i.delim);
|
||||
}
|
||||
if (lockRun) unlock();
|
||||
}
|
||||
|
||||
|
||||
bool PITimer::waitForFinish(int timeout_msecs) {
|
||||
if (timeout_msecs < 0) {
|
||||
while (isRunning())
|
||||
msleep(1);
|
||||
return true;
|
||||
}
|
||||
PITimeMeasurer tm;
|
||||
while (isRunning() && tm.elapsed_m() < timeout_msecs)
|
||||
msleep(1);
|
||||
return tm.elapsed_m() < timeout_msecs;
|
||||
}
|
||||
309
src/thread/pitimer.h
Executable file
309
src/thread/pitimer.h
Executable file
@@ -0,0 +1,309 @@
|
||||
/*! \file pitimer.h
|
||||
* \brief Timer
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Timer
|
||||
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PITIMER_H
|
||||
#define PITIMER_H
|
||||
|
||||
#include "pithread.h"
|
||||
#include "pitime.h"
|
||||
|
||||
|
||||
typedef void (*TimerEvent)(void * , int );
|
||||
|
||||
class PITimer;
|
||||
|
||||
class _PITimerBase {
|
||||
friend class PITimer;
|
||||
public:
|
||||
_PITimerBase();
|
||||
virtual ~_PITimerBase() {stop();}
|
||||
|
||||
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_;}
|
||||
|
||||
bool start() {return start(interval_);}
|
||||
bool start(double interval_ms);
|
||||
void startDeferred(double delay_ms) {startDeferred(interval_, delay_ms);}
|
||||
void startDeferred(double interval_ms, double delay_ms);
|
||||
void startDeferred(PIDateTime start_datetime) {startDeferred(interval_, start_datetime);}
|
||||
void startDeferred(double interval_ms, PIDateTime start_datetime);
|
||||
|
||||
bool stop();
|
||||
|
||||
protected:
|
||||
typedef void(*TickFunc)(PITimer*);
|
||||
|
||||
virtual bool startTimer(double interval_ms) = 0;
|
||||
virtual bool stopTimer() = 0;
|
||||
|
||||
double interval_, deferred_delay;
|
||||
bool deferred_, deferred_mode; // mode: true - date, false - delay
|
||||
volatile bool running_;
|
||||
PIDateTime deferred_datetime;
|
||||
PITimer * parent;
|
||||
TickFunc tfunc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class _PITimerImp_Thread: public _PITimerBase {
|
||||
public:
|
||||
_PITimerImp_Thread();
|
||||
virtual ~_PITimerImp_Thread();
|
||||
protected:
|
||||
void prepareStart(double interval_ms);
|
||||
bool threadFunc(); // returns true if repeat is needed
|
||||
int wait_dt, wait_dd, wait_tick;
|
||||
private:
|
||||
virtual bool startTimer(double interval_ms);
|
||||
virtual bool stopTimer();
|
||||
static void threadFuncS(void * d) {((_PITimerImp_Thread*)d)->threadFunc();}
|
||||
void adjustTimes();
|
||||
|
||||
PIThread thread_;
|
||||
PISystemTime st_time, st_inc, st_wait, st_odt;
|
||||
};
|
||||
|
||||
|
||||
#ifdef PIP_TIMER_RT
|
||||
class _PITimerImp_RT: public _PITimerBase {
|
||||
public:
|
||||
_PITimerImp_RT();
|
||||
virtual ~_PITimerImp_RT() {stop();}
|
||||
protected:
|
||||
private:
|
||||
virtual bool startTimer(double interval_ms);
|
||||
virtual bool stopTimer();
|
||||
static void threadFuncS(sigval sv) {((_PITimerImp_RT * )sv.sival_ptr)->tfunc(((_PITimerImp_RT * )sv.sival_ptr)->parent);}
|
||||
|
||||
int ti;
|
||||
itimerspec spec;
|
||||
timer_t tt;
|
||||
sigevent se;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
class _PITimerImp_Pool: public _PITimerImp_Thread {
|
||||
public:
|
||||
_PITimerImp_Pool();
|
||||
virtual ~_PITimerImp_Pool() {stop();}
|
||||
private:
|
||||
class Pool: public PIThread {
|
||||
public:
|
||||
Pool();
|
||||
~Pool();
|
||||
static Pool * instance();
|
||||
void add(_PITimerImp_Pool * t);
|
||||
void remove(_PITimerImp_Pool * t);
|
||||
void run();
|
||||
PIVector<_PITimerImp_Pool * > timers;
|
||||
};
|
||||
virtual bool startTimer(double interval_ms);
|
||||
virtual bool stopTimer();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class PITimer: public PIObject {
|
||||
PIOBJECT(PITimer)
|
||||
public:
|
||||
|
||||
//! \brief Constructs timer with PITimer::Thread implementation
|
||||
explicit PITimer();
|
||||
|
||||
//! \brief Timer implementations
|
||||
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,
|
||||
// SignalRT /*! */ = 0x03,
|
||||
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
|
||||
};
|
||||
|
||||
//! \brief Constructs timer with "ti" implementation
|
||||
explicit PITimer(TimerImplementation ti);
|
||||
|
||||
//! \brief Constructs timer with "slot" slot, "data" data and "ti" implementation
|
||||
explicit PITimer(TimerEvent slot, void * data = 0, TimerImplementation ti = Thread);
|
||||
|
||||
virtual ~PITimer();
|
||||
|
||||
|
||||
//! \brief Returns timer implementation
|
||||
PITimer::TimerImplementation implementation() const {return imp_mode;}
|
||||
|
||||
//! \brief Returns timer loop delay in milliseconds
|
||||
double interval() const {return imp->interval_;}
|
||||
|
||||
//! \brief Set timer loop delay in milliseconds
|
||||
EVENT_HANDLER1(void, setInterval, double, ms) {setProperty("interval", ms); imp->setInterval(ms);}
|
||||
|
||||
//! \brief Returns if timer is started
|
||||
bool isRunning() const {return imp->running_;}
|
||||
|
||||
//! \brief Returns if timer is not started
|
||||
bool isStopped() const {return !imp->running_;}
|
||||
|
||||
EVENT_HANDLER0(bool, start) {return imp->start();}
|
||||
EVENT_HANDLER1(bool, start, int, interval_ms_i) {setInterval(double(interval_ms_i)); return imp->start(double(interval_ms_i));}
|
||||
EVENT_HANDLER1(bool, start, double, interval_ms_d) {setInterval(interval_ms_d); return imp->start(interval_ms_d);}
|
||||
EVENT_HANDLER0(bool, restart) {imp->stop(); return imp->start();}
|
||||
EVENT_HANDLER1(void, startDeferred, double, delay_ms) {imp->startDeferred(delay_ms);}
|
||||
EVENT_HANDLER2(void, startDeferred, double, interval_ms, double, delay_ms) {imp->startDeferred(interval_ms, delay_ms);}
|
||||
EVENT_HANDLER1(void, startDeferred, PIDateTime, start_datetime) {startDeferred(imp->interval_, start_datetime);}
|
||||
EVENT_HANDLER2(void, startDeferred, double, interval_ms, PIDateTime, start_datetime) {imp->startDeferred(interval_ms, start_datetime);}
|
||||
|
||||
EVENT_HANDLER0(bool, stop) {return imp->stop();}
|
||||
bool waitForFinish() {return waitForFinish(-1);}
|
||||
bool waitForFinish(int timeout_msecs);
|
||||
|
||||
//! \brief Set custom data
|
||||
void setData(void * data_) {data_t = data_;}
|
||||
|
||||
//! \brief Set timer tick function
|
||||
void setSlot(TimerEvent slot) {ret_func = slot;}
|
||||
|
||||
//! \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 Add frequency delimiter \b delim with optional delimiter slot \b slot.
|
||||
void addDelimiter(int delim, TimerEvent slot = 0) {delims << Delimiter(slot, delim);}
|
||||
|
||||
//! \brief Remove all frequency delimiters \b delim.
|
||||
void removeDelimiter(int delim) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].delim == delim) {delims.remove(i); i--;}}
|
||||
|
||||
//! \brief Remove all frequency delimiters with slot \b slot.
|
||||
void removeDelimiter(TimerEvent slot) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].slot == slot) {delims.remove(i); i--;}}
|
||||
|
||||
//! \brief Remove all frequency delimiters \b delim with slot \b slot.
|
||||
void removeDelimiter(int delim, TimerEvent slot) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].slot == slot && delims[i].delim == delim) {delims.remove(i); i--;}}
|
||||
|
||||
void setDelimiterValue(int delim, int value) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].delim == delim) delims[i].tick = value;}
|
||||
void setDelimiterValue(TimerEvent slot, int value) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].slot == slot) delims[i].tick = value;}
|
||||
void setDelimiterValue(int delim, TimerEvent slot, int value) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].slot == slot && delims[i].delim == delim) delims[i].tick = value;}
|
||||
int delimiterValue(int delim) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].delim == delim) return delims[i].tick; return -1;}
|
||||
int delimiterValue(int delim, TimerEvent slot) {for (int i = 0; i < delims.size_s(); ++i) if (delims[i].slot == slot && delims[i].delim == delim) return delims[i].tick; return -1;}
|
||||
EVENT_HANDLER0(void, clearDelimiters) {delims.clear();}
|
||||
|
||||
EVENT2(tickEvent, void * , data_, int, delimiter)
|
||||
|
||||
//! \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(int msecs)
|
||||
* \brief Start timer with \b msecs 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 bool restart()
|
||||
//! \brief Stop and start timer with \a interval() loop delay
|
||||
|
||||
//! \fn bool stop()
|
||||
//! \brief Stop timer
|
||||
|
||||
/** \fn void deferredStart(double delay_msecs)
|
||||
* \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. */
|
||||
|
||||
/** \fn void deferredStart(const PIDateTime & start_datetime)
|
||||
* \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. */
|
||||
|
||||
/** \fn void deferredStart(double interval_msecs, double delay_msecs)
|
||||
* \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. */
|
||||
|
||||
/** \fn void deferredStart(double interval_msecs, const 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. */
|
||||
|
||||
//! \fn void clearDelimiters()
|
||||
//! \brief Remove all frequency delimiters
|
||||
|
||||
//! \}
|
||||
//! \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 if frequency delimiter, 1 for main loop. */
|
||||
|
||||
|
||||
//! \}
|
||||
|
||||
protected:
|
||||
struct Delimiter {
|
||||
Delimiter(TimerEvent slot_ = 0, int delim_ = 1) {slot = slot_; delim = delim_; tick = 0;}
|
||||
TimerEvent slot;
|
||||
int delim;
|
||||
int tick;
|
||||
};
|
||||
|
||||
void initFirst();
|
||||
void init();
|
||||
void destroy();
|
||||
|
||||
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.
|
||||
virtual void tick(void * data_, int delimiter) {}
|
||||
|
||||
void * data_t;
|
||||
volatile bool lockRun;
|
||||
PIMutex mutex_;
|
||||
TimerEvent ret_func;
|
||||
TimerImplementation imp_mode;
|
||||
PIVector<Delimiter> delims;
|
||||
|
||||
_PITimerBase * imp;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // PITIMER_H
|
||||
Reference in New Issue
Block a user