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

This commit is contained in:
2017-04-18 20:49:45 +00:00
parent b1b23bf89c
commit 95c1f34b45
192 changed files with 291 additions and 187 deletions

View File

@@ -0,0 +1,196 @@
/*! \file pigrabberbase.h
* \brief Abstract class for create grabbers
*/
/*
PIP - Platform Independent Primitives
Abstract class for create grabbers
Copyright (C) 2017 Andrey Bychkov work.a.b@yandex.ru
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 PIGRABBERBASE_H
#define PIGRABBERBASE_H
#include "pithread.h"
#include "pidiagnostics.h"
template<typename T = PIByteArray>
class PIGrabberBase: public PIThread
{
PIOBJECT_SUBCLASS(PIGrabberBase, PIThread)
public:
PIGrabberBase() {
is_opened = false;
is_recording = false;
}
virtual ~PIGrabberBase() {
stopGrabber(false);
}
virtual bool isOpened() const {return is_opened;}
virtual bool isRecording() const {return is_recording;}
virtual void startRecord(const PIString & filename) {
if (!isOpened()) return;
if (isRecording()) return;
rec_mutex.lock();
startRecordInternal(filename);
is_recording = true;
rec_mutex.unlock();
}
virtual void stopRecord() {
if (!isOpened()) return;
if (!isRecording()) return;
rec_mutex.lock();
is_recording = false;
stopRecordInternal();
rec_mutex.unlock();
}
T last() const {
T ret;
last_mutex.lock();
ret = last_;
last_mutex.unlock();
return ret;
}
bool isEmpty() {
bool ret;
que_mutex.lock();
ret = que.isEmpty();
que_mutex.unlock();
return ret;
}
int queSize() {
int ret;
que_mutex.lock();
ret = que.size();
que_mutex.unlock();
return ret;
}
T dequeue() {
T ret;
// piCoutObj << "start";
que_mutex.lock();
if (!que.isEmpty()) {
// piCoutObj << "dequeue";
ret = que.dequeue();
}
// piCoutObj << "end";
que_mutex.unlock();
return ret;
}
void stopGrabber(bool wait_forever = true) {
if (isRunning()) {
stop();
if (wait_forever) waitForFinish();
else {
if (!waitForFinish(100)) terminate();
stopRecord();
close();
}
}
}
bool open() {
bool ret = openInternal();
if (!is_opened && ret)
opened();
is_opened = ret;
return ret;
}
void close() {
bool em = is_opened;
closeInternal();
last_ = T();
if (em)
closed();
is_opened = false;
}
const PIDiagnostics & diag() const {return diag_;}
void clear() {
que_mutex.lock();
que.clear();
que_mutex.unlock();
}
void restart() {
clear();
close();
}
EVENT(dataReady)
EVENT(opened)
EVENT(closed)
protected:
virtual void init() {}
virtual bool openInternal() = 0;
virtual void closeInternal() = 0;
virtual int get(T & val) = 0;
virtual void record(const T & val) {}
virtual void startRecordInternal(const PIString & filename) {}
virtual void stopRecordInternal() {}
bool is_opened, is_recording;
mutable PIMutex rec_mutex;
private:
void begin() {
init();
}
void run() {
if (!isOpened()) {
open();
diag_.reset();
if (!is_opened)
piMSleep(200);
}
if (isOpened()) {
T c;
int ret = get(c);
if (ret < 0) {
close();
return;
}
if (ret > 0) {
piMSleep(1);
return;
}
diag_.received(1);
que_mutex.lock();
que.enqueue(c);
que_mutex.unlock();
if (isRecording()) {
rec_mutex.lock();
record(c);
rec_mutex.unlock();
diag_.sended(1);
}
last_mutex.lock();
last_ = c;
last_mutex.unlock();
dataReady();
}
}
void end() {
stopRecord();
close();
}
T last_;
PIQueue<T> que;
PIDiagnostics diag_;
mutable PIMutex que_mutex, last_mutex;
};
#endif // PIGRABBERBASE_H

132
src_main/thread/pimutex.cpp Executable file
View File

@@ -0,0 +1,132 @@
/*
PIP - Platform Independent Primitives
Mutex
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
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/>.
*/
/** \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.
*
* */
#include "pimutex.h"
#include "piincludes_p.h"
#ifdef BLACKBERRY
# include <pthread.h>
#endif
PRIVATE_DEFINITION_START(PIMutex)
#ifdef WINDOWS
void *
#else
pthread_mutex_t
#endif
mutex;
PRIVATE_DEFINITION_END(PIMutex)
PIMutex::PIMutex(): inited_(false) {
#ifdef WINDOWS
PRIVATE->mutex = 0;
#endif
init();
}
PIMutex::~PIMutex() {
destroy();
}
void PIMutex::lock() {
#ifdef WINDOWS
WaitForSingleObject(PRIVATE->mutex, INFINITE);
#else
pthread_mutex_lock(&(PRIVATE->mutex));
#endif
locked = true;
}
void PIMutex::unlock() {
#ifdef WINDOWS
ReleaseMutex(PRIVATE->mutex);
#else
pthread_mutex_unlock(&(PRIVATE->mutex));
#endif
locked = false;
}
bool PIMutex::tryLock() {
bool ret =
#ifdef WINDOWS
(WaitForSingleObject(PRIVATE->mutex, 0) == WAIT_OBJECT_0);
#else
(pthread_mutex_trylock(&(PRIVATE->mutex)) == 0);
#endif
locked = true;
return ret;
}
bool PIMutex::isLocked() const {
return locked;
}
void PIMutex::init() {
if (inited_) destroy();
#ifdef WINDOWS
PRIVATE->mutex = CreateMutex(0, false, 0);
#else
pthread_mutexattr_t attr;
memset(&attr, 0, sizeof(attr));
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
memset(&(PRIVATE->mutex), 0, sizeof(PRIVATE->mutex));
pthread_mutex_init(&(PRIVATE->mutex), &attr);
pthread_mutexattr_destroy(&attr);
#endif
locked = false;
inited_ = true;
}
void PIMutex::destroy() {
if (inited_) {
#ifdef WINDOWS
if (PRIVATE->mutex) CloseHandle(PRIVATE->mutex);
PRIVATE->mutex = 0;
#else
pthread_mutex_destroy(&(PRIVATE->mutex));
#endif
}
locked = inited_ = false;
}

