/*
PIP - Platform Independent Primitives
Timer
Copyright (C) 2013 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 .
*/
#include "pitimer.h"
#include "pisystemtests.h"
#ifdef PIP_TIMER_RT
PITimer::TimerPool * pool = 0;
#endif
PITimer::PITimer(TimerEvent slot, void * data_, bool threaded_)
#ifndef PIP_TIMER_RT
: PIThread() {
#else
: PIObject() {
#endif
ret_func = slot;
data = data_;
#ifdef PIP_TIMER_RT
piMonitor.timers++;
ti = -1;
threaded = threaded_;
running = false;
memset(&se, 0, sizeof(se));
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = this;
se.sigev_notify_function = PITimer::timer_event;
se.sigev_notify_attributes = 0;
lockRun = false;
#endif
reset();
}
PITimer::PITimer(bool threaded_)
#ifndef PIP_TIMER_RT
: PIThread() {
#else
: PIObject() {
#endif
ret_func = 0;
data = 0;
#ifdef PIP_TIMER_RT
piMonitor.timers++;
ti = -1;
threaded = threaded_;
running = false;
memset(&se, 0, sizeof(se));
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = this;
se.sigev_notify_function = PITimer::timer_event;
se.sigev_notify_attributes = 0;
lockRun = false;
#endif
reset();
}
PITimer::~PITimer() {
#ifdef PIP_TIMER_RT
piMonitor.timers--;
#endif
stop();
}
#ifdef PIP_TIMER_RT
void PITimer::start(double msecs) {
if (ti != -1 || msecs < 0 || running) return;
if (!threaded) {
ticks = int(msecs);
if (pool == 0) pool = new TimerPool();
pool->add(this);
//cout << "not threaded timer start " << msecs << " msecs\n";
if (!pool->isRunning()) pool->start();
running = true;
return;
}
spec.it_interval.tv_nsec = ((int)(msecs * 1000) % 1000000) * 1000;
spec.it_interval.tv_sec = (time_t)(msecs / 1000);
spec.it_value = spec.it_interval;
ti = timer_create(CLOCK_REALTIME, &se, &timer);
//cout << "***create timer " << msecs << " msecs\n";
if (ti == -1) {
piCout << "[PITimer] Can`t create timer for " << msecs << " msecs: " << errorString() << endl;
return;
}
timer_settime(timer, 0, &spec, 0);
running = true;
}
void PITimer::deferredStart(double interval_msecs, double delay_msecs) {
if (ti != -1 || interval_msecs < 0 || running) return;
spec.it_interval.tv_nsec = ((int)(interval_msecs * 1000) % 1000000) * 1000;
spec.it_interval.tv_sec = (time_t)(interval_msecs / 1000);
spec.it_value.tv_nsec = ((int)(delay_msecs * 1000) % 1000000) * 1000;
spec.it_value.tv_sec = (time_t)(delay_msecs / 1000);
ti = timer_create(CLOCK_REALTIME, &se, &timer);
//cout << "***create timer\n";
if (ti == -1) {
piCout << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString() << endl;
return;
}
timer_settime(timer, 0, &spec, 0);
running = true;
}
void PITimer::deferredStart(double interval_msecs, const PIDateTime & start_datetime) {
if (ti != -1 || interval_msecs < 0 || running) return;
spec.it_interval.tv_nsec = ((int)(interval_msecs * 1000) % 1000000) * 1000;
spec.it_interval.tv_sec = (time_t)(interval_msecs / 1000);
struct tm dtm;
memset(&dtm, 0, sizeof(dtm));
dtm.tm_sec = start_datetime.seconds;
dtm.tm_min = start_datetime.minutes;
dtm.tm_hour = start_datetime.hours;
dtm.tm_mday = start_datetime.day;
dtm.tm_mon = start_datetime.month - 1;
dtm.tm_year = start_datetime.year - 1900;
spec.it_value.tv_nsec = 0;
spec.it_value.tv_sec = mktime(&dtm);
ti = timer_create(CLOCK_REALTIME, &se, &timer);
//cout << "***create timer\n";
if (ti == -1) {
piCout << "[PITimer] Can`t create timer for " << interval_msecs << " msecs: " << errorString() << endl;
return;
}
timer_settime(timer, TIMER_ABSTIME, &spec, 0);
running = true;
}
void PITimer::TimerPool::remove(PITimer * t) {
mutex.lock();
for (int i = 0; i < timers.size_s(); ++i)
if (timers[i].first == t) {
timers.remove(i);
mutex.unlock();
return;
}
mutex.unlock();
}
void PITimer::TimerPool::begin() {
//cout << "pool begin\n";
/*struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = empty_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, 0) == -1) {
piCout << "[PITimer] sigaction error: " << errorString() << endl;
stop();
return;
}*/
sigemptyset(&ss);
sigaddset(&ss, SIGALRM);
memset(&se, 0, sizeof(se));
se.sigev_notify = SIGEV_SIGNAL;
se.sigev_signo = SIGALRM;
spec.it_interval.tv_nsec = 1000000;
spec.it_interval.tv_sec = 0;
spec.it_value = spec.it_interval;
//cout << "***create pool timer\n";
if (timer_create(CLOCK_REALTIME, &se, &timer) == -1) {
piCout << "[PITimer] Can`t create timer for pool: " << errorString() << endl;
stop();
return;
}
if (timer_settime(timer, 0, &spec, 0) == -1) {
piCout << "[PITimer] Can`t set timer for pool: " << errorString() << endl;
stop();
return;
}
ti = 1;
}
void PITimer::TimerPool::run() {
//cout << "wait ...\n";
sigwait(&ss, &si);
//cout << "ok\n";
mutex.lock();
//cout << "* pool tick , pool = " << this <<", timers = " << timers.size()<<"\n";
for (int i = 0; i < timers.size_s(); ++i) {
TimerPair & ct(timers[i]);
sv.sival_ptr = ct.first;
ct.second++;
//cout << "** pool tick for " << ct.first << ", cnt " << ct.second << ", " << ct.first->ticks << "\n";
if (ct.second >= ct.first->ticks) {
//cout << "*** timer "<remove(this);
if (pool->isEmpty()) pool->terminate();
}
}
if (ti != -1) timer_delete(timer);
ti = -1;
}
void PITimer::timer_event(sigval e) {
PITimer * ct = (PITimer * )e.sival_ptr;
if (!ct->running) return;
if (ct->lockRun) ct->lock();
if (ct->ret_func != 0) ct->ret_func(ct->data, 1);
ct->timeout(ct->data, 1);
ct->tick(ct->data, 1);
piForeach (TimerSlot & i, ct->ret_funcs) {
if (i.delim > ++(i.tick)) continue;
i.tick = 0;
if (i.slot != 0) i.slot(ct->data, i.delim);
else if (ct->ret_func != 0) ct->ret_func(ct->data, i.delim);
ct->timeout(ct->data, i.delim);
ct->tick(ct->data, i.delim);
}
if (ct->lockRun) ct->unlock();
}
bool PITimer::waitForFinish(int timeout_msecs) {
if (timeout_msecs < 0) {
while (running)
msleep(1);
return true;
}
int cnt = 0;
while (running && cnt < timeout_msecs) {
msleep(1);
++cnt;
}
return cnt < timeout_msecs;
}
#else
void PITimer::start(double msecs) {
if (msecs < 0 || running) return;
inc_time = PISystemTime::fromMilliseconds(msecs);
st_time = currentSystemTime() + inc_time;
PIThread::start();
}
void PITimer::run() {
if (!running) return;
(st_time - currentSystemTime()).sleep();
st_time += inc_time;
if (lockRun) lock();
if (ret_func != 0) ret_func(data, 1);
timeout(data, 1);
tick(data, 1);
piForeach (TimerSlot & i, ret_funcs) {
if (i.delim > ++(i.tick)) continue;
i.tick = 0;
if (i.slot != 0) i.slot(data, i.delim);
else if (ret_func != 0) ret_func(data, i.delim);
timeout(data, i.delim);
tick(data, i.delim);
}
if (lockRun) unlock();
}
#endif
double PITimer::elapsed_n() {
#ifdef WINDOWS
t_cur = GetCurrentTime();
return (t_cur - t_st) * 1000000.;
#else
# ifdef MAC_OS
clock_get_time(__pi_mac_clock, &t_cur);
# else
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec - t_st.tv_sec) * 1.e+9 + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns);
#endif
}
double PITimer::elapsed_u() {
#ifdef WINDOWS
t_cur = GetCurrentTime();
return (t_cur - t_st) * 1000.;
#else
# ifdef MAC_OS
clock_get_time(__pi_mac_clock, &t_cur);
# else
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec - t_st.tv_sec) * 1.e+6 + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns) / 1.e+3;
#endif
}
double PITimer::elapsed_m() {
#ifdef WINDOWS
t_cur = GetCurrentTime();
return (double)(t_cur - t_st);
#else
# ifdef MAC_OS
clock_get_time(__pi_mac_clock, &t_cur);
# else
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec - t_st.tv_sec) * 1.e+3 + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns) / 1.e+6;
#endif
}
double PITimer::elapsed_s() {
#ifdef WINDOWS
t_cur = GetCurrentTime();
return (t_cur - t_st) / 1000.;
#else
# ifdef MAC_OS
clock_get_time(__pi_mac_clock, &t_cur);
# else
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec - t_st.tv_sec) + (t_cur.tv_nsec - t_st.tv_nsec - PISystemTests::time_elapsed_ns) / 1.e+9;
#endif
}
double PITimer::reset_time_n() {
#ifdef WINDOWS
return t_st * 1.e+6;
#else
return t_st.tv_sec * 1.e+9 + t_st.tv_nsec;
#endif
}
double PITimer::reset_time_u() {
#ifdef WINDOWS
return t_st * 1.e+3;
#else
return t_st.tv_sec * 1.e+6 + t_st.tv_nsec / 1.e+3;
#endif
}
double PITimer::reset_time_m() {
#ifdef WINDOWS
return (double)t_st;
#else
return t_st.tv_sec * 1.e+3 + t_st.tv_nsec / 1.e+6;
#endif
}
double PITimer::reset_time_s() {
#ifdef WINDOWS
return t_st / 1000.;
#else
return t_st.tv_sec + t_st.tv_nsec / 1.e+9;
#endif
}
PISystemTime PITimer::reset_time() {
#ifdef WINDOWS
return PISystemTime(t_st / 1000, (t_st % 1000) * 1000000);
#else
return PISystemTime(t_st.tv_sec, t_st.tv_nsec);
#endif
}
double PITimer::elapsed_system_n() {
#ifdef WINDOWS
long t_cur = GetCurrentTime();
return (t_cur * 1000000.);
#else
# ifdef MAC_OS
mach_timespec_t t_cur;
clock_get_time(__pi_mac_clock, &t_cur);
# else
timespec t_cur;
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec * 1.e+9 + t_cur.tv_nsec);
#endif
}
double PITimer::elapsed_system_u() {
#ifdef WINDOWS
long t_cur = GetCurrentTime();
return (t_cur * 1000.);
#else
# ifdef MAC_OS
mach_timespec_t t_cur;
clock_get_time(__pi_mac_clock, &t_cur);
# else
timespec t_cur;
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec * 1.e+6 + (t_cur.tv_nsec / 1.e+3));
#endif
}
double PITimer::elapsed_system_m() {
#ifdef WINDOWS
long t_cur = GetCurrentTime();
return (double)t_cur;
#else
# ifdef MAC_OS
mach_timespec_t t_cur;
clock_get_time(__pi_mac_clock, &t_cur);
# else
timespec t_cur;
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec * 1.e+3 + (t_cur.tv_nsec / 1.e+6));
#endif
}
double PITimer::elapsed_system_s() {
#ifdef WINDOWS
long t_cur = GetCurrentTime();
return (t_cur / 1000.);
#else
# ifdef MAC_OS
mach_timespec_t t_cur;
clock_get_time(__pi_mac_clock, &t_cur);
# else
timespec t_cur;
clock_gettime(0, &t_cur);
# endif
return (t_cur.tv_sec + (t_cur.tv_nsec / 1.e+9));
#endif
}
PITime currentTime() {
time_t rt = time(0);
tm * pt = localtime(&rt);
PITime t;
t.seconds = pt->tm_sec;
t.minutes = pt->tm_min;
t.hours = pt->tm_hour;
return t;
}
PIDate currentDate() {
time_t rt = time(0);
tm * pt = localtime(&rt);
PIDate d;
d.day = pt->tm_mday;
d.month = pt->tm_mon + 1;
d.year = pt->tm_year + 1900;
return d;
}
PIDateTime currentDateTime() {
time_t rt = time(0);
tm * pt = localtime(&rt);
PIDateTime dt;
dt.seconds = pt->tm_sec;
dt.minutes = pt->tm_min;
dt.hours = pt->tm_hour;
dt.day = pt->tm_mday;
dt.month = pt->tm_mon + 1;
dt.year = pt->tm_year + 1900;
return dt;
}
PISystemTime currentSystemTime() {
#ifdef WINDOWS
long t_cur = GetCurrentTime();
return PISystemTime(t_cur / 1000, (t_cur % 1000) * 1000000);
#else
# ifdef MAC_OS
mach_timespec_t t_cur;
clock_get_time(__pi_mac_clock, &t_cur);
# else
timespec t_cur;
clock_gettime(0, &t_cur);
# endif
return PISystemTime(t_cur.tv_sec, t_cur.tv_nsec);
#endif
}
PIString PITime::toString(const PIString & format) {
PIString ts = format;
ts.replace("hh", PIString::fromNumber(hours).expandLeftTo(2, '0'));
ts.replace("h", PIString::fromNumber(hours));
ts.replace("mm", PIString::fromNumber(minutes).expandLeftTo(2, '0'));
ts.replace("m", PIString::fromNumber(minutes));
ts.replace("ss", PIString::fromNumber(seconds).expandLeftTo(2, '0'));
ts.replace("s", PIString::fromNumber(seconds));
return ts;
}
PIString PIDate::toString(const PIString & format) {
PIString ts = format;
ts.replace("yyyy", PIString::fromNumber(year).expandLeftTo(4, '0'));
ts.replace("yy", PIString::fromNumber(year).right(2));
ts.replace("y", PIString::fromNumber(year).right(1));
ts.replace("MM", PIString::fromNumber(month).expandLeftTo(2, '0'));
ts.replace("M", PIString::fromNumber(month));
ts.replace("dd", PIString::fromNumber(day).expandLeftTo(2, '0'));
ts.replace("d", PIString::fromNumber(day));
return ts;
}
PIString PIDateTime::toString(const PIString & format) {
PIString ts = format;
ts.replace("hh", PIString::fromNumber(hours).expandLeftTo(2, '0'));
ts.replace("h", PIString::fromNumber(hours));
ts.replace("mm", PIString::fromNumber(minutes).expandLeftTo(2, '0'));
ts.replace("m", PIString::fromNumber(minutes));
ts.replace("ss", PIString::fromNumber(seconds).expandLeftTo(2, '0'));
ts.replace("s", PIString::fromNumber(seconds));
ts.replace("yyyy", PIString::fromNumber(year).expandLeftTo(4, '0'));
ts.replace("yy", PIString::fromNumber(year).right(2));
ts.replace("y", PIString::fromNumber(year).right(1));
ts.replace("MM", PIString::fromNumber(month).expandLeftTo(2, '0'));
ts.replace("M", PIString::fromNumber(month));
ts.replace("dd", PIString::fromNumber(day).expandLeftTo(2, '0'));
ts.replace("d", PIString::fromNumber(day));
return ts;
}
PIString time2string(const PITime & time, const PIString & format) {
PIString ts = format;
ts.replace("hh", PIString::fromNumber(time.hours).expandLeftTo(2, '0'));
ts.replace("h", PIString::fromNumber(time.hours));
ts.replace("mm", PIString::fromNumber(time.minutes).expandLeftTo(2, '0'));
ts.replace("m", PIString::fromNumber(time.minutes));
ts.replace("ss", PIString::fromNumber(time.seconds).expandLeftTo(2, '0'));
ts.replace("s", PIString::fromNumber(time.seconds));
return ts;
}
PIString date2string(const PIDate & date, const PIString & format) {
PIString ts = format;
ts.replace("yyyy", PIString::fromNumber(date.year).expandLeftTo(4, '0'));
ts.replace("yy", PIString::fromNumber(date.year).right(2));
ts.replace("y", PIString::fromNumber(date.year).right(1));
ts.replace("MM", PIString::fromNumber(date.month).expandLeftTo(2, '0'));
ts.replace("M", PIString::fromNumber(date.month));
ts.replace("dd", PIString::fromNumber(date.day).expandLeftTo(2, '0'));
ts.replace("d", PIString::fromNumber(date.day));
return ts;
}
PIString datetime2string(const PIDateTime & date, const PIString & format) {
PIString ts = format;
ts.replace("hh", PIString::fromNumber(date.hours).expandLeftTo(2, '0'));
ts.replace("h", PIString::fromNumber(date.hours));
ts.replace("mm", PIString::fromNumber(date.minutes).expandLeftTo(2, '0'));
ts.replace("m", PIString::fromNumber(date.minutes));
ts.replace("ss", PIString::fromNumber(date.seconds).expandLeftTo(2, '0'));
ts.replace("s", PIString::fromNumber(date.seconds));
ts.replace("yyyy", PIString::fromNumber(date.year).expandLeftTo(4, '0'));
ts.replace("yy", PIString::fromNumber(date.year).right(2));
ts.replace("y", PIString::fromNumber(date.year).right(1));
ts.replace("MM", PIString::fromNumber(date.month).expandLeftTo(2, '0'));
ts.replace("M", PIString::fromNumber(date.month));
ts.replace("dd", PIString::fromNumber(date.day).expandLeftTo(2, '0'));
ts.replace("d", PIString::fromNumber(date.day));
return ts;
}