DISTRHO Plugin Framework
Thread.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2021 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  */
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  */
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  */
71  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  */
83  bool shouldThreadExit() const noexcept
84  {
85  return fShouldExit;
86  }
87 
88  /*
89  * Start the thread.
90  */
91  bool startThread(const bool withRealtimePriority = false) noexcept
92  {
93  // check if already running
94  DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true);
95 
96  pthread_t handle;
97 
98  pthread_attr_t attr;
99  pthread_attr_init(&attr);
100 
101  struct sched_param sched_param;
102  std::memset(&sched_param, 0, sizeof(sched_param));
103 
104  if (withRealtimePriority)
105  {
106  sched_param.sched_priority = 80;
107 
108 #ifndef DISTRHO_OS_HAIKU
109  if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 &&
110  pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 &&
111 # ifndef DISTRHO_OS_WINDOWS
112  (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 ||
113  pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) &&
114 # endif
115  pthread_attr_setschedparam(&attr, &sched_param) == 0)
116  {
117  d_stdout("Thread setup with realtime priority successful");
118  }
119  else
120 #endif
121  {
122  d_stdout("Thread setup with realtime priority failed, going with normal priority instead");
123  pthread_attr_destroy(&attr);
124  pthread_attr_init(&attr);
125  }
126  }
127 
128  const MutexLocker ml(fLock);
129 
130  fShouldExit = false;
131 
132  bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
133  pthread_attr_destroy(&attr);
134 
135  if (withRealtimePriority && !ok)
136  {
137  d_stdout("Thread with realtime priority failed on creation, going with normal priority instead");
138  pthread_attr_init(&attr);
139  ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
140  pthread_attr_destroy(&attr);
141  }
142 
143  DISTRHO_SAFE_ASSERT_RETURN(ok, false);
144 #ifdef PTW32_DLLPORT
145  DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
146 #else
147  DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
148 #endif
149  pthread_detach(handle);
150  _copyFrom(handle);
151 
152  // wait for thread to start
153  fSignal.wait();
154  return true;
155  }
156 
157  /*
158  * Stop the thread.
159  * In the 'timeOutMilliseconds':
160  * = 0 -> no wait
161  * > 0 -> wait timeout value
162  * < 0 -> wait forever
163  */
164  bool stopThread(const int timeOutMilliseconds) noexcept
165  {
166  const MutexLocker ml(fLock);
167 
168  if (isThreadRunning())
169  {
170  signalThreadShouldExit();
171 
172  if (timeOutMilliseconds != 0)
173  {
174  // Wait for the thread to stop
175  int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2;
176 
177  for (; isThreadRunning();)
178  {
179  d_msleep(2);
180 
181  if (timeOutCheck < 0)
182  continue;
183 
184  if (timeOutCheck > 0)
185  timeOutCheck -= 1;
186  else
187  break;
188  }
189  }
190 
191  if (isThreadRunning())
192  {
193  // should never happen!
194  d_stderr2("assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__);
195 
196  // copy thread id so we can clear our one
197  pthread_t threadId;
198  _copyTo(threadId);
199  _init();
200 
201  pthread_detach(threadId);
202  return false;
203  }
204  }
205 
206  return true;
207  }
208 
209  /*
210  * Tell the thread to stop as soon as possible.
211  */
212  void signalThreadShouldExit() noexcept
213  {
214  fShouldExit = true;
215  }
216 
217  // -------------------------------------------------------------------
218 
219  /*
220  * Returns the name of the thread.
221  * This is the name that gets set in the constructor.
222  */
223  const String& getThreadName() const noexcept
224  {
225  return fName;
226  }
227 
228  /*
229  * Returns the Id/handle of the thread.
230  */
231  pthread_t getThreadId() const noexcept
232  {
233  return fHandle;
234  }
235 
236  /*
237  * Changes the name of the caller thread.
238  */
239  static void setCurrentThreadName(const char* const name) noexcept
240  {
241  DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
242 
243 #ifdef DISTRHO_OS_LINUX
244  prctl(PR_SET_NAME, name, 0, 0, 0);
245 #endif
246 #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD)
247  pthread_setname_np(pthread_self(), name);
248 #endif
249  }
250 
251  // -------------------------------------------------------------------
252 
253 private:
254  Mutex fLock; // Thread lock
255  Signal fSignal; // Thread start wait signal
256  const String fName; // Thread name
257  volatile pthread_t fHandle; // Handle for this thread
258  volatile bool fShouldExit; // true if thread should exit
259 
260  /*
261  * Init pthread type.
262  */
263  void _init() noexcept
264  {
265 #ifdef PTW32_DLLPORT
266  fHandle.p = nullptr;
267  fHandle.x = 0;
268 #else
269  fHandle = 0;
270 #endif
271  }
272 
273  /*
274  * Copy our pthread type from another var.
275  */
276  void _copyFrom(const pthread_t& handle) noexcept
277  {
278 #ifdef PTW32_DLLPORT
279  fHandle.p = handle.p;
280  fHandle.x = handle.x;
281 #else
282  fHandle = handle;
283 #endif
284  }
285 
286  /*
287  * Copy our pthread type to another var.
288  */
289  void _copyTo(volatile pthread_t& handle) const noexcept
290  {
291 #ifdef PTW32_DLLPORT
292  handle.p = fHandle.p;
293  handle.x = fHandle.x;
294 #else
295  handle = fHandle;
296 #endif
297  }
298 
299  /*
300  * Thread entry point.
301  */
302  void _runEntryPoint() noexcept
303  {
304  if (fName.isNotEmpty())
305  setCurrentThreadName(fName);
306 
307  // report ready
308  fSignal.signal();
309 
310  try {
311  run();
312  } catch(...) {}
313 
314  // done
315  _init();
316  }
317 
318  /*
319  * Thread entry point.
320  */
321  static void* _entryPoint(void* userData) noexcept
322  {
323  static_cast<Thread*>(userData)->_runEntryPoint();
324  return nullptr;
325  }
326 
327  DISTRHO_DECLARE_NON_COPYABLE(Thread)
328 };
329 
330 // -----------------------------------------------------------------------
331 
332 END_NAMESPACE_DISTRHO
333 
334 #endif // DISTRHO_THREAD_HPP_INCLUDED
String
Definition: String.hpp:30
Signal
Definition: Mutex.hpp:183
ScopeLocker
Definition: Mutex.hpp:265
Mutex
Definition: Mutex.hpp:36
Thread
Definition: Thread.hpp:33