git-svn-id: svn://db.shs.com.ru/pip@4 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5

This commit is contained in:
2015-02-28 18:35:47 +00:00
parent 8e451c891d
commit 13336674eb
154 changed files with 44021 additions and 0 deletions

59
src/thread/pimutex.cpp Executable file
View 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
View 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
View 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
View 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
View 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
View 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