Files
pip/libs/main/thread/pithread.cpp
2024-09-17 16:11:18 +03:00

1065 lines
33 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "pithread.h"
#include "piincludes_p.h"
#include "piintrospection_threads.h"
#include "pitime.h"
#ifndef MICRO_PIP
# include "pisystemtests.h"
#endif
#ifdef WINDOWS
# include <ioapiset.h>
#endif
#include <signal.h>
#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 <sys/syscall.h>
# define gettid() syscall(SYS_gettid)
#endif
#if defined(MAC_OS) || defined(BLACKBERRY)
# include <pthread.h>
#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<PIThread *> __PIThreadCollection::threads() const {
return threads_;
}
void __PIThreadCollection::startedAuto(PIThread * t) {
PIMutexLocker _ml(auto_mutex);
auto_threads_ << t;
}
void __PIThreadCollection::stoppedAuto() {
PIThread * t = emitter()->cast<PIThread>();
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, PISystemTime loop_delay): PIObject() {
PIINTROSPECTION_THREAD_NEW(this);
data_ = data;
ret_func = func;
terminating = running_ = lockRun = false;
priority_ = piNormal;
delay_ = loop_delay;
if (startNow) start();
}
PIThread::PIThread(std::function<void()> func, bool startNow, PISystemTime loop_delay) {
PIINTROSPECTION_THREAD_NEW(this);
ret_func = [func](void *) { func(); };
terminating = running_ = lockRun = false;
priority_ = piNormal;
delay_ = loop_delay;
if (startNow) start();
}
PIThread::PIThread(bool startNow, PISystemTime loop_delay): PIObject() {
PIINTROSPECTION_THREAD_NEW(this);
terminating = running_ = lockRun = false;
priority_ = piNormal;
delay_ = loop_delay;
if (startNow) start();
}
PIThread::~PIThread() {
PIINTROSPECTION_THREAD_DELETE(this);
if (!running_ || PRIVATE->thread == 0) return;
piCout << "[PIThread \"" << name() << "\"] Warning, terminate on destructor!";
#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";
stopAndWait();
// 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
UNREGISTER_THREAD(this);
PIINTROSPECTION_THREAD_STOP(this);
terminating = running_ = false;
}
bool PIThread::start() {
if (running_) return false;
return _startThread((void *)thread_function);
}
bool PIThread::start(PISystemTime loop_delay) {
delay_ = loop_delay;
return start();
}
bool PIThread::start(ThreadFunc func) {
ret_func = func;
return start();
}
bool PIThread::start(ThreadFunc func, PISystemTime loop_delay) {
ret_func = func;
delay_ = loop_delay;
return start();
}
bool PIThread::start(std::function<void()> func) {
ret_func = [func](void *) { func(); };
return start();
}
bool PIThread::start(std::function<void()> func, PISystemTime loop_delay) {
ret_func = [func](void *) { func(); };
delay_ = loop_delay;
return start();
}
bool PIThread::startOnce() {
if (running_) return false;
return _startThread((void *)thread_function_once);
}
bool PIThread::startOnce(ThreadFunc func) {
ret_func = func;
return startOnce();
}
bool PIThread::startOnce(std::function<void()> func) {
ret_func = [func](void *) { func(); };
return startOnce();
}
void PIThread::stopAndWait(PISystemTime timeout) {
stop();
waitForFinish(timeout);
}
void PIThread::stop() {
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop ..." << running_ << wait;
terminating = true;
}
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;
int 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
}
#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(PISystemTime timeout) {
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "PIThread::waitForFinish" << running_ << terminating <<
// timeout_msecs;
if (!running_) return true;
if (timeout.isNull()) {
while (running_) {
piMinSleep();
#ifdef WINDOWS
if (!isExists(PRIVATE->thread)) {
unlock();
return true;
}
#endif
}
return true;
}
tmf_.reset();
while (running_ && tmf_.elapsed() < timeout) {
piMinSleep();
#ifdef WINDOWS
if (!isExists(PRIVATE->thread)) {
unlock();
return true;
}
#endif
}
return tmf_.elapsed() < timeout;
}
bool PIThread::waitForStart(PISystemTime timeout) {
if (running_) return true;
if (timeout.isNull()) {
while (!running_)
piMinSleep();
return true;
}
tms_.reset();
while (!running_ && tms_.elapsed() < timeout)
piMinSleep();
return tms_.elapsed() < timeout;
}
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() {
PIScopeExitCall ec([this] {
terminating = running_ = false;
tid_ = -1;
});
// 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();
// 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)
ec.callAndCancel();
# 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_.isNotNull()) {
tmr_.reset();
double sl(0.);
while (1) {
sl = piMind(delay_.toMilliseconds() - 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<void()> 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();
}