/* 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 "piincludes_p.h" #include "pithread.h" #include "piintrospection_threads.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(timer_delay) // if timer_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 timer_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 timer_delay = -1) //! \~\details //! \~english //! Start execution of \a run() in internal loop with //! "timer_delay" delay in milliseconds. If "timer_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() во внутреннем цикле //! с задержкой "timer_delay" миллисекунд. Если "timer_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(); delete t; } 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; #elif defined(FREERTOS) TaskHandle_t thread; #else pthread_t thread; sched_param sparam; #endif PRIVATE_DEFINITION_END(PIThread) PIThread::PIThread(void * data, ThreadFunc func, bool startNow, int timer_delay): PIObject() { PIINTROSPECTION_THREAD_NEW(this); tid_ = -1; PRIVATE->thread = 0; data_ = data; ret_func = func; terminating = running_ = lockRun = false; priority_ = piNormal; delay_ = timer_delay; if (startNow) start(timer_delay); } PIThread::PIThread(std::function func, bool startNow, int timer_delay) { PIINTROSPECTION_THREAD_NEW(this); tid_ = -1; PRIVATE->thread = 0; data_ = 0; ret_func = [func](void*){func();}; terminating = running_ = lockRun = false; priority_ = piNormal; delay_ = timer_delay; if (startNow) start(timer_delay); } PIThread::PIThread(bool startNow, int timer_delay): PIObject() { PIINTROSPECTION_THREAD_NEW(this); tid_ = -1; PRIVATE->thread = 0; ret_func = 0; terminating = running_ = lockRun = false; priority_ = piNormal; delay_ = timer_delay; if (startNow) start(timer_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; } void PIThread::stopAndWait(int timeout_ms) { stop(); waitForFinish(timeout_ms); } #ifdef WINDOWS NTAPI void winThreadAPC(ULONG_PTR) { //piCout << "APC"; } #endif void PIThread::interrupt() { if (PRIVATE->thread == 0) return; piCout << "PIThread::interrupt"; #ifdef WINDOWS CancelSynchronousIo(PRIVATE->thread); QueueUserAPC(winThreadAPC, PRIVATE->thread, 0); #else # ifdef POSIX_SIGNALS pthread_kill(PRIVATE->thread, PIP_INTERRUPT_SIGNAL); # endif #endif } void PIThread::stop() { //PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop ..." << running_ << wait; terminating = true; } bool PIThread::start(int timer_delay) { if (running_) return false; delay_ = timer_delay; return _startThread((void*)thread_function); } bool PIThread::startOnce() { if (running_) return false; return _startThread((void*)thread_function_once); } void PIThread::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; #if !defined(WINDOWS) && !defined(FREERTOS) 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 #endif #ifdef 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) { #endif #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; #endif #ifndef FREERTOS setPriority(priority_); #endif return true; } else { running_ = false; PRIVATE->thread = 0; piCoutObj << "Error: Can`t start new thread:" << errorString(); } running_ = false; 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 } #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(); }