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