| @@ -0,0 +1,291 @@ | |||||
| /* | |||||
| * DISTRHO Plugin Framework (DPF) | |||||
| * Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
| * or without fee is hereby granted, provided that the above copyright notice and this | |||||
| * permission notice appear in all copies. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
| * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
| * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
| */ | |||||
| #ifndef DISTRHO_THREAD_HPP_INCLUDED | |||||
| #define DISTRHO_THREAD_HPP_INCLUDED | |||||
| #include "d_mutex.hpp" | |||||
| #include "d_sleep.hpp" | |||||
| #include "d_string.hpp" | |||||
| #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 | |||||
| // has pthread_setname_np | |||||
| #elif defined(DISTRHO_OS_LINUX) | |||||
| # include <sys/prctl.h> | |||||
| #endif | |||||
| START_NAMESPACE_DISTRHO | |||||
| // ----------------------------------------------------------------------- | |||||
| // Thread class | |||||
| class Thread | |||||
| { | |||||
| protected: | |||||
| /* | |||||
| * Constructor. | |||||
| */ | |||||
| Thread(const char* const threadName = nullptr) noexcept | |||||
| : fLock(), | |||||
| fName(threadName), | |||||
| #ifdef PTW32_DLLPORT | |||||
| fHandle({nullptr, 0}), | |||||
| #else | |||||
| fHandle(0), | |||||
| #endif | |||||
| fShouldExit(false) {} | |||||
| /* | |||||
| * Destructor. | |||||
| */ | |||||
| virtual ~Thread() /*noexcept*/ | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT(! isThreadRunning()); | |||||
| stopThread(-1); | |||||
| } | |||||
| /* | |||||
| * Virtual function to be implemented by the subclass. | |||||
| */ | |||||
| virtual void run() = 0; | |||||
| // ------------------------------------------------------------------- | |||||
| public: | |||||
| /* | |||||
| * Check if the thread is running. | |||||
| */ | |||||
| bool isThreadRunning() const noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| return (fHandle.p != nullptr); | |||||
| #else | |||||
| return (fHandle != 0); | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Check if the thread should exit. | |||||
| */ | |||||
| bool shouldThreadExit() const noexcept | |||||
| { | |||||
| return fShouldExit; | |||||
| } | |||||
| /* | |||||
| * Start the thread. | |||||
| */ | |||||
| bool startThread() noexcept | |||||
| { | |||||
| // check if already running | |||||
| DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true); | |||||
| const MutexLocker cml(fLock); | |||||
| fShouldExit = false; | |||||
| pthread_t handle; | |||||
| if (pthread_create(&handle, nullptr, _entryPoint, this) == 0) | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false); | |||||
| #else | |||||
| DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false); | |||||
| #endif | |||||
| pthread_detach(handle); | |||||
| _copyFrom(handle); | |||||
| // wait for thread to start | |||||
| fLock.lock(); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /* | |||||
| * Stop the thread. | |||||
| * In the 'timeOutMilliseconds': | |||||
| * = 0 -> no wait | |||||
| * > 0 -> wait timeout value | |||||
| * < 0 -> wait forever | |||||
| */ | |||||
| bool stopThread(const int timeOutMilliseconds) noexcept | |||||
| { | |||||
| const MutexLocker cml(fLock); | |||||
| if (isThreadRunning()) | |||||
| { | |||||
| signalThreadShouldExit(); | |||||
| if (timeOutMilliseconds != 0) | |||||
| { | |||||
| // Wait for the thread to stop | |||||
| int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2; | |||||
| for (; isThreadRunning();) | |||||
| { | |||||
| d_msleep(2); | |||||
| if (timeOutCheck < 0) | |||||
| continue; | |||||
| if (timeOutCheck > 0) | |||||
| timeOutCheck -= 1; | |||||
| else | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (isThreadRunning()) | |||||
| { | |||||
| // should never happen! | |||||
| d_stderr2("Carla assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__); | |||||
| // copy thread id so we can clear our one | |||||
| pthread_t threadId; | |||||
| _copyTo(threadId); | |||||
| _init(); | |||||
| try { | |||||
| pthread_cancel(threadId); | |||||
| } DISTRHO_SAFE_EXCEPTION("pthread_cancel"); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /* | |||||
| * Tell the thread to stop as soon as possible. | |||||
| */ | |||||
| void signalThreadShouldExit() noexcept | |||||
| { | |||||
| fShouldExit = true; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| /* | |||||
| * Returns the name of the thread. | |||||
| * This is the name that gets set in the constructor. | |||||
| */ | |||||
| const d_string& getThreadName() const noexcept | |||||
| { | |||||
| return fName; | |||||
| } | |||||
| /* | |||||
| * Changes the name of the caller thread. | |||||
| */ | |||||
| static void setCurrentThreadName(const char* const name) noexcept | |||||
| { | |||||
| DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); | |||||
| #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 | |||||
| pthread_setname_np(pthread_self(), name); | |||||
| #elif defined(DISTRHO_OS_LINUX) | |||||
| prctl(PR_SET_NAME, name, 0, 0, 0); | |||||
| #endif | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| private: | |||||
| Mutex fLock; // Thread lock | |||||
| const d_string fName; // Thread name | |||||
| volatile pthread_t fHandle; // Handle for this thread | |||||
| volatile bool fShouldExit; // true if thread should exit | |||||
| /* | |||||
| * Init pthread type. | |||||
| */ | |||||
| void _init() noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| fHandle.p = nullptr; | |||||
| fHandle.x = 0; | |||||
| #else | |||||
| fHandle = 0; | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Copy our pthread type from another var. | |||||
| */ | |||||
| void _copyFrom(const pthread_t& handle) noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| fHandle.p = handle.p; | |||||
| fHandle.x = handle.x; | |||||
| #else | |||||
| fHandle = handle; | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Copy our pthread type to another var. | |||||
| */ | |||||
| void _copyTo(volatile pthread_t& handle) const noexcept | |||||
| { | |||||
| #ifdef PTW32_DLLPORT | |||||
| handle.p = fHandle.p; | |||||
| handle.x = fHandle.x; | |||||
| #else | |||||
| handle = fHandle; | |||||
| #endif | |||||
| } | |||||
| /* | |||||
| * Thread entry point. | |||||
| */ | |||||
| void _runEntryPoint() noexcept | |||||
| { | |||||
| // report ready | |||||
| fLock.unlock(); | |||||
| setCurrentThreadName(fName); | |||||
| try { | |||||
| run(); | |||||
| } catch(...) {} | |||||
| // done | |||||
| _init(); | |||||
| } | |||||
| /* | |||||
| * Thread entry point. | |||||
| */ | |||||
| static void* _entryPoint(void* userData) noexcept | |||||
| { | |||||
| static_cast<Thread*>(userData)->_runEntryPoint(); | |||||
| return nullptr; | |||||
| } | |||||
| DISTRHO_DECLARE_NON_COPY_CLASS(Thread) | |||||
| }; | |||||
| // ----------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | |||||
| #endif // DISTRHO_THREAD_HPP_INCLUDED | |||||