DISTRHO Plugin Framework
Thread.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2015 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  fName(threadName),
42 #ifdef PTW32_DLLPORT
43  fHandle({nullptr, 0}),
44 #else
45  fHandle(0),
46 #endif
47  fShouldExit(false) {}
48 
49  /*
50  * Destructor.
51  */
52  virtual ~Thread() /*noexcept*/
53  {
54  DISTRHO_SAFE_ASSERT(! isThreadRunning());
55 
56  stopThread(-1);
57  }
58 
59  /*
60  * Virtual function to be implemented by the subclass.
61  */
62  virtual void run() = 0;
63 
64  // -------------------------------------------------------------------
65 
66 public:
67  /*
68  * Check if the thread is running.
69  */
70  bool isThreadRunning() const noexcept
71  {
72 #ifdef PTW32_DLLPORT
73  return (fHandle.p != nullptr);
74 #else
75  return (fHandle != 0);
76 #endif
77  }
78 
79  /*
80  * Check if the thread should exit.
81  */
82  bool shouldThreadExit() const noexcept
83  {
84  return fShouldExit;
85  }
86 
87  /*
88  * Start the thread.
89  */
90  bool startThread() noexcept
91  {
92  // check if already running
93  DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true);
94 
95  const MutexLocker cml(fLock);
96 
97  fShouldExit = false;
98 
99  pthread_t handle;
100 
101  if (pthread_create(&handle, nullptr, _entryPoint, this) == 0)
102  {
103 #ifdef PTW32_DLLPORT
104  DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
105 #else
106  DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
107 #endif
108  pthread_detach(handle);
109  _copyFrom(handle);
110 
111  // wait for thread to start
112  fLock.lock();
113 
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 cml(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  const String fName; // Thread name
214  volatile pthread_t fHandle; // Handle for this thread
215  volatile bool fShouldExit; // true if thread should exit
216 
217  /*
218  * Init pthread type.
219  */
220  void _init() noexcept
221  {
222 #ifdef PTW32_DLLPORT
223  fHandle.p = nullptr;
224  fHandle.x = 0;
225 #else
226  fHandle = 0;
227 #endif
228  }
229 
230  /*
231  * Copy our pthread type from another var.
232  */
233  void _copyFrom(const pthread_t& handle) noexcept
234  {
235 #ifdef PTW32_DLLPORT
236  fHandle.p = handle.p;
237  fHandle.x = handle.x;
238 #else
239  fHandle = handle;
240 #endif
241  }
242 
243  /*
244  * Copy our pthread type to another var.
245  */
246  void _copyTo(volatile pthread_t& handle) const noexcept
247  {
248 #ifdef PTW32_DLLPORT
249  handle.p = fHandle.p;
250  handle.x = fHandle.x;
251 #else
252  handle = fHandle;
253 #endif
254  }
255 
256  /*
257  * Thread entry point.
258  */
259  void _runEntryPoint() noexcept
260  {
261  // report ready
262  fLock.unlock();
263 
264  setCurrentThreadName(fName);
265 
266  try {
267  run();
268  } catch(...) {}
269 
270  // done
271  _init();
272  }
273 
274  /*
275  * Thread entry point.
276  */
277  static void* _entryPoint(void* userData) noexcept
278  {
279  static_cast<Thread*>(userData)->_runEntryPoint();
280  return nullptr;
281  }
282 
283  DISTRHO_DECLARE_NON_COPY_CLASS(Thread)
284 };
285 
286 // -----------------------------------------------------------------------
287 
288 END_NAMESPACE_DISTRHO
289 
290 #endif // DISTRHO_THREAD_HPP_INCLUDED
Definition: Mutex.hpp:177
Definition: Mutex.hpp:34
Definition: String.hpp:27
Definition: Thread.hpp:33