1087 lines
33 KiB
C++
1087 lines
33 KiB
C++
/*
|
||
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"
|
||
#include "pitranslator.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 \"%1\"] Warning, terminate on destructor!"_tr("PIThread").arg(name());
|
||
#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!"_tr("PIThread");
|
||
// 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
|
||
|
||
auto name_ba = createThreadName();
|
||
if (xTaskCreate((__THREAD_FUNC_RET__(*)(void *))func,
|
||
(const char *)name_ba.data(), // 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, CREATE_SUSPENDED, 0);
|
||
# else
|
||
PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, CREATE_SUSPENDED, 0);
|
||
# endif
|
||
if (PRIVATE->thread != 0) {
|
||
ResumeThread(PRIVATE->thread);
|
||
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);
|
||
pthread_attr_destroy(&attr);
|
||
// PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread;
|
||
// piCout << "started" << PRIVATE->thread;
|
||
if (ret == 0) {
|
||
return true;
|
||
}
|
||
|
||
#endif
|
||
|
||
running_ = false;
|
||
PRIVATE->thread = 0;
|
||
piCoutObj << "Error: Can`t start new thread: %1"_tr("PIThread").arg(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
|
||
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
|
||
setPriority(priority_);
|
||
setThreadName();
|
||
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;
|
||
});
|
||
// while (PRIVATE->starting)
|
||
// piMinSleep();
|
||
// 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();
|
||
}
|
||
|
||
|
||
PIByteArray PIThread::createThreadName(int size) const {
|
||
PIString tname = name().simplified();
|
||
tname.elide(size - 1, 0.4f).resize(size - 1, PIChar('\0'));
|
||
PIByteArray ret = tname.toAscii();
|
||
ret.resize(size);
|
||
ret.back() = 0;
|
||
return ret;
|
||
}
|
||
|
||
|
||
void PIThread::setThreadName() {
|
||
#ifndef WINDOWS
|
||
auto name_ba = createThreadName();
|
||
# ifdef MAC_OS
|
||
pthread_setname_np((const char *)name_ba.data());
|
||
pthread_threadid_np(PRIVATE->thread, (__uint64_t *)&tid_);
|
||
# else
|
||
pthread_setname_np(PRIVATE->thread, (const char *)name_ba.data());
|
||
# endif
|
||
#endif
|
||
}
|