Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/extra/Thread.hpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2022 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 #ifdef DISTRHO_OS_WASM | |
29 # error Threads do not work under wasm! | |
30 #endif | |
31 | |
32 START_NAMESPACE_DISTRHO | |
33 | |
34 // ----------------------------------------------------------------------- | |
35 // Thread class | |
36 | |
37 class Thread | |
38 { | |
39 protected: | |
40 /* | |
41 * Constructor. | |
42 */ | |
43 Thread(const char* const threadName = nullptr) noexcept | |
44 : fLock(), | |
45 fSignal(), | |
46 fName(threadName), | |
47 #ifdef PTW32_DLLPORT | |
48 fHandle({nullptr, 0}), | |
49 #else | |
50 fHandle(0), | |
51 #endif | |
52 fShouldExit(false) {} | |
53 | |
54 /* | |
55 * Destructor. | |
56 */ | |
57 virtual ~Thread() /*noexcept*/ | |
58 { | |
59 DISTRHO_SAFE_ASSERT(! isThreadRunning()); | |
60 | |
61 stopThread(-1); | |
62 } | |
63 | |
64 /* | |
65 * Virtual function to be implemented by the subclass. | |
66 */ | |
67 virtual void run() = 0; | |
68 | |
69 // ------------------------------------------------------------------- | |
70 | |
71 public: | |
72 /* | |
73 * Check if the thread is running. | |
74 */ | |
75 bool isThreadRunning() const noexcept | |
76 { | |
77 #ifdef PTW32_DLLPORT | |
78 return (fHandle.p != nullptr); | |
79 #else | |
80 return (fHandle != 0); | |
81 #endif | |
82 } | |
83 | |
84 /* | |
85 * Check if the thread should exit. | |
86 */ | |
87 bool shouldThreadExit() const noexcept | |
88 { | |
89 return fShouldExit; | |
90 } | |
91 | |
92 /* | |
93 * Start the thread. | |
94 */ | |
95 bool startThread(const bool withRealtimePriority = false) noexcept | |
96 { | |
97 // check if already running | |
98 DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true); | |
99 | |
100 pthread_t handle; | |
101 | |
102 pthread_attr_t attr; | |
103 pthread_attr_init(&attr); | |
104 | |
105 struct sched_param sched_param = {}; | |
106 | |
107 if (withRealtimePriority) | |
108 { | |
109 #ifdef __MOD_DEVICES__ | |
110 int rtprio; | |
111 const char* const srtprio = std::getenv("MOD_PLUGIN_THREAD_PRIORITY"); | |
112 if (srtprio != nullptr && (rtprio = std::atoi(srtprio)) > 0) | |
113 sched_param.sched_priority = rtprio - 1; | |
114 else | |
115 #endif | |
116 sched_param.sched_priority = 80; | |
117 | |
118 #ifndef DISTRHO_OS_HAIKU | |
119 if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 && | |
120 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 && | |
121 #ifndef DISTRHO_OS_WINDOWS | |
122 (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 || | |
123 pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) && | |
124 #endif | |
125 pthread_attr_setschedparam(&attr, &sched_param) == 0) | |
126 { | |
127 d_stdout("Thread setup with realtime priority successful"); | |
128 } | |
129 else | |
130 #endif | |
131 { | |
132 d_stdout("Thread setup with realtime priority failed, going with normal priority instead"); | |
133 pthread_attr_destroy(&attr); | |
134 pthread_attr_init(&attr); | |
135 } | |
136 } | |
137 | |
138 const MutexLocker ml(fLock); | |
139 | |
140 fShouldExit = false; | |
141 | |
142 bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; | |
143 pthread_attr_destroy(&attr); | |
144 | |
145 if (withRealtimePriority && !ok) | |
146 { | |
147 d_stdout("Thread with realtime priority failed on creation, going with normal priority instead"); | |
148 pthread_attr_init(&attr); | |
149 ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; | |
150 pthread_attr_destroy(&attr); | |
151 } | |
152 | |
153 DISTRHO_SAFE_ASSERT_RETURN(ok, false); | |
154 #ifdef PTW32_DLLPORT | |
155 DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false); | |
156 #else | |
157 DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false); | |
158 #endif | |
159 pthread_detach(handle); | |
160 _copyFrom(handle); | |
161 | |
162 // wait for thread to start | |
163 fSignal.wait(); | |
164 return true; | |
165 } | |
166 | |
167 /* | |
168 * Stop the thread. | |
169 * In the 'timeOutMilliseconds': | |
170 * = 0 -> no wait | |
171 * > 0 -> wait timeout value | |
172 * < 0 -> wait forever | |
173 */ | |
174 bool stopThread(const int timeOutMilliseconds) noexcept | |
175 { | |
176 const MutexLocker ml(fLock); | |
177 | |
178 if (isThreadRunning()) | |
179 { | |
180 signalThreadShouldExit(); | |
181 | |
182 if (timeOutMilliseconds != 0) | |
183 { | |
184 // Wait for the thread to stop | |
185 int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2; | |
186 | |
187 for (; isThreadRunning();) | |
188 { | |
189 d_msleep(2); | |
190 | |
191 if (timeOutCheck < 0) | |
192 continue; | |
193 | |
194 if (timeOutCheck > 0) | |
195 timeOutCheck -= 1; | |
196 else | |
197 break; | |
198 } | |
199 } | |
200 | |
201 if (isThreadRunning()) | |
202 { | |
203 // should never happen! | |
204 d_stderr2("assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__); | |
205 | |
206 // copy thread id so we can clear our one | |
207 pthread_t threadId; | |
208 _copyTo(threadId); | |
209 _init(); | |
210 | |
211 pthread_detach(threadId); | |
212 return false; | |
213 } | |
214 } | |
215 | |
216 return true; | |
217 } | |
218 | |
219 /* | |
220 * Tell the thread to stop as soon as possible. | |
221 */ | |
222 void signalThreadShouldExit() noexcept | |
223 { | |
224 fShouldExit = true; | |
225 } | |
226 | |
227 // ------------------------------------------------------------------- | |
228 | |
229 /* | |
230 * Returns the name of the thread. | |
231 * This is the name that gets set in the constructor. | |
232 */ | |
233 const String& getThreadName() const noexcept | |
234 { | |
235 return fName; | |
236 } | |
237 | |
238 /* | |
239 * Returns the Id/handle of the thread. | |
240 */ | |
241 pthread_t getThreadId() const noexcept | |
242 { | |
243 return fHandle; | |
244 } | |
245 | |
246 /* | |
247 * Changes the name of the caller thread. | |
248 */ | |
249 static void setCurrentThreadName(const char* const name) noexcept | |
250 { | |
251 DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); | |
252 | |
253 #ifdef DISTRHO_OS_LINUX | |
254 prctl(PR_SET_NAME, name, 0, 0, 0); | |
255 #endif | |
256 #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD) | |
257 pthread_setname_np(pthread_self(), name); | |
258 #endif | |
259 } | |
260 | |
261 // ------------------------------------------------------------------- | |
262 | |
263 private: | |
264 Mutex fLock; // Thread lock | |
265 Signal fSignal; // Thread start wait signal | |
266 const String fName; // Thread name | |
267 volatile pthread_t fHandle; // Handle for this thread | |
268 volatile bool fShouldExit; // true if thread should exit | |
269 | |
270 /* | |
271 * Init pthread type. | |
272 */ | |
273 void _init() noexcept | |
274 { | |
275 #ifdef PTW32_DLLPORT | |
276 fHandle.p = nullptr; | |
277 fHandle.x = 0; | |
278 #else | |
279 fHandle = 0; | |
280 #endif | |
281 } | |
282 | |
283 /* | |
284 * Copy our pthread type from another var. | |
285 */ | |
286 void _copyFrom(const pthread_t& handle) noexcept | |
287 { | |
288 #ifdef PTW32_DLLPORT | |
289 fHandle.p = handle.p; | |
290 fHandle.x = handle.x; | |
291 #else | |
292 fHandle = handle; | |
293 #endif | |
294 } | |
295 | |
296 /* | |
297 * Copy our pthread type to another var. | |
298 */ | |
299 void _copyTo(volatile pthread_t& handle) const noexcept | |
300 { | |
301 #ifdef PTW32_DLLPORT | |
302 handle.p = fHandle.p; | |
303 handle.x = fHandle.x; | |
304 #else | |
305 handle = fHandle; | |
306 #endif | |
307 } | |
308 | |
309 /* | |
310 * Thread entry point. | |
311 */ | |
312 void _runEntryPoint() noexcept | |
313 { | |
314 if (fName.isNotEmpty()) | |
315 setCurrentThreadName(fName); | |
316 | |
317 // report ready | |
318 fSignal.signal(); | |
319 | |
320 try { | |
321 run(); | |
322 } catch(...) {} | |
323 | |
324 // done | |
325 _init(); | |
326 } | |
327 | |
328 /* | |
329 * Thread entry point. | |
330 */ | |
331 static void* _entryPoint(void* userData) noexcept | |
332 { | |
333 static_cast<Thread*>(userData)->_runEntryPoint(); | |
334 return nullptr; | |
335 } | |
336 | |
337 DISTRHO_DECLARE_NON_COPYABLE(Thread) | |
338 }; | |
339 | |
340 // ----------------------------------------------------------------------- | |
341 | |
342 END_NAMESPACE_DISTRHO | |
343 | |
344 #endif // DISTRHO_THREAD_HPP_INCLUDED |