diff --git a/distrho/extra/d_thread.hpp b/distrho/extra/d_thread.hpp new file mode 100644 index 00000000..9a7b2a9d --- /dev/null +++ b/distrho/extra/d_thread.hpp @@ -0,0 +1,291 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2014 Filipe Coelho + * + * 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 +#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(userData)->_runEntryPoint(); + return nullptr; + } + + DISTRHO_DECLARE_NON_COPY_CLASS(Thread) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_THREAD_HPP_INCLUDED