|
|
@@ -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 |