/* 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 #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 /*! \class PIThread * @brief Thread class * \details This class allow you exec your code in separate thread. * * \section PIThread_sec0 Synopsis * Multithreading allow you to write program which will be executed * in several threads simultaneously. This trend allow you to use all * cores of modern processors, but there are many dangers. * * This class provide virtual functions \a begin(), \a run() and \a end(), * which describes start, execution and finish work of some process. * These functions executes in \b separate thread. When you execute * \a start(), %PIThread create separate system thread and sequentially * executes function \a begin(), \a run() and \a end(). You can * reimplement each function and write your own code to execute. * Scheme of functions executing: \code{.cpp} begin(); event started(); while (isRunning()) { run(); ThreadFunc(); piMSleep(timer_delay); } event stopped(); end(); \endcode * Unlike from directly using "pthread" or some similar you doesn`t need * to write your own main thread cycle and sleep at every cycle end. * %PIThread make it for you, and your job is to set sleep value from * contructor or when starting thread, and reimplement \a begin(), \a run() * and \a end() functions. * * \section PIThread_sec1 Using without subclassing * You can use %PIThread without subclassing by using "ThreadFunc" pointer * that can be set from constructor or by overloaded function \a start(ThreadFunc func, int timer_delay). * If "func" if not null this function will be executed as \a run(). ThreadFunc is any static * function with format void func(void * data). "Data" is custom data set from constructor or * with function \a setData(). \n Also you can connect to event \a started(), but * in this case you should to white your thread main cycle, because this event raised only one time. * * \section PIThread_sec2 Locking * %PIThread has inrternal mutex that can be locked and unlocked every \a run() if you set this flag * with function \a needLockRun(bool). Also you can access to this mutex by functions \a lock(), \a unlock() * and \a mutex(). Using this functions together with needLockRun(true) can guarantee one-thread access to * some data. * */ #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::stop(bool wait) { //PICout(PICoutManipulators::DefaultControls) << "thread" << this << "stop ..." << running_ << wait; terminating = true; if (wait) waitForFinish(); } 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(); } 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); CONNECTU(t, stopped, __PIThreadCollection::instance(), stoppedAuto); #endif t->startOnce(); } 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); CONNECTU(t, stopped, __PIThreadCollection::instance(), stoppedAuto); #endif t->startOnce(); }