DISTRHO Plugin Framework
 All Classes Functions Variables Modules Pages
d_thread.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2014 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 "d_mutex.hpp"
21 #include "d_sleep.hpp"
22 #include "d_string.hpp"
23 
24 #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012
25 // has pthread_setname_np
26 #elif defined(DISTRHO_OS_LINUX)
27 # include <sys/prctl.h>
28 #endif
29 
30 START_NAMESPACE_DISTRHO
31 
32 // -----------------------------------------------------------------------
33 // Thread class
34 
35 class Thread
36 {
37 protected:
38  /*
39  * Constructor.
40  */
41  Thread(const char* const threadName = nullptr) noexcept
42  : fLock(),
43  fName(threadName),
44 #ifdef PTW32_DLLPORT
45  fHandle({nullptr, 0}),
46 #else
47  fHandle(0),
48 #endif
49  fShouldExit(false) {}
50 
51  /*
52  * Destructor.
53  */
54  virtual ~Thread() /*noexcept*/
55  {
56  DISTRHO_SAFE_ASSERT(! isThreadRunning());
57 
58  stopThread(-1);
59  }
60 
61  /*
62  * Virtual function to be implemented by the subclass.
63  */
64  virtual void run() = 0;
65 
66  // -------------------------------------------------------------------
67 
68 public:
69  /*
70  * Check if the thread is running.
71  */
72  bool isThreadRunning() const noexcept
73  {
74 #ifdef PTW32_DLLPORT
75  return (fHandle.p != nullptr);
76 #else
77  return (fHandle != 0);
78 #endif
79  }
80 
81  /*
82  * Check if the thread should exit.
83  */
84  bool shouldThreadExit() const noexcept
85  {
86  return fShouldExit;
87  }
88 
89  /*
90  * Start the thread.
91  */
92  bool startThread() noexcept
93  {
94  // check if already running
95  DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true);
96 
97  const MutexLocker cml(fLock);
98 
99  fShouldExit = false;
100 
101  pthread_t handle;
102 
103  if (pthread_create(&handle, nullptr, _entryPoint, this) == 0)
104  {
105 #ifdef PTW32_DLLPORT
106  DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
107 #else
108  DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
109 #endif
110  pthread_detach(handle);
111  _copyFrom(handle);
112 
113  // wait for thread to start
114  fLock.lock();
115 
116  return true;
117  }
118 
119  return false;
120  }
121 
122  /*
123  * Stop the thread.
124  * In the 'timeOutMilliseconds':
125  * = 0 -> no wait
126  * > 0 -> wait timeout value
127  * < 0 -> wait forever
128  */
129  bool stopThread(const int timeOutMilliseconds) noexcept
130  {
131  const MutexLocker cml(fLock);
132 
133  if (isThreadRunning())
134  {
135  signalThreadShouldExit();
136 
137  if (timeOutMilliseconds != 0)
138  {
139  // Wait for the thread to stop
140  int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2;
141 
142  for (; isThreadRunning();)
143  {
144  d_msleep(2);
145 
146  if (timeOutCheck < 0)
147  continue;
148 
149  if (timeOutCheck > 0)
150  timeOutCheck -= 1;
151  else
152  break;
153  }
154  }
155 
156  if (isThreadRunning())
157  {
158  // should never happen!
159  d_stderr2("Carla assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__);
160 
161  // copy thread id so we can clear our one
162  pthread_t threadId;
163  _copyTo(threadId);
164  _init();
165 
166  try {
167  pthread_cancel(threadId);
168  } DISTRHO_SAFE_EXCEPTION("pthread_cancel");
169 
170  return false;
171  }
172  }
173 
174  return true;
175  }
176 
177  /*
178  * Tell the thread to stop as soon as possible.
179  */
180  void signalThreadShouldExit() noexcept
181  {
182  fShouldExit = true;
183  }
184 
185  // -------------------------------------------------------------------
186 
187  /*
188  * Returns the name of the thread.
189  * This is the name that gets set in the constructor.
190  */
191  const d_string& getThreadName() const noexcept
192  {
193  return fName;
194  }
195 
196  /*
197  * Changes the name of the caller thread.
198  */
199  static void setCurrentThreadName(const char* const name) noexcept
200  {
201  DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
202 
203 #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012
204  pthread_setname_np(pthread_self(), name);
205 #elif defined(DISTRHO_OS_LINUX)
206  prctl(PR_SET_NAME, name, 0, 0, 0);
207 #endif
208  }
209 
210  // -------------------------------------------------------------------
211 
212 private:
213  Mutex fLock; // Thread lock
214  const d_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  // report ready
263  fLock.unlock();
264 
265  setCurrentThreadName(fName);
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
Definition: d_mutex.hpp:34
Definition: d_mutex.hpp:171
Definition: d_thread.hpp:35
Definition: d_string.hpp:27