78
src_main/thread/pimutex.h Executable file
View File

@@ -0,0 +1,78 @@
/*! \file pimutex.h
* \brief Mutex
*/
/*
PIP - Platform Independent Primitives
Mutex
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
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
explicit PIMutex();
//! Destroy mutex
~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();
//! \brief Unlock mutex
//! \details In any case this function returns immediate
void unlock();
//! \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();
//! Returns if mutex is locked
bool isLocked() const;
private:
explicit PIMutex(const PIMutex & );
void operator =(const PIMutex & );
void init();
void destroy();
PRIVATE_DECLARATION
bool inited_;
volatile bool locked;
};
class PIP_EXPORT PIMutexLocker
{
public:
PIMutexLocker(PIMutex * m, bool condition = true): mutex(m), cond(condition) {if (cond) mutex->lock();}
PIMutexLocker(PIMutex & m, bool condition = true): mutex(&m), cond(condition) {if (cond) mutex->lock();}
~PIMutexLocker() {if (cond) mutex->unlock();}
private:
PIMutex * mutex;
volatile bool cond;
};
#endif // PIMUTEX_H

View File

@@ -0,0 +1,167 @@
/*! \file pipipelinethread.h
* \brief Class for create multihread pipeline
*/
/*
PIP - Platform Independent Primitives
Class for create multihread pipeline
Copyright (C) 2016 Andrey Bychkov work.a.b@yandex.ru
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 PIPIPELINETHREAD_H
#define PIPIPELINETHREAD_H
#include "pithread.h"
#include "piqueue.h"
template <typename Tin, typename Tout>
class PIPipelineThread : public PIThread
{
PIOBJECT_SUBCLASS(PIPipelineThread, PIThread)
public:
PIPipelineThread() {
cnt = 0;
max_size = 0;
wait_next_pipe = false;
next_overload = false;
}
~PIPipelineThread() {
stop();
if (!waitForFinish(1000)) {
piCoutObj << "terminating self thread";
terminate();
}
}
template <typename T>
void connectTo(PIPipelineThread<Tout, T> * next) {
CONNECT2(void, Tout, bool *, this, calculated, next, enqueue)
}
EVENT2(calculated, const Tout &, v, bool *, overload)
void enqueue(const Tin &v) {enqueue(v, 0);}
EVENT_HANDLER2(void, enqueue, const Tin &, v, bool *, overload) {
mutex.lock();
//piCoutObj << "enque" << overload;//max_size << in.size();
if (max_size == 0 || in.size() < max_size) {
in.enqueue(v);
if (overload) *overload = false;
} else {
if (overload) *overload = true;
}
mutex.unlock();
}
const ullong * counterPtr() const {return &cnt;}
ullong counter() const {return cnt;}
bool isEmpty() {
bool ret;
mutex.lock();
ret = in.isEmpty();
mutex.unlock();
return ret;
}
int queSize() {
int ret;
mutex.lock();
ret = in.size();
mutex.unlock();
return ret;
}
void clear() {
mutex.lock();
in.clear();
next_overload = false;
mutex.unlock();
}
void stopCalc() {
if (isRunning()) {
stop();
if (!waitForFinish(100)) terminate();
mutex.unlock();
mutex_l.unlock();
}
}
Tout getLast() {
Tout ret;
mutex_l.lock();
ret = last;
mutex_l.unlock();
return ret;
}
uint maxQueSize() {
uint ret;
mutex.lock();
ret = max_size;
mutex.unlock();
return ret;
}
void setMaxQueSize(uint count) {
mutex.lock();
max_size = count;
if (max_size > 0 && in.size() > max_size) in.resize(max_size);
mutex.unlock();
}
bool isWaitNextPipe() {return wait_next_pipe;}
void setWaitNextPipe(bool wait) {wait_next_pipe = wait;}
protected:
virtual Tout calc(Tin &v, bool &ok) = 0;
uint max_size;
private:
void begin() {cnt = 0;}
void run() {
//piCoutObj << "run ...";
mutex.lock();
if (in.isEmpty()) {
mutex.unlock();
piMSleep(10);
//piCoutObj << "run in empty";
return;
}
if (next_overload && wait_next_pipe) {
mutex.unlock();
//piCoutObj << "wait" << &next_overload;
calculated(last, &next_overload);
piMSleep(10);
} else {
Tin t = in.dequeue();
mutex.unlock();
bool ok = true;
Tout r = calc(t, ok);
if (ok) {
mutex_l.lock();
last = r;
mutex_l.unlock();
cnt++;
// next_overload = true;
//piCoutObj << "calc ok" << &next_overload;
calculated(r, &next_overload);
}
}
//piCoutObj << "run ok";
}
PIMutex mutex;
PIMutex mutex_l;
bool wait_next_pipe;
bool next_overload;
ullong cnt;
PIQueue<Tin> in;
Tout last;
};
#endif // PIPIPELINETHREAD_H

413
src_main/thread/pithread.cpp Executable file
View File

@@ -0,0 +1,413 @@
/*
PIP - Platform Independent Primitives
Thread
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
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 "piincludes_p.h"
#include "pithread.h"
#include "pisystemtests.h"
#include "piintrospection_proxy.h"
#include <signal.h>
#ifdef WINDOWS
extern PINtSetTimerResolution setTimerResolutionAddr;
void __PISetTimerResolution() {if (setTimerResolutionAddr == NULL) return; ULONG ret; setTimerResolutionAddr(1, TRUE, &ret);}
#endif
#if defined(MAC_OS) || defined(BLACKBERRY)
# include <pthread.h>
#endif
/*! \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.
*
*/
PRIVATE_DEFINITION_START(PIThread)
#ifndef WINDOWS
pthread_t thread;
sched_param sparam;
#else
void * thread;
#endif
PRIVATE_DEFINITION_END(PIThread)
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, int timer_delay): PIObject() {
piMonitor.threads++;
tid_ = -1;
PRIVATE->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++;
tid_ = -1;
PRIVATE->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_ || PRIVATE->thread == 0) return;
#ifndef WINDOWS
# ifdef ANDROID
pthread_kill(PRIVATE->thread, SIGTERM);
# else
pthread_cancel(PRIVATE->thread);
# endif
#else
TerminateThread(PRIVATE->thread, 0);
CloseHandle(PRIVATE->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);
# ifndef ANDROID
//pthread_attr_setschedparam(&attr, &(PRIVATE->sparam));
# endif
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int ret = pthread_create(&PRIVATE->thread, &attr, thread_function, this);
//piCout << "pthread_create" << PRIVATE->thread;
pthread_attr_destroy(&attr);
if (ret == 0) {
# ifdef MAC_OS
pthread_threadid_np(PRIVATE->thread, (__uint64_t*)&tid_);
# else
tid_ = PRIVATE->thread;
# endif
#else
if (PRIVATE->thread != 0) CloseHandle(PRIVATE->thread);
PRIVATE->thread = (void *)_beginthreadex(0, 0, thread_function, this, 0, 0);
// PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_function, this, 0, 0);
if (PRIVATE->thread != 0) {
#endif
setPriority(priority_);
running_ = true;
return true;
} else {
PRIVATE->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);
# ifndef ANDROID
//pthread_attr_setschedparam(&attr, &(PRIVATE->sparam));
# endif
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int ret = pthread_create(&(PRIVATE->thread), &attr, thread_function_once, this);
//piCout << "pthread_create" << PRIVATE->thread;
pthread_attr_destroy(&attr);
if (ret == 0) {
# ifdef MAC_OS
pthread_threadid_np(PRIVATE->thread, (__uint64_t*)&tid_);
# else
tid_ = PRIVATE->thread;
# endif
#else
if (PRIVATE->thread != 0) CloseHandle(PRIVATE->thread);
PRIVATE->thread = (void *)_beginthreadex(0, 0, thread_function_once, this, 0, 0);
// thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_function_once, this, 0, 0);
if (PRIVATE->thread != 0) {
#endif
setPriority(priority_);
running_ = true;
return true;
} else {
PRIVATE->thread = 0;
piCoutObj << "Error: Can`t start new thread:" << errorString();
}
return false;
}
void PIThread::terminate() {
if (PRIVATE->thread == 0) return;
PIINTROSPECTION_UNREGISTER_THREAD(tid());
terminating = running_ = false;
tid_ = -1;
//piCout << "terminate" << thread;
#ifndef WINDOWS
# ifdef ANDROID
pthread_kill(PRIVATE->thread, SIGTERM);
# else
//pthread_kill(PRIVATE->thread, SIGKILL);
//void * ret(0);
pthread_cancel(PRIVATE->thread);
//pthread_join(PRIVATE->thread, &ret);
# endif
#else
TerminateThread(PRIVATE->thread, 0);
CloseHandle(PRIVATE->thread);
#endif
PRIVATE->thread = 0;
end();
}
__THREAD_FUNC__ 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);
#ifdef WINDOWS
ct.tid_ = GetCurrentThreadId();
#endif
PIINTROSPECTION_REGISTER_THREAD(ct.tid(), ct.priority(), ct.name());
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();
//piCout << "thread" << ct.name() << "...";
ct.run();
//piCout << "thread" << ct.name() << "done";
//printf("thread %p tick\n", &ct);
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;
ct.tid_ = -1;
//cout << "thread " << t << " exiting ... " << endl;
//piCout << "pthread_exit" << (ct.__privateinitializer__.p)->thread;
PIINTROSPECTION_UNREGISTER_THREAD(ct.tid());
#ifndef WINDOWS
pthread_detach((ct.__privateinitializer__.p)->thread);
(ct.__privateinitializer__.p)->thread = 0;
#endif
#ifndef WINDOWS
pthread_exit(0);
#else
_endthreadex(0);
//ExitThread(0);
#endif
return 0;
}
__THREAD_FUNC__ 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);
#ifdef WINDOWS
ct.tid_ = GetCurrentThreadId();
#endif
PIINTROSPECTION_REGISTER_THREAD(ct.tid(), ct.priority(), ct.name());
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;
ct.tid_ = -1;
//cout << "thread " << t << " exiting ... " << endl;
//piCout << "pthread_exit" << (ct.__privateinitializer__.p)->thread;
PIINTROSPECTION_UNREGISTER_THREAD(ct.tid());
#ifndef WINDOWS
pthread_detach((ct.__privateinitializer__.p)->thread);
(ct.__privateinitializer__.p)->thread = 0;
#endif
#ifndef WINDOWS
pthread_exit(0);
#else
_endthreadex(0);
//ExitThread(0);
#endif
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_ || (PRIVATE->thread == 0)) return;
//piCout << "setPriority" << PRIVATE->thread;
policy_ = 0;
memset(&(PRIVATE->sparam), 0, sizeof(PRIVATE->sparam));
pthread_getschedparam(PRIVATE->thread, &policy_, &(PRIVATE->sparam));
PRIVATE->sparam.
# ifndef LINUX
sched_priority
# else
__sched_priority
# endif
= priority2System(priority_);
pthread_setschedparam(PRIVATE->thread, policy_, &(PRIVATE->sparam));
#else
if (!running_ || (PRIVATE->thread == 0)) return;
SetThreadPriority(PRIVATE->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;
}

