Mercurial > hg > pub > prymula > com
diff DPF-Prymula-audioplugins/dpf/distrho/extra/Thread.hpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DPF-Prymula-audioplugins/dpf/distrho/extra/Thread.hpp Mon Oct 16 21:53:34 2023 +0200 @@ -0,0 +1,344 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 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 "Mutex.hpp" +#include "Sleep.hpp" +#include "String.hpp" + +#ifdef DISTRHO_OS_LINUX +# include <sys/prctl.h> +#endif + +#ifdef DISTRHO_OS_WASM +# error Threads do not work under wasm! +#endif + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- +// Thread class + +class Thread +{ +protected: + /* + * Constructor. + */ + Thread(const char* const threadName = nullptr) noexcept + : fLock(), + fSignal(), + 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(const bool withRealtimePriority = false) noexcept + { + // check if already running + DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true); + + pthread_t handle; + + pthread_attr_t attr; + pthread_attr_init(&attr); + + struct sched_param sched_param = {}; + + if (withRealtimePriority) + { + #ifdef __MOD_DEVICES__ + int rtprio; + const char* const srtprio = std::getenv("MOD_PLUGIN_THREAD_PRIORITY"); + if (srtprio != nullptr && (rtprio = std::atoi(srtprio)) > 0) + sched_param.sched_priority = rtprio - 1; + else + #endif + sched_param.sched_priority = 80; + + #ifndef DISTRHO_OS_HAIKU + if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 && + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 && + #ifndef DISTRHO_OS_WINDOWS + (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 || + pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) && + #endif + pthread_attr_setschedparam(&attr, &sched_param) == 0) + { + d_stdout("Thread setup with realtime priority successful"); + } + else + #endif + { + d_stdout("Thread setup with realtime priority failed, going with normal priority instead"); + pthread_attr_destroy(&attr); + pthread_attr_init(&attr); + } + } + + const MutexLocker ml(fLock); + + fShouldExit = false; + + bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; + pthread_attr_destroy(&attr); + + if (withRealtimePriority && !ok) + { + d_stdout("Thread with realtime priority failed on creation, going with normal priority instead"); + pthread_attr_init(&attr); + ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; + pthread_attr_destroy(&attr); + } + + DISTRHO_SAFE_ASSERT_RETURN(ok, false); + #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 + fSignal.wait(); + return true; + } + + /* + * Stop the thread. + * In the 'timeOutMilliseconds': + * = 0 -> no wait + * > 0 -> wait timeout value + * < 0 -> wait forever + */ + bool stopThread(const int timeOutMilliseconds) noexcept + { + const MutexLocker ml(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("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(); + + pthread_detach(threadId); + 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 String& getThreadName() const noexcept + { + return fName; + } + + /* + * Returns the Id/handle of the thread. + */ + pthread_t getThreadId() const noexcept + { + return fHandle; + } + + /* + * Changes the name of the caller thread. + */ + static void setCurrentThreadName(const char* const name) noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); + + #ifdef DISTRHO_OS_LINUX + prctl(PR_SET_NAME, name, 0, 0, 0); + #endif + #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD) + pthread_setname_np(pthread_self(), name); + #endif + } + + // ------------------------------------------------------------------- + +private: + Mutex fLock; // Thread lock + Signal fSignal; // Thread start wait signal + const 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 + { + if (fName.isNotEmpty()) + setCurrentThreadName(fName); + + // report ready + fSignal.signal(); + + try { + run(); + } catch(...) {} + + // done + _init(); + } + + /* + * Thread entry point. + */ + static void* _entryPoint(void* userData) noexcept + { + static_cast<Thread*>(userData)->_runEntryPoint(); + return nullptr; + } + + DISTRHO_DECLARE_NON_COPYABLE(Thread) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_THREAD_HPP_INCLUDED