Files
pip/src/thread/pitimer.cpp
Пелипенко Иван e50bedc5ef PIDeque bugs fixed
git-svn-id: svn://db.shs.com.ru/pip@27 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
2015-03-16 12:17:14 +00:00

456 lines
10 KiB
C++
Executable File

/*
PIP - Platform Independent Primitives
Timer
Copyright (C) 2015 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"
#ifdef PIP_TIMER_RT
# include <csignal>
#endif
/*! \class PITimer
* \brief Timer
*
* \section PITimer_sec0 Synopsis
* This class implements timer function. PIP timers supports 3 way to tick notify,
* frequency delimiters and time measurements.
* \section PITimer_sec1 Notify variants
* Notify variants:
* * "slot" - static function with format void func(void * data, int delimiter);
* * event - \a tickEvent();
* * virtual function - \a tick().
*
* All these variants are equivalent, use most applicable.
* \section PITimer_sec2 Frequency delimiters
* Frequency delimiter is an integer number and "slot" function. If "slot" function is null
* timer main "slot" will be used. Each delimiter numbers tick timer will be execute
* delimiters or timer main "slot" function with \b delimiter value = delimiter number.
* Example: \snippet pitimer.cpp delimiter
* \section PITimer_sec3 Time measurements
* PITimer can be used as time measurer. Function \a reset() set time mark to current
* system time, then functions double elapsed_*() returns time elapsed from this mark.
* These functions can returns nano-, micro-, milli- and seconds with suffixes "n", "u", "m"
* and "s"
* Example: \snippet pitimer.cpp elapsed
*/
_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
void threadFuncS(sigval sv) {((_PITimerImp_RT * )sv.sival_ptr)->tfunc(((_PITimerImp_RT * )sv.sival_ptr)->parent);}
struct _PITimerImp_RT_Private_ {
itimerspec spec;
timer_t tt;
sigevent se;
};
_PITimerImp_RT::_PITimerImp_RT() {
//piCout << "new _PITimerImp_RT";
priv = new _PITimerImp_RT_Private_();
priv->tt = 0;
ti = -1;
memset(&(priv->se), 0, sizeof(priv->se));
priv->se.sigev_notify = SIGEV_THREAD;
priv->se.sigev_value.sival_ptr = this;
priv->se.sigev_notify_function = threadFuncS;
priv->se.sigev_notify_attributes = 0;
}
_PITimerImp_RT::~_PITimerImp_RT() {
stop();
delete priv;
}
bool _PITimerImp_RT::startTimer(double interval_ms) {
int flags(0);
priv->spec.it_interval.tv_nsec = ((int)(interval_ms * 1000) % 1000000) * 1000;
priv->spec.it_interval.tv_sec = (time_t)(interval_ms / 1000);
if (deferred_) {
if (deferred_mode) {
PISystemTime dtm = deferred_datetime.toSystemTime();
priv->spec.it_value.tv_nsec = dtm.nanoseconds;
priv->spec.it_value.tv_sec = dtm.seconds;
flags = TIMER_ABSTIME;
} else {
priv->spec.it_value.tv_nsec = ((int)(deferred_delay * 1000) % 1000000) * 1000;
priv->spec.it_value.tv_sec = (time_t)(deferred_delay / 1000);
}
} else {
priv->spec.it_value = priv->spec.it_interval;
}
ti = timer_create(CLOCK_REALTIME, &(priv->se), &(priv->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(priv->tt, flags, &(priv->spec), 0);
return true;
}
bool _PITimerImp_RT::stopTimer() {
if (ti < 0) return true;
timer_delete(priv->tt);
ti = -1;
priv->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;
data_t = data;
ret_func = slot;
initFirst();
}
PITimer::PITimer(const PITimer & other): PIObject() {
piMonitor.timers++;
imp_mode = other.imp_mode;
data_t = other.data_t;
ret_func = other.ret_func;
initFirst();
}
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;
}