DISTRHO Plugin Framework
Thread.hpp
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  */
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() 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  */
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  */
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  */
189  const String& getThreadName() const noexcept
190  {
191  return fName;
192  }
193 
194  /*
195  * Changes the name of the caller thread.
196  */
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  */
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  */
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  */
247  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  */
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  */
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
String
Definition: String.hpp:29
Signal
Definition: Mutex.hpp:184
ScopeLocker
Definition: Mutex.hpp:266
Mutex
Definition: Mutex.hpp:36
Thread
Definition: Thread.hpp:33