/[svn]/linuxsampler/trunk/src/common/Thread.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/common/Thread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3290 - (show annotations) (download)
Fri Jun 23 12:24:58 2017 UTC (6 years, 10 months ago) by schoenebeck
File size: 17163 byte(s)
* Revised fundamental C++ classes "Thread", "Mutex" and
  "Condition" which fixes potential undefined behavior
  (note: this addresses mainly the POSIX implementation,
   Win32 is untested yet and would also need an update).
* Bumped version (2.0.0.svn64).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2017 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include "Thread.h"
25
26 #if HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #if DEBUG
31 # include <assert.h>
32 #endif
33
34 // this is the minimum stack size a thread will be spawned with
35 // if this value is too small, the OS will allocate memory on demand and
36 // thus might lead to dropouts in realtime threads
37 // TODO: should be up for testing to get a reasonable good value
38 #define MIN_STACK_SIZE 524288
39
40 namespace LinuxSampler {
41
42 Thread::Thread(bool LockMemory, bool RealTime, int PriorityMax, int PriorityDelta) {
43 this->bLockedMemory = LockMemory;
44 this->isRealTime = RealTime;
45 this->PriorityDelta = PriorityDelta;
46 this->PriorityMax = PriorityMax;
47 this->state = NOT_RUNNING;
48 #if defined(WIN32)
49 # if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
50 win32isRunning = false;
51 # endif
52 #else
53 __thread_destructor_key = 0;
54 pthread_attr_init(&__thread_attr);
55 #endif
56 }
57
58 Thread::~Thread() {
59 // The thread must no longer be running at this point, otherwise it is an
60 // error (we should avoid an implied call of StopThread() in the destructor,
61 // because trying to do so might cause undefined behavior).
62 #if DEBUG
63 assert(!RunningCondition.GetUnsafe());
64 #else
65 if (RunningCondition.GetUnsafe()) {
66 std::cerr << "WARNING: Thread destructed while still running!\n" << std::flush;
67 StopThread();
68 }
69 #endif
70 #if !defined(WIN32)
71 pthread_attr_destroy(&__thread_attr);
72 #endif
73 }
74
75 /**
76 * Starts the thread synchronously. This method will block until the thread
77 * actually started it's execution before it will return. The abstract method
78 * Main() is the entry point for the new thread. You have to implement the
79 * Main() method in your subclass.
80 *
81 * If this thread is already running when this method is called, then this
82 * method will detect this and return accordingly without further actions.
83 *
84 * @returns 0 on success, any other value if thread could not be launched
85 */
86 int Thread::StartThread() {
87 int res = -1;
88 #if defined (WIN32_SIGNALSTARTTHREAD_WORKAROUND)
89 // poll the win32isRunning variable and sleep 1msec inbetween
90 if(!win32isRunning) {
91 res = SignalStartThread();
92 if (res == 0) {
93 while (true) {
94 Sleep(1);
95 if (win32isRunning) break;
96 }
97 }
98 } else res = 0;
99 #else
100 LockGuard g(RunningCondition);
101 // If the thread terminated on its own (i.e. returned from Main()) without
102 // any thread calling StopThread() yet, then the OS blocks termination of
103 // the thread waiting for a pthread_join() call. So we must detach the
104 // thread in this case, because otherwise it will cause a thread leak.
105 if (state == PENDING_JOIN) {
106 state = DETACHED;
107 #if !defined(WIN32)
108 pthread_detach(__thread_id);
109 #endif
110 }
111 if (!RunningCondition.GetUnsafe()) {
112 res = SignalStartThread();
113 // if thread was triggered successfully, wait until thread actually
114 // started execution
115 if (res == 0)
116 RunningCondition.PreLockedWaitIf(false);
117 } else {
118 res = 0;
119 }
120 #endif
121 return res;
122 }
123
124 /**
125 * Starts the thread. This method will signal to start the thread and
126 * return immediately. Note that the thread might not yet run when this
127 * method returns! The abstract method Main() is the entry point for the
128 * new thread. You have to implement the Main() method in your subclass.
129 *
130 * @b IMPORTANT: Calling this method assumes that this thread is not yet
131 * running! Calling this method if the thread is already running causes
132 * undefined behavior!
133 *
134 * @see StartThread()
135 */
136 int Thread::SignalStartThread() {
137 state = RUNNING;
138 #if defined(WIN32)
139 LPVOID lpParameter;
140 hThread = CreateThread(
141 NULL, // no security attributes
142 MIN_STACK_SIZE,
143 win32threadLauncher,
144 this,
145 0,
146 &lpThreadId);
147 if(hThread == NULL) {
148 std::cerr << "Thread creation failed: Error" << GetLastError() << std::endl << std::flush;
149 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
150 win32isRunning = false;
151 #else
152 RunningCondition.Set(false);
153 #endif
154 return -1;
155 }
156 return 0;
157 #else
158 // prepare the thread properties
159 int res = pthread_attr_setinheritsched(&__thread_attr, PTHREAD_EXPLICIT_SCHED);
160 if (res) {
161 std::cerr << "Thread creation failed: Could not inherit thread properties."
162 << std::endl << std::flush;
163 RunningCondition.Set(false);
164 return res;
165 }
166 res = pthread_attr_setdetachstate(&__thread_attr, PTHREAD_CREATE_JOINABLE);
167 if (res) {
168 std::cerr << "Thread creation failed: Could not request a joinable thread."
169 << std::endl << std::flush;
170 RunningCondition.Set(false);
171 return res;
172 }
173 res = pthread_attr_setscope(&__thread_attr, PTHREAD_SCOPE_SYSTEM);
174 if (res) {
175 std::cerr << "Thread creation failed: Could not request system scope for thread scheduling."
176 << std::endl << std::flush;
177 RunningCondition.Set(false);
178 return res;
179 }
180 res = pthread_attr_setstacksize(&__thread_attr, MIN_STACK_SIZE);
181 if (res) {
182 std::cerr << "Thread creation failed: Could not set minimum stack size."
183 << std::endl << std::flush;
184 RunningCondition.Set(false);
185 return res;
186 }
187
188 // Create and run the thread
189 res = pthread_create(&this->__thread_id, &__thread_attr, pthreadLauncher, this);
190 switch (res) {
191 case 0: // Success
192 break;
193 case EAGAIN:
194 std::cerr << "Thread creation failed: System doesn't allow to create another thread."
195 << std::endl << std::flush;
196 RunningCondition.Set(false);
197 break;
198 case EPERM:
199 std::cerr << "Thread creation failed: You're lacking permisssions to set required scheduling policy and parameters."
200 << std::endl << std::flush;
201 RunningCondition.Set(false);
202 break;
203 default:
204 std::cerr << "Thread creation failed: Unknown cause."
205 << std::endl << std::flush;
206 RunningCondition.Set(false);
207 break;
208 }
209 return res;
210 #endif
211 }
212
213 /**
214 * Stops the thread synchronously. This method will block until the thread
215 * actually stopped its execution before it will return from this method.
216 *
217 * If the thread is not running when calling this method, this will be detected
218 * and the call will be ignored. So it is safe to call this method both if the
219 * thread never started, as well as if the thread has already been stopped. And
220 * in fact you should explicitly call StopThread() before the Thread object is
221 * going to be destructured!
222 *
223 * @see SignalStopThread()
224 */
225 int Thread::StopThread() {
226 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
227 SignalStopThread();
228 win32isRunning = false;
229 return 0;
230 #else
231 // LockGuard cannot be used here, because this is a bit more tricky here
232 RunningCondition.Lock();
233 #if !defined(WIN32)
234 // if thread was calling StopThread() on itself
235 if (pthread_equal(__thread_id, pthread_self())) {
236 RunningCondition.PreLockedSet(false);
237 state = DETACHED;
238 pthread_detach(__thread_id);
239 RunningCondition.Unlock();
240 pthread_exit(NULL);
241 }
242 #endif
243 // if we are here, then any other thread called StopThread() but not the thread itself
244 if (RunningCondition.GetUnsafe()) {
245 SignalStopThread();
246 // wait until thread stopped execution
247 RunningCondition.PreLockedWaitAndUnlockIf(true);
248 #if !defined(WIN32)
249 pthread_join(__thread_id, NULL);
250 #endif
251 RunningCondition.Lock();
252 }
253 // If the thread terminated on its own (i.e. returned from Main()) without
254 // any thread calling StopThread() yet, then the OS blocks termination of
255 // the thread waiting for a pthread_join() call. So we must detach the
256 // thread in this case, because otherwise it will cause a thread leak.
257 if (state == PENDING_JOIN) {
258 state = DETACHED;
259 #if !defined(WIN32)
260 pthread_detach(__thread_id);
261 #endif
262 }
263 RunningCondition.Unlock();
264 return 0;
265 #endif
266 }
267
268 /**
269 * Stops the thread asynchronously. This method will signal to stop the thread
270 * and return immediately. Note that due to this the thread might still run
271 * when this method returns!
272 *
273 * @b IMPORTANT: You @ MUST still call StopThread() before destructing the
274 * Thread object, even if you called SignalStopThread() before and the thread
275 * is no longer running! Otherwise this may lead to a thread leak!
276 *
277 * @see StopThread()
278 */
279 int Thread::SignalStopThread() {
280 //FIXME: segfaults when thread is not yet running
281 #if defined(WIN32)
282 BOOL res;
283 res = TerminateThread(hThread, 0); // we set ExitCode to 0
284 //res = WaitForSingleObject( hThread, INFINITE);
285 //myprint(("Thread::SignalStopThread: WaitForSingleObject( hThread, INFINITE) res=%d\n",res));
286 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
287 win32isRunning = false;
288 #else
289 RunningCondition.Set(false);
290 #endif
291 #else
292 pthread_cancel(__thread_id);
293 #endif
294 return 0;
295 }
296
297 /**
298 * Returns @c true in case the thread is currently running. This method does not
299 * block and returns immediately.
300 *
301 * Note that no synchronization is performed when calling this method. So the
302 * returned result is a very volatile information which must be processed with
303 * precautions, that is it may not be used for code that might cause a race
304 * condition.
305 */
306 bool Thread::IsRunning() {
307 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
308 return win32isRunning;
309 #else
310 return RunningCondition.GetUnsafe();
311 #endif
312 }
313
314 /**
315 * Sets the process SCHED_FIFO policy, if max=1 then set at max priority,
316 * else use min priority. delta is added to the priority so that we can
317 * for example set 3 SCHED_FIFO tasks to different priorities by specifying
318 * delta 0 , -1 , -2 ( 0 = highest priority because -1 is subtracted to the
319 * current priority).
320 */
321 int Thread::SetSchedulingPriority() {
322 #if defined(WIN32)
323 DWORD dwPriorityClass;
324 int nPriority;
325
326 if(isRealTime) {
327 dwPriorityClass = REALTIME_PRIORITY_CLASS;
328 if (this->PriorityMax == 1) {
329 if(this->PriorityDelta == 0) nPriority = THREAD_PRIORITY_TIME_CRITICAL;
330 else nPriority = 7 + this->PriorityDelta;
331 }
332 else nPriority = THREAD_PRIORITY_NORMAL + this->PriorityDelta;
333 }
334 else {
335 dwPriorityClass = NORMAL_PRIORITY_CLASS;
336 nPriority = THREAD_PRIORITY_NORMAL + this->PriorityDelta;
337 }
338
339 BOOL res;
340 // FIXME: priority class (realtime) does not work yet, gives error. check why.
341 #if 0
342 res = SetPriorityClass( hThread, dwPriorityClass );
343 if(res == false) {
344 std::cerr << "Thread: WARNING, setPriorityClass " << dwPriorityClass << "failed. Error " << GetLastError() << "\n";
345 return -1;
346 }
347
348 res = SetThreadPriority( hThread, nPriority );
349 if(res == false) {
350 std::cerr << "Thread: WARNING, setThreadPriority " << nPriority << "failed. Error " << GetLastError() << "\n";
351 return -1;
352 }
353 #endif
354 return 0;
355 #else
356 #if !defined(__APPLE__)
357 int policy;
358 const char* policyDescription = NULL;
359 if (isRealTime) { // becomes a RT thread
360 policy = SCHED_FIFO;
361 policyDescription = "realtime";
362 } else { // 'normal', non-RT thread
363 policy = SCHED_OTHER;
364 policyDescription = "normal (non-RT)";
365 }
366 // set selected scheduling policy and priority
367 struct sched_param schp;
368 memset(&schp, 0, sizeof(schp));
369 if (isRealTime) { // it is not possible to change priority for the SCHED_OTHER policy
370 if (this->PriorityMax == 1) {
371 schp.sched_priority = sched_get_priority_max(policy) + this->PriorityDelta;
372 }
373 if (this->PriorityMax == -1) {
374 schp.sched_priority = sched_get_priority_min(policy) + this->PriorityDelta;
375 }
376 }
377 if (pthread_setschedparam(__thread_id, policy, &schp) != 0) {
378 std::cerr << "Thread: WARNING, can't assign "
379 << policyDescription
380 << " scheduling to thread!"
381 << std::endl << std::flush;
382 return -1;
383 }
384 #endif
385 return 0;
386 #endif
387 }
388
389 /**
390 * Locks the memory so it will not be swapped out by the operating system.
391 */
392 int Thread::LockMemory() {
393 #if defined(WIN32)
394 return 0;
395 #else
396 #if !defined(__APPLE__)
397 if (!bLockedMemory) return 0;
398 if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0) {
399 std::cerr << "Thread: WARNING, can't mlockall() memory!\n"
400 << std::flush;
401 return -1;
402 }
403 #endif
404 return 0;
405 #endif
406 }
407
408 /**
409 * Registers thread destructor callback function which will be executed when
410 * the thread stops it's execution and sets the 'Running' flag to true. This
411 * method will be called by the pthreadLauncher callback function, DO NOT
412 * CALL THIS METHOD YOURSELF!
413 */
414 void Thread::EnableDestructor() {
415 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
416 win32isRunning = true;
417 return;
418 #endif
419 LockGuard g(RunningCondition);
420 #if !defined(WIN32)
421 pthread_key_create(&__thread_destructor_key, pthreadDestructor);
422 pthread_setspecific(__thread_destructor_key, this);
423 #endif
424 RunningCondition.PreLockedSet(true);
425 }
426
427 /**
428 * May be overridden by deriving classes to add additional custom cleanup
429 * code if necessary for the event when thread terminates. Currently this
430 * default implementation does nothing.
431 */
432 int Thread::onThreadEnd() {
433 return 0;
434 }
435
436 void Thread::TestCancel() {
437 #if !defined(WIN32)
438 pthread_testcancel();
439 #endif
440 }
441
442 #if defined(WIN32)
443 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
444 // make sure stack is 16-byte aligned for SSE instructions
445 __attribute__((force_align_arg_pointer))
446 #endif
447 DWORD WINAPI Thread::win32threadLauncher(LPVOID lpParameter) {
448 Thread* t;
449 t = (Thread*) lpParameter;
450 t->SetSchedulingPriority();
451 t->LockMemory();
452 t->EnableDestructor();
453 t->Main();
454 return 0;
455 }
456 #else
457 /// Callback function for the POSIX thread API
458 void* Thread::pthreadLauncher(void* thread) {
459 #if !CONFIG_PTHREAD_TESTCANCEL
460 // let the thread be killable under any circumstances
461 if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
462 std::cerr << "Thread: WARNING, PTHREAD_CANCEL_ASYNCHRONOUS not supported!\n" << std::flush;
463 }
464 #endif
465 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
466 Thread* t;
467 t = (Thread*) thread;
468 t->SetSchedulingPriority();
469 t->LockMemory();
470 t->EnableDestructor();
471 t->Main();
472 return NULL;
473 }
474 #endif
475
476 #if !defined(WIN32)
477 /// Callback function for the POSIX thread API
478 void Thread::pthreadDestructor(void* thread) {
479 Thread* t;
480 t = (Thread*) thread;
481 LockGuard g(t->RunningCondition);
482 t->onThreadEnd();
483 pthread_key_delete(t->__thread_destructor_key);
484 // inform that thread termination blocks waiting for pthread_join()
485 // (not detaching the thread here already, because otherwise this might lead
486 // to a data race of the vpointer with the Thread object destructor)
487 t->state = PENDING_JOIN;
488 t->RunningCondition.PreLockedSet(false);
489 }
490 #endif
491
492 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC