/*
PIP - Platform Independent Primitives
Thread
Ivan Pelipenko peri4ko@yandex.ru, 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 Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
#include "pithread.h"
#include "piincludes_p.h"
#include "piintrospection_threads.h"
#include "pitime.h"
#ifndef MICRO_PIP
# include "pisystemtests.h"
#endif
#ifdef WINDOWS
# include
#endif
#include
#if defined(WINDOWS)
# define __THREAD_FUNC_RET__ uint __stdcall
#elif defined(FREERTOS)
# define __THREAD_FUNC_RET__ void
#else
# define __THREAD_FUNC_RET__ void *
#endif
#ifndef FREERTOS
# define __THREAD_FUNC_END__ 0
#else
# define __THREAD_FUNC_END__
#endif
#if defined(LINUX)
# include
# define gettid() syscall(SYS_gettid)
#endif
#if defined(MAC_OS) || defined(BLACKBERRY)
# include
#endif
__THREAD_FUNC_RET__ thread_function(void * t) {
((PIThread *)t)->__thread_func__();
return __THREAD_FUNC_END__;
}
__THREAD_FUNC_RET__ thread_function_once(void * t) {
((PIThread *)t)->__thread_func_once__();
return __THREAD_FUNC_END__;
}
#ifndef MICRO_PIP
# define REGISTER_THREAD(t) __PIThreadCollection::instance()->registerThread(t)
# define UNREGISTER_THREAD(t) __PIThreadCollection::instance()->unregisterThread(t)
#else
# define REGISTER_THREAD(t)
# define UNREGISTER_THREAD(t)
#endif
//! \addtogroup Thread
//! \{
//! \class PIThread pithread.h
//! \~\brief
//! \~english Thread class
//! \~russian Класс потока
//!
//! \~\details
//! \~english
//! This class allow you exec your code in separate thread.
//!
//! \~russian
//! Этот класс позволяет выполнять код из параллельного потока.
//!
//!
//! \~english \section PIThread_sec0 Synopsis
//! \~russian \section PIThread_sec0 Краткий обзор
//! \~english
//! Multithreading allow you to execute code
//! 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(int), %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:
//!
//! \~russian
//! Многопоточность позволяет исполнять код в нескольких потоках
//! одновременно и использовать все ядра современных процессоров.
//! Однако это таит в себе опасности.
//!
//! Этот класс предоставляет виртуальные методы \a begin(), \a run() и \a end(),
//! которые описывают старт, выполнение и завершение потоковой задачи.
//! Все эти методы исполняются в \b отдельном потоке. Когда выполняется
//! \a start(int), %PIThread создает отдельный поток средствами ОС и последовательно
//! выполняет \a begin(), \a run() и \a end(). Можно переопределить каждый метод
//! для реализации нужной задачи.
//!
//! Схема выполнения методов:
//!
//! \~\code{.cpp}
//! virtual begin()
//! event started()
//! while (isRunning()) { // while not stop()
//! virtual run()
//! ThreadFunc() // Slot or lambda
//! piMSleep(loop_delay) // if loop_delay > 0
//! }
//! event stopped()
//! virtual end()
//! \endcode
//!
//! \~english
//! If no internal loop needed, you can use \a startOnce() method instead of \a start(int).
//!
//! In this case scheme is next:
//!
//! \~russian
//! Если внутренний цикл не нужен, то можно использовать метод \a startOnce() вместо \a start(int).
//!
//! В этом случает схема выполнения следущая:
//!
//! \~\code{.cpp}
//! virtual begin()
//! event started()
//! virtual run()
//! ThreadFunc() // Slot or lambda
//! event stopped()
//! virtual end()
//! \endcode
//!
//! \~english
//! 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.
//!
//! \~russian
//! В отличии от прямого использования "pthread" или похожих механизмов
//! нет необходимости писать свой цикл внутри поточного метода и ожидать
//! в его теле. %PIThread уже рализует такой цикл, необходимо лишь
//! установить значение ожидания в конструкторе или при запуске потока,
//! и переопределить методы \a begin(), \a run() и \a end().
//!
//! \~english Using with internal loop
//! \~russian Использование с внутренним циклом
//! \~\code{.cpp}
//! class MyThread: public PIThread {
//! PIOBJECT_SUBCLASS(MyThread, PIThread)
//! public:
//! void begin() override {piCout << "thread begin";}
//! void run() override {piCout << "thread run" << ++cnt;}
//! void end() override {piCout << "thread end";}
//! private:
//! int cnt = 0;
//! };
//!
//! int main(int argc, char * argv[]) {
//! MyThread mt;
//! mt.start(100);
//! piMSleep(500);
//! mt.stop(true);
//! return 0;
//! }
//!
//! // thread begin
//! // thread run 1
//! // thread run 2
//! // thread run 3
//! // thread run 4
//! // thread run 5
//! // thread end
//! \endcode
//!
//! \~english Using without internal loop
//! \~russian Использование без внутреннего цикла
//! \~\code{.cpp}
//! class MyThread: public PIThread {
//! PIOBJECT_SUBCLASS(MyThread, PIThread)
//! public:
//! void begin() override {piCout << "thread begin";}
//! void run() override {piCout << "thread run" << ++cnt;}
//! void end() override {piCout << "thread end";}
//! private:
//! int cnt = 0;
//! };
//!
//! int main(int argc, char * argv[]) {
//! MyThread mt;
//! mt.startOnce();
//! piMSleep(500);
//! mt.stop(true);
//! return 0;
//! }
//!
//! // thread begin
//! // thread run 1
//! // thread end
//! \endcode
//!
//!
//! \~english \section PIThread_sec1 Using without subclassing
//! \~russian \section PIThread_sec1 Использование без наследования
//! \~english
//! 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 loop_delay).
//! If "func" if not null this function will be executed after \a run().
//!
//! ThreadFunc is any static function with format "void func(void * data)", or
//! [lambda expression](https://en.cppreference.com/w/cpp/language/lambda) with format [...]( ){...}.
//! "Data" is custom data set from constructor or with \a setData() function.
//!
//! 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 after thread start.
//!
//! \~russian
//! Возможно использовать %PIThread без наследования. Можно передать ему в конструкторе
//! или при запуске указатель на внешний метод "ThreadFunc". В основном цикле потока,
//! если "ThreadFunc" существует, он будет вызываться после \a run().
//!
//! ThreadFunc может быть любым статическим методом в формате "void func(void * data)", либо
//! [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda) в формате [...]( ){...}.
//! "Data" является произвольным указателем, задаваемым в конструкторе или методом \a setData().
//!
//! Также можно присоединиться к событию \a started(), но в этом случае надо будет своими силами
//! реализовать весь цикл, т.к. это событие вызывается один раз после запуска потока.
//!
//! \~english Using with internal loop
//! \~russian Использование с внутренним циклом
//! \~\code{.cpp}
//! void myThreadFunc(void * d) {
//! int * cnt = (int*)d;
//! piCout << "thread run" << ++(*cnt);
//! }
//!
//! int main(int argc, char * argv[]) {
//! int cnt = 0;
//! PIThread mt;
//! mt.setData(&cnt);
//! mt.start(myThreadFunc, 100);
//! piMSleep(500);
//! mt.stop(true);
//! return 0;
//! }
//!
//! // thread run 1
//! // thread run 2
//! // thread run 3
//! // thread run 4
//! // thread run 5
//! \endcode
//!
//! \~english Using without internal loop
//! \~russian Использование без внутреннего цикла
//! \~\code{.cpp}
//! void myThreadFunc(void * d) {
//! int * cnt = (int*)d;
//! piCout << "thread run" << ++(*cnt);
//! }
//!
//! int main(int argc, char * argv[]) {
//! int cnt = 0;
//! PIThread mt;
//! mt.setData(&cnt);
//! mt.startOnce(myThreadFunc);
//! piMSleep(500);
//! mt.stop(true);
//! return 0;
//! }
//!
//! // thread run 1
//! \endcode
//!
//! \~english Using with [lambda expression](https://en.cppreference.com/w/cpp/language/lambda)
//! \~russian Использование с [лямбда-выражением](https://ru.cppreference.com/w/cpp/language/lambda)
//! \~\code{.cpp}
//! int main(int argc, char * argv[]) {
//! int cnt = 0;
//! PIThread mt;
//! mt.start([&cnt](){
//! piCout << "thread run" << ++cnt;
//! }, 100);
//! piMSleep(500);
//! mt.stop(true);
//! return 0;
//! }
//!
//! // thread run 1
//! // thread run 2
//! // thread run 3
//! // thread run 4
//! // thread run 5
//! \endcode
//!
//! \~english Using with event
//! \~russian Использование с событием
//! \~\code{.cpp}
//! class MyObj: public PIObject {
//! PIOBJECT(MyObj)
//! public:
//! EVENT_HANDLER(void, threadRun) {
//! piForTimes (5) {
//! piCout << "threadRun" << ++cnt;
//! piMSleep(100);
//! }
//! };
//! private:
//! int cnt = 0;
//! };
//!
//! int main(int argc, char * argv[]) {
//! MyObj mo;
//! PIThread mt;
//! CONNECTU(&mt, started, &mo, threadRun);
//! mt.startOnce();
//! mt.stop(true);
//! return 0;
//! }
//!
//! // threadRun 1
//! // threadRun 2
//! // threadRun 3
//! // threadRun 4
//! // threadRun 5
//! \endcode
//!
//! \~english \section PIThread_sec2 Locking
//! \~russian \section PIThread_sec2 Блокировки
//! \~english
//! %PIThread has inrternal mutex that can be locked and unlocked every \a run() if you set this flag
//! with function \a needLockRun(true). Also you can access to this mutex by functions \a lock(), \a unlock()
//! and \a mutex(). Using this functions outside of thread together with \a needLockRun(true) can defend
//! your data.
//!
//! \~russian
//! %PIThread имеет встроенный мьютекс, который может блокироваться и разблокироваться каждый \a run()
//! при установленном \a needLockRun(true). К нему есть доступ через методы \a lock(), \a unlock() и \a mutex().
//! Использование этих методов вне потока совместно с \a needLockRun(true) поможет защитить данные.
//!
//! \}
//!
//! \fn bool PIThread::start(int loop_delay = -1)
//! \~\details
//! \~english
//! Start execution of \a run() in internal loop with
//! "loop_delay" delay in milliseconds. If "loop_delay" <= 0
//! this 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
//!
//! \~russian
//! Начинает выполнение \a run() во внутреннем цикле
//! с задержкой "loop_delay" миллисекунд. Если "loop_delay" <= 0
//! то задержки не будет. Также поток вызывает внешний метод,
//! заданный через \a setSlot(), если он существует.
//! \return \c false если поток уже запущен или не может запуститься
//! \fn bool PIThread::startOnce()
//! \~\details
//! \~english
//! 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
//!
//! \~russian
//! Начинает выполнение \a run() один раз. Также поток вызывает
//! внешний метод, заданный через \a setSlot(), если он существует.
//! \return \c false если поток уже запущен или не может запуститься
//! \fn bool PIThread::startOnce(ThreadFunc func)
//! \~\details
//! \~english
//! Overloaded function. Set external function "func" before start.
//! \return \c false if thread already started or can`t start thread
//!
//! \~russian
//! Перегрузка метода, устанавливает внешний метод в "func" перед запуском.
//! \return \c false если поток уже запущен или не может запуститься
//! \fn void PIThread::stop(bool wait = false)
//! \~\details
//! \~english
//! Mark thread to stop execution and wait for finish
//! if "wait" is \c true. Thread can be stopped only outside of
//! \a run() or external method.
//! \warning This function can block for infinite
//! time if "wait" is \c true and any of thread function is
//! busy forever.
//!
//! \~russian
//! Помечает поток на остановку и ожидает завершения если
//! "wait" \c true. Поток может быть остановлен только вне
//! \a run() или внешнего метода.
//! \warning Этот метод может ожидать бесконечно если
//! "wait" \c true и любой из потоковых методов занят навечно.
//! \fn void PIThread::terminate()
//! \~\details
//! \~english
//! Stop execution of thread immediately.
//! \warning Memory can be corrupted, try not to use this method!
//!
//! \~russian
//! Немедленно останавливает поток.
//! \warning Это может повредить память, старайтесь не использовать этот метод!
//! \fn bool PIThread::waitForStart(int timeout_msecs = -1)
//! \~\details
//! \~english
//! Block until thread start within "timeout_msecs"
//! or forever if "timeout_msecs" < 0
//! \return \c true if thread started less than timeout
//! \return \c false if timeout is exceeded
//!
//! \~russian
//! Блокирует до тех пор, пока поток не запустится в течении
//! "timeout_msecs", или бесконечно, если "timeout_msecs" < 0
//! \return \c true если поток запустился менее чем за таймаут
//! \return \c false если таймаут истёк
//! \fn bool PIThread::waitForFinish(int timeout_msecs = -1)
//! \~\details
//! \~english
//! Block until thread finish for "timeout_msecs"
//! or forever if "timeout_msecs" < 0
//! \return \c true if thread finished less than timeout
//! \return \c false if timeout is exceeded
//!
//! \~russian
//! Блокирует до тех пор, пока поток не завершится в течении
//! "timeout_msecs", или бесконечно, если "timeout_msecs" < 0
//! \return \c true если поток завершился менее чем за таймаут
//! \return \c false если таймаут истёк
#ifndef MICRO_PIP
__PIThreadCollection * __PIThreadCollection::instance() {
return __PIThreadCollection_Initializer__::__instance__;
}
void __PIThreadCollection::registerThread(PIThread * t) {
lock();
if (!threads_.contains(t)) threads_ << t;
unlock();
}
void __PIThreadCollection::unregisterThread(PIThread * t) {
lock();
threads_.removeAll(t);
unlock();
}
PIVector __PIThreadCollection::threads() const {
return threads_;
}
void __PIThreadCollection::startedAuto(PIThread * t) {
PIMutexLocker _ml(auto_mutex);
auto_threads_ << t;
}
void __PIThreadCollection::stoppedAuto() {
PIThread * t = emitter()->cast();
if (!t) return;
auto_mutex.lock();
auto_threads_.removeAll(t);
auto_mutex.unlock();
t->deleteLater();
}
int __PIThreadCollection_Initializer__::count_(0);
__PIThreadCollection * __PIThreadCollection_Initializer__::__instance__(0);
__PIThreadCollection_Initializer__::__PIThreadCollection_Initializer__() {
count_++;
// PICout(PICoutManipulators::DefaultControls) << "try create Core" << count_;
if (count_ > 1) return;
// PICout(PICoutManipulators::DefaultControls) << "create Core";
__instance__ = new __PIThreadCollection();
}
__PIThreadCollection_Initializer__::~__PIThreadCollection_Initializer__() {
count_--;
// PICout(PICoutManipulators::DefaultControls) << "try delete Core" << count_;
if (count_ > 0) return;
// PICout(PICoutManipulators::DefaultControls) << "delete Core";
if (__instance__ != 0) {
delete __instance__;
__instance__ = 0;
}
}
#endif // MICRO_PIP
PRIVATE_DEFINITION_START(PIThread)
#if defined(WINDOWS)
void * thread = nullptr;
#elif defined(FREERTOS)
TaskHandle_t thread;
#else
pthread_t thread = 0;
sched_param sparam;
#endif
PRIVATE_DEFINITION_END(PIThread)
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, int loop_delay): PIObject() {
PIINTROSPECTION_THREAD_NEW(this);
data_ = data;
ret_func = func;
terminating = running_ = lockRun = false;
priority_ = piNormal;
delay_ = loop_delay;
if (startNow) start(loop_delay);
}
PIThread::PIThread(std::function func, bool startNow, int loop_delay) {
PIINTROSPECTION_THREAD_NEW(this);
ret_func = [func](void *) { func(); };
terminating = running_ = lockRun = false;
priority_ = piNormal;
delay_ = loop_delay;
if (startNow) start(loop_delay);
}
PIThread::PIThread(bool startNow, int loop_delay): PIObject() {
PIINTROSPECTION_THREAD_NEW(this);
terminating = running_ = lockRun = false;
priority_ = piNormal;
delay_ = loop_delay;
if (startNow) start(loop_delay);
}
PIThread::~PIThread() {
PIINTROSPECTION_THREAD_DELETE(this);
if (!running_ || PRIVATE->thread == 0) return;
#ifdef FREERTOS
// void * ret(0);
// PICout(PICoutManipulators::DefaultControls) << "~PIThread" << PRIVATE->thread;
// PICout(PICoutManipulators::DefaultControls) << pthread_join(PRIVATE->thread, 0);
PICout(PICoutManipulators::DefaultControls) << "FreeRTOS can't terminate pthreads! waiting for stop";
stop(true);
// PICout(PICoutManipulators::DefaultControls) << "stopped!";
#else
# ifndef WINDOWS
# ifdef ANDROID
pthread_kill(PRIVATE->thread, SIGTERM);
# else
pthread_cancel(PRIVATE->thread);
# endif
# else
TerminateThread(PRIVATE->thread, 0);
CloseHandle(PRIVATE->thread);
# endif
#endif
terminating = running_ = false;
}
bool PIThread::start(ThreadFunc func, int loop_delay) {
ret_func = func;
return start(loop_delay);
}
bool PIThread::start(std::function func, int loop_delay) {
ret_func = [func](void *) { func(); };
return start(loop_delay);
}
bool PIThread::startOnce(ThreadFunc func) {
ret_func = func;
return startOnce();
}
void PIThread::stopAndWait(int timeout_ms) {
stop();
waitForFinish(timeout_ms);
}
void PIThread::stop() {
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop ..." << running_ << wait;
terminating = true;
}
bool PIThread::start(int loop_delay) {
if (running_) return false;
delay_ = loop_delay;
return _startThread((void *)thread_function);
}
bool PIThread::startOnce() {
if (running_) return false;
return _startThread((void *)thread_function_once);
}
void PIThread::terminate() {
piCoutObj << "Warning, terminate!";
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "terminate ..." << running_;
#ifdef FREERTOS
PICout(PICoutManipulators::DefaultControls) << "FreeRTOS can't terminate pthreads! waiting for stop";
stop(true);
// PICout(PICoutManipulators::DefaultControls) << "stopped!";
#else
if (PRIVATE->thread == 0) return;
UNREGISTER_THREAD(this);
terminating = running_ = false;
tid_ = -1;
// PICout(PICoutManipulators::DefaultControls) << "terminate" << PRIVATE->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();
#endif // FREERTOS
PIINTROSPECTION_THREAD_STOP(this);
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "terminate ok" << running_;
}
int PIThread::priority2System(PIThread::Priority p) {
switch (p) {
#if defined(QNX)
case piLowerst: return 8;
case piLow: return 9;
case piNormal: return 10;
case piHigh: return 11;
case piHighest: return 12;
#elif defined(WINDOWS)
case piLowerst: return -2;
case piLow: return -1;
case piNormal: return 0;
case piHigh: return 1;
case piHighest: return 2;
#elif defined(FREERTOS)
case piLowerst: return 2;
case piLow: return 3;
case piNormal: return 4;
case piHigh: return 5;
case piHighest: return 6;
#else
case piLowerst: return 2;
case piLow: return 1;
case piNormal: return 0;
case piHigh: return -1;
case piHighest: return -2;
#endif
default: return 0;
}
return 0;
}
bool PIThread::_startThread(void * func) {
terminating = false;
running_ = true;
#ifdef FREERTOS
if (xTaskCreate((__THREAD_FUNC_RET__(*)(void *))func,
((PIString &)name().elided(15, 0.4f).resize(15, PIChar('\0'))).dataAscii(), // A name just for humans
128, // This stack size can be checked & adjusted by reading the Stack Highwater
this,
priority_,
&PRIVATE->thread) == pdPASS) {
tid_ = (llong)PRIVATE->thread;
return true;
}
#elif defined(WINDOWS)
if (PRIVATE->thread) CloseHandle(PRIVATE->thread);
# ifdef CC_GCC
PRIVATE->thread = (void *)_beginthreadex(0, 0, (__THREAD_FUNC_RET__(*)(void *))func, this, 0, 0);
# else
PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, 0, 0);
# endif
if (PRIVATE->thread != 0) {
setPriority(priority_);
return true;
}
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int ret = pthread_create(&PRIVATE->thread, &attr, (__THREAD_FUNC_RET__(*)(void *))func, this);
// PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread;
pthread_attr_destroy(&attr);
if (ret == 0) {
# ifdef MAC_OS
pthread_setname_np(((PIString &)name().elided(15, 0.4f).resize(15, PIChar('\0'))).dataAscii());
pthread_threadid_np(PRIVATE->thread, (__uint64_t *)&tid_);
# else
pthread_setname_np(PRIVATE->thread, ((PIString &)name().elided(15, 0.4f).resize(15, PIChar('\0'))).dataAscii());
# endif
setPriority(priority_);
return true;
}
#endif
running_ = false;
PRIVATE->thread = 0;
piCoutObj << "Error: Can`t start new thread:" << errorString();
return false;
}
void PIThread::setPriority(PIThread::Priority prior) {
priority_ = prior;
if (!running_ || (PRIVATE->thread == 0)) return;
#ifdef FREERTOS
vTaskPrioritySet(PRIVATE->thread, priority2System(priority_));
#else
# ifndef WINDOWS
// PICout(PICoutManipulators::DefaultControls) << "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
#endif // FREERTOS
}
bool PIThread::waitForStart(PISystemTime timeout) {
return waitForStart(timeout.toMilliseconds());
}
bool PIThread::waitForFinish(PISystemTime timeout) {
return waitForFinish(timeout.toMilliseconds());
}
#ifdef WINDOWS
bool isExists(HANDLE hThread) {
// errorClear();
// piCout << "isExists" << hThread;
DWORD dw = 0;
GetExitCodeThread(hThread, &dw);
// piCout << ret << dw << errorString();
if (dw == STILL_ACTIVE) return true;
// piCout << errorString();
return false;
}
#endif
bool PIThread::waitForFinish(int timeout_msecs) {
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "PIThread::waitForFinish" << running_ << terminating <<
// timeout_msecs;
if (!running_) return true;
if (timeout_msecs < 0) {
while (running_) {
piMinSleep();
#ifdef WINDOWS
if (!isExists(PRIVATE->thread)) {
unlock();
return true;
}
#endif
}
return true;
}
tmf_.reset();
while (running_ && tmf_.elapsed_m() < timeout_msecs) {
piMinSleep();
#ifdef WINDOWS
if (!isExists(PRIVATE->thread)) {
unlock();
return true;
}
#endif
}
return tmf_.elapsed_m() < timeout_msecs;
}
bool PIThread::waitForStart(int timeout_msecs) {
if (running_) return true;
if (timeout_msecs < 0) {
while (!running_)
piMinSleep();
return true;
}
tms_.reset();
while (!running_ && tms_.elapsed_m() < timeout_msecs)
piMinSleep();
return tms_.elapsed_m() < timeout_msecs;
}
void PIThread::_beginThread() {
#ifndef WINDOWS
# if !defined(ANDROID) && !defined(FREERTOS)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0);
# endif
#endif
#ifdef WINDOWS
tid_ = GetCurrentThreadId();
#endif
#ifdef LINUX
tid_ = gettid();
#endif
PIINTROSPECTION_THREAD_START(this);
REGISTER_THREAD(this);
running_ = true;
if (lockRun) thread_mutex.lock();
begin();
if (lockRun) thread_mutex.unlock();
started();
}
void PIThread::_runThread() {
PIINTROSPECTION_THREAD_RUN(this);
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "...";
if (lockRun) thread_mutex.lock();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "ok";
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "...";
#ifdef PIP_INTROSPECTION
PITimeMeasurer _tm;
#endif
run();
#ifdef PIP_INTROSPECTION
PIINTROSPECTION_THREAD_RUN_DONE(this, ullong(_tm.elapsed_u()));
#endif
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "ok";
// printf("thread %p tick\n", this);
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "ret_func" << "...";
if (ret_func != 0) ret_func(data_);
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "ret_func" << "ok";
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "unlock" << "...";
if (lockRun) thread_mutex.unlock();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "unlock" << "ok";
}
void PIThread::_endThread() {
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop" << "...";
stopped();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop" << "ok";
if (lockRun) thread_mutex.lock();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "end" << "...";
end();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop" << "ok";
if (lockRun) thread_mutex.unlock();
terminating = running_ = false;
tid_ = -1;
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "exit";
// cout << "thread " << t << " exiting ... " << endl;
// PICout(PICoutManipulators::DefaultControls) << "pthread_exit" << (__privateinitializer__.p)->thread;
UNREGISTER_THREAD(this);
PIINTROSPECTION_THREAD_STOP(this);
#if defined(WINDOWS)
# ifdef CC_GCC
_endthreadex(0);
# else
ExitThread(0);
# endif
#elif defined(FREERTOS)
PRIVATE->thread = 0;
#else
pthread_detach(PRIVATE->thread);
PRIVATE->thread = 0;
pthread_exit(0);
#endif
}
void PIThread::__thread_func__() {
_beginThread();
while (!terminating) {
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "queued" << "...";
maybeCallQueuedEvents();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "queued" << "ok";
_runThread();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "wait" << "...";
PIINTROSPECTION_THREAD_WAIT(this);
if (delay_ > 0) {
tmr_.reset();
double sl(0.);
while (1) {
sl = piMind(delay_ - tmr_.elapsed_m(), PIP_MIN_MSLEEP);
if (terminating) break;
if (sl <= 0.) break;
piMSleep(sl);
}
}
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "wait" << "ok";
}
_endThread();
}
void PIThread::__thread_func_once__() {
_beginThread();
_runThread();
_endThread();
}
//! \~\details
//! \~english
//! This method create %PIThread with name "name" and execute
//! event handler "handler" of object "object" in this thread.\n
//! This %PIThread automatically delete on function finish.
//!
//! \~russian
//! Этот метод создает %PIThread с именем "name" и выполняет
//! обработчик "handler" объекта "object" в этом потоке.\n
//! %PIThread автоматически удаляется после завершения обработчика.
//!
//! \~\code
//! class MyObj: public PIObject {
//! PIOBJECT(MyObj)
//! public:
//! EVENT_HANDLER(void, threadRun) {
//! piForTimes (5) {
//! piCout << "threadRun";
//! piMSleep(100);
//! }
//! };
//! };
//!
//! int main(int argc, char * argv[]) {
//! MyObj mo;
//! PIThread::runOnce(&mo, "threadRun");
//! piMSleep(1000); // wait for thread finish
//! return 0;
//! }
//! \endcode
void PIThread::runOnce(PIObject * object, const char * handler, const PIString & name) {
PIThread * t = new PIThread();
t->setName(name);
if (!PIObject::piConnectU(t, PIStringAscii("started"), object, object, PIStringAscii(handler), "PIThread::runOnce").isValid()) {
delete t;
return;
}
#ifndef MICRO_PIP
__PIThreadCollection::instance()->startedAuto(t);
CONNECT0(void, t, stopped, __PIThreadCollection::instance(), stoppedAuto);
#endif
t->startOnce();
}
//! \~\details
//! \~english
//! This method create %PIThread with name "name" and execute
//! [lambda expression](https://en.cppreference.com/w/cpp/language/lambda) "func" in this thread.\n
//! This %PIThread automatically delete on function finish.\n
//! "func" shouldn`t have arguments.
//!
//! \~russian
//! Этот метод создает %PIThread с именем "name" и выполняет
//! [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda) "func" в этом потоке.\n
//! %PIThread автоматически удаляется после завершения функции.\n
//! "func" не должна иметь аргументов.
//!
//! \~\code
//! PIThread::runOnce([](){
//! piForTimes(5) {
//! piCout << "thread func";
//! piMSleep(100);
//! }
//! });
//! piMSleep(1000);
//! \endcode
void PIThread::runOnce(std::function func, const PIString & name) {
PIThread * t = new PIThread();
t->setName(name);
t->setSlot(func);
#ifndef MICRO_PIP
__PIThreadCollection::instance()->startedAuto(t);
CONNECT0(void, t, stopped, __PIThreadCollection::instance(), stoppedAuto);
#endif
t->startOnce();
}