git-svn-id: svn://db.shs.com.ru/pip@400 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
This commit is contained in:
196
src_main/thread/pigrabberbase.h
Normal file
196
src_main/thread/pigrabberbase.h
Normal 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
132
src_main/thread/pimutex.cpp
Executable 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
78
src_main/thread/pimutex.h
Executable 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
|
||||
167
src_main/thread/pipipelinethread.h
Normal file
167
src_main/thread/pipipelinethread.h
Normal 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
413
src_main/thread/pithread.cpp
Executable 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
210
src_main/thread/pithread.h
Executable 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
|
||||
29
src_main/thread/pithreadmodule.h
Normal file
29
src_main/thread/pithreadmodule.h
Normal 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
541
src_main/thread/pitimer.cpp
Executable 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
253
src_main/thread/pitimer.h
Executable 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
|
||||
Reference in New Issue
Block a user