1 /* 2 * DISTRHO Plugin Framework (DPF) 3 * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any purpose with 6 * or without fee is hereby granted, provided that the above copyright notice and this 7 * permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN 11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #ifndef DISTRHO_THREAD_HPP_INCLUDED 18 #define DISTRHO_THREAD_HPP_INCLUDED 19 20 #include "Mutex.hpp" 21 #include "Sleep.hpp" 22 #include "String.hpp" 23 24 #ifdef DISTRHO_OS_LINUX 25 # include <sys/prctl.h> 26 #endif 27 28 START_NAMESPACE_DISTRHO 29 30 // ----------------------------------------------------------------------- 31 // Thread class 32 33 class Thread 34 { 35 protected: 36 /* 37 * Constructor. 38 */ Thread(const char * const threadName=nullptr)39 Thread(const char* const threadName = nullptr) noexcept 40 : fLock(), 41 fSignal(), 42 fName(threadName), 43 #ifdef PTW32_DLLPORT 44 fHandle({nullptr, 0}), 45 #else 46 fHandle(0), 47 #endif 48 fShouldExit(false) {} 49 50 /* 51 * Destructor. 52 */ ~Thread()53 virtual ~Thread() /*noexcept*/ 54 { 55 DISTRHO_SAFE_ASSERT(! isThreadRunning()); 56 57 stopThread(-1); 58 } 59 60 /* 61 * Virtual function to be implemented by the subclass. 62 */ 63 virtual void run() = 0; 64 65 // ------------------------------------------------------------------- 66 67 public: 68 /* 69 * Check if the thread is running. 70 */ isThreadRunning() const71 bool isThreadRunning() const noexcept 72 { 73 #ifdef PTW32_DLLPORT 74 return (fHandle.p != nullptr); 75 #else 76 return (fHandle != 0); 77 #endif 78 } 79 80 /* 81 * Check if the thread should exit. 82 */ shouldThreadExit() const83 bool shouldThreadExit() const noexcept 84 { 85 return fShouldExit; 86 } 87 88 /* 89 * Start the thread. 90 */ startThread()91 bool startThread() noexcept 92 { 93 // check if already running 94 DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true); 95 96 const MutexLocker ml(fLock); 97 98 fShouldExit = false; 99 100 pthread_t handle; 101 102 if (pthread_create(&handle, nullptr, _entryPoint, this) == 0) 103 { 104 #ifdef PTW32_DLLPORT 105 DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false); 106 #else 107 DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false); 108 #endif 109 pthread_detach(handle); 110 _copyFrom(handle); 111 112 // wait for thread to start 113 fSignal.wait(); 114 return true; 115 } 116 117 return false; 118 } 119 120 /* 121 * Stop the thread. 122 * In the 'timeOutMilliseconds': 123 * = 0 -> no wait 124 * > 0 -> wait timeout value 125 * < 0 -> wait forever 126 */ stopThread(const int timeOutMilliseconds)127 bool stopThread(const int timeOutMilliseconds) noexcept 128 { 129 const MutexLocker ml(fLock); 130 131 if (isThreadRunning()) 132 { 133 signalThreadShouldExit(); 134 135 if (timeOutMilliseconds != 0) 136 { 137 // Wait for the thread to stop 138 int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2; 139 140 for (; isThreadRunning();) 141 { 142 d_msleep(2); 143 144 if (timeOutCheck < 0) 145 continue; 146 147 if (timeOutCheck > 0) 148 timeOutCheck -= 1; 149 else 150 break; 151 } 152 } 153 154 if (isThreadRunning()) 155 { 156 // should never happen! 157 d_stderr2("assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__); 158 159 // copy thread id so we can clear our one 160 pthread_t threadId; 161 _copyTo(threadId); 162 _init(); 163 164 try { 165 pthread_cancel(threadId); 166 } DISTRHO_SAFE_EXCEPTION("pthread_cancel"); 167 168 return false; 169 } 170 } 171 172 return true; 173 } 174 175 /* 176 * Tell the thread to stop as soon as possible. 177 */ signalThreadShouldExit()178 void signalThreadShouldExit() noexcept 179 { 180 fShouldExit = true; 181 } 182 183 // ------------------------------------------------------------------- 184 185 /* 186 * Returns the name of the thread. 187 * This is the name that gets set in the constructor. 188 */ getThreadName() const189 const String& getThreadName() const noexcept 190 { 191 return fName; 192 } 193 194 /* 195 * Changes the name of the caller thread. 196 */ setCurrentThreadName(const char * const name)197 static void setCurrentThreadName(const char* const name) noexcept 198 { 199 DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); 200 201 #ifdef DISTRHO_OS_LINUX 202 prctl(PR_SET_NAME, name, 0, 0, 0); 203 #endif 204 #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 205 pthread_setname_np(pthread_self(), name); 206 #endif 207 } 208 209 // ------------------------------------------------------------------- 210 211 private: 212 Mutex fLock; // Thread lock 213 Signal fSignal; // Thread start wait signal 214 const String fName; // Thread name 215 volatile pthread_t fHandle; // Handle for this thread 216 volatile bool fShouldExit; // true if thread should exit 217 218 /* 219 * Init pthread type. 220 */ _init()221 void _init() noexcept 222 { 223 #ifdef PTW32_DLLPORT 224 fHandle.p = nullptr; 225 fHandle.x = 0; 226 #else 227 fHandle = 0; 228 #endif 229 } 230 231 /* 232 * Copy our pthread type from another var. 233 */ _copyFrom(const pthread_t & handle)234 void _copyFrom(const pthread_t& handle) noexcept 235 { 236 #ifdef PTW32_DLLPORT 237 fHandle.p = handle.p; 238 fHandle.x = handle.x; 239 #else 240 fHandle = handle; 241 #endif 242 } 243 244 /* 245 * Copy our pthread type to another var. 246 */ _copyTo(volatile pthread_t & handle) const247 void _copyTo(volatile pthread_t& handle) const noexcept 248 { 249 #ifdef PTW32_DLLPORT 250 handle.p = fHandle.p; 251 handle.x = fHandle.x; 252 #else 253 handle = fHandle; 254 #endif 255 } 256 257 /* 258 * Thread entry point. 259 */ _runEntryPoint()260 void _runEntryPoint() noexcept 261 { 262 setCurrentThreadName(fName); 263 264 // report ready 265 fSignal.signal(); 266 267 try { 268 run(); 269 } catch(...) {} 270 271 // done 272 _init(); 273 } 274 275 /* 276 * Thread entry point. 277 */ _entryPoint(void * userData)278 static void* _entryPoint(void* userData) noexcept 279 { 280 static_cast<Thread*>(userData)->_runEntryPoint(); 281 return nullptr; 282 } 283 284 DISTRHO_DECLARE_NON_COPY_CLASS(Thread) 285 }; 286 287 // ----------------------------------------------------------------------- 288 289 END_NAMESPACE_DISTRHO 290 291 #endif // DISTRHO_THREAD_HPP_INCLUDED 292