210
src_main/thread/pithread.h Executable file
View File

@@ -0,0 +1,210 @@
/*! \file pithread.h
* \brief Thread
*
* This file declare thread class and some wait functions
*/
/*
PIP - Platform Independent Primitives
Thread
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
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 "piinit.h"
#include "pimutex.h"
#include "piobject.h"
#ifdef WINDOWS
# define __THREAD_FUNC__ uint __stdcall
#else
# define __THREAD_FUNC__ void*
#endif
typedef void (*ThreadFunc)(void * );
class PIP_EXPORT PIThread: public PIObject
{
PIOBJECT_SUBCLASS(PIThread, PIObject)
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 Returns internal mutex
PIMutex & mutex() {return mutex_;}
//! \brief Returns thread ID
llong tid() const {return tid_;}
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 __THREAD_FUNC__ thread_function(void * t);
static __THREAD_FUNC__ 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_;
llong tid_;
void * data_;
PIMutex mutex_;
PITimeMeasurer tmf_, tms_, tmr_;
PIThread::Priority priority_;
ThreadFunc ret_func;
PRIVATE_DECLARATION
};
#endif // PITHREAD_H

View File

@@ -0,0 +1,29 @@
/*
PIP - Platform Independent Primitives
Module includes
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
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 PITHREADMODULE_H
#define PITHREADMODULE_H
#include "pimutex.h"
#include "pithread.h"
#include "pitimer.h"
#include "pipipelinethread.h"
#include "pigrabberbase.h"
#endif // PITHREADMODULE_H

541
src_main/thread/pitimer.cpp Executable file
View File

@@ -0,0 +1,541 @@
/*
PIP - Platform Independent Primitives
Timer
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
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;
tfunc = 0;
parent = 0;
}
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_;
}
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
struct _PITimerImp_RT_Private_;
class _PITimerImp_RT: public _PITimerBase {
public:
_PITimerImp_RT();
virtual ~_PITimerImp_RT();
protected:
private:
virtual bool startTimer(double interval_ms);
virtual bool stopTimer();
int ti;
_PITimerImp_RT_Private_ * priv;
};
#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, to_remove;
};
virtual bool startTimer(double interval_ms);
virtual bool stopTimer();
};
_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) {
if (interval_ms <= 0.) {
piCout << "Achtung! Start PITimer with interval <= 0!";
piCout << "Achtung! Parent" << parent;
assert(interval_ms > 0.);
}
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;
if (!parent->isPIObject()) {
piCout << "Achtung! PITimer \"parent\" is not PIObject!";
return false;
}
tfunc(parent);
return true;
}
void _PITimerImp_Thread::adjustTimes() {
PISystemTime cst = PISystemTime::current(true);
if (st_time < cst) {
int rs = (cst - st_time).toSeconds() / st_inc.toSeconds();
if (rs >= 100)
st_time = cst + st_inc;
else {
while (st_time < cst)
st_time += st_inc;
}
} else {
int rs = (st_time - cst).toSeconds() / st_inc.toSeconds();
if (rs >= 100)
st_time = cst - 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();
unlock();
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();
to_remove.removeOne(t);
if (!timers.contains(t))
timers << t;
unlock();
//piCout << "add done";
}
void _PITimerImp_Pool::Pool::remove(_PITimerImp_Pool * t) {
//piCout << "remove ...";
lock();
to_remove << t;
unlock();
//piCout << "remove done";
}
void _PITimerImp_Pool::Pool::run() {
piForeach (_PITimerImp_Pool * t, timers)
t->threadFunc();
if (!to_remove.isEmpty()) {
piForeach (_PITimerImp_Pool * t, to_remove)
timers.removeOne(t);
to_remove.clear();
}
//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(const PITimer & other): PIObject() {
piMonitor.timers++;
imp_mode = other.imp_mode;
initFirst();
data_t = other.data_t;
ret_func = other.ret_func;
}
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 << "!"; assert(0);
}
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;
}

253
src_main/thread/pitimer.h Executable file
View File

@@ -0,0 +1,253 @@
/*! \file pitimer.h
* \brief Timer
*/
/*
PIP - Platform Independent Primitives
Timer
Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru
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();
typedef void(*TickFunc)(PITimer*);
TickFunc tfunc;
PITimer * parent;
protected:
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;
};
class PITimer: public PIObject {
PIOBJECT_SUBCLASS(PITimer, PIObject)
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);
PITimer(const PITimer & other);
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 is 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