/[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 3764 - (show annotations) (download)
Sun Apr 5 21:41:27 2020 UTC (3 years, 11 months ago) by schoenebeck
File size: 21096 byte(s)
Thread class: Added new methods (POSIX implementation only yet):

* Added methods pushCancelable() and popCancelable() for allowing to
  prevent thread being terminated on critical sections.

* Added methods name(), nameOfCaller() and setNameOfCaller() to allow
  assigning threads human readable names for debugging purposes.

* configure: Always check for availability of pthread_testcancel()
  and use it if so (previously it was only used if explicitly enabled
  by configure script option).

* Raise a compiler warning if pthread_testcancel() is not available.

* Bumped version (2.1.1.svn52).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2020 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 #include "global_private.h"
30
31 #include <list>
32 #if DEBUG
33 # include <assert.h>
34 #endif
35
36 #if !CONFIG_PTHREAD_TESTCANCEL
37 # warning No pthread_testcancel() available: this may lead to mutex dead locks when threads are stopped!
38 #endif
39
40 // this is the minimum stack size a thread will be spawned with
41 // if this value is too small, the OS will allocate memory on demand and
42 // thus might lead to dropouts in realtime threads
43 // TODO: should be up for testing to get a reasonable good value
44 #define MIN_STACK_SIZE 524288
45
46 #if !defined(WIN32)
47 static thread_local std::list<int> cancelStates;
48 #endif
49
50 namespace LinuxSampler {
51
52 Thread::Thread(bool LockMemory, bool RealTime, int PriorityMax, int PriorityDelta) {
53 this->bLockedMemory = LockMemory;
54 this->isRealTime = RealTime;
55 this->PriorityDelta = PriorityDelta;
56 this->PriorityMax = PriorityMax;
57 this->state = NOT_RUNNING;
58 #if defined(WIN32)
59 # if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
60 win32isRunning = false;
61 # endif
62 #else
63 __thread_destructor_key = 0;
64 pthread_attr_init(&__thread_attr);
65 #endif
66 }
67
68 Thread::~Thread() {
69 // The thread must no longer be running at this point, otherwise it is an
70 // error (we should avoid an implied call of StopThread() in the destructor,
71 // because trying to do so might cause undefined behavior).
72 #if DEBUG
73 assert(!RunningCondition.GetUnsafe());
74 #else
75 if (RunningCondition.GetUnsafe()) {
76 std::cerr << "WARNING: Thread destructed while still running!\n" << std::flush;
77 StopThread();
78 }
79 #endif
80 #if !defined(WIN32)
81 pthread_attr_destroy(&__thread_attr);
82 #endif
83 }
84
85 /**
86 * Starts the thread synchronously. This method will block until the thread
87 * actually started it's execution before it will return. The abstract method
88 * Main() is the entry point for the new thread. You have to implement the
89 * Main() method in your subclass.
90 *
91 * If this thread is already running when this method is called, then this
92 * method will detect this and return accordingly without further actions.
93 *
94 * @returns 0 on success, any other value if thread could not be launched
95 */
96 int Thread::StartThread() {
97 int res = -1;
98 #if defined (WIN32_SIGNALSTARTTHREAD_WORKAROUND)
99 // poll the win32isRunning variable and sleep 1msec inbetween
100 if(!win32isRunning) {
101 res = SignalStartThread();
102 if (res == 0) {
103 while (true) {
104 Sleep(1);
105 if (win32isRunning) break;
106 }
107 }
108 } else res = 0;
109 #else
110 LockGuard g(RunningCondition);
111 // If the thread terminated on its own (i.e. returned from Main()) without
112 // any thread calling StopThread() yet, then the OS blocks termination of
113 // the thread waiting for a pthread_join() call. So we must detach the
114 // thread in this case, because otherwise it will cause a thread leak.
115 if (state == PENDING_JOIN) {
116 state = DETACHED;
117 #if !defined(WIN32)
118 pthread_detach(__thread_id);
119 #endif
120 }
121 if (!RunningCondition.GetUnsafe()) {
122 res = SignalStartThread();
123 // if thread was triggered successfully, wait until thread actually
124 // started execution
125 if (res == 0)
126 RunningCondition.PreLockedWaitIf(false);
127 } else {
128 res = 0;
129 }
130 #endif
131 return res;
132 }
133
134 /**
135 * Starts the thread. This method will signal to start the thread and
136 * return immediately. Note that the thread might not yet run when this
137 * method returns! The abstract method Main() is the entry point for the
138 * new thread. You have to implement the Main() method in your subclass.
139 *
140 * @b IMPORTANT: Calling this method assumes that this thread is not yet
141 * running! Calling this method if the thread is already running causes
142 * undefined behavior!
143 *
144 * @see StartThread()
145 */
146 int Thread::SignalStartThread() {
147 state = RUNNING;
148 #if defined(WIN32)
149 LPVOID lpParameter;
150 hThread = CreateThread(
151 NULL, // no security attributes
152 MIN_STACK_SIZE,
153 win32threadLauncher,
154 this,
155 0,
156 &lpThreadId);
157 if(hThread == NULL) {
158 std::cerr << "Thread creation failed: Error" << GetLastError() << std::endl << std::flush;
159 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
160 win32isRunning = false;
161 #else
162 RunningCondition.Set(false);
163 #endif
164 return -1;
165 }
166 return 0;
167 #else
168 // prepare the thread properties
169 int res = pthread_attr_setinheritsched(&__thread_attr, PTHREAD_EXPLICIT_SCHED);
170 if (res) {
171 std::cerr << "Thread creation failed: Could not inherit thread properties."
172 << std::endl << std::flush;
173 RunningCondition.Set(false);
174 return res;
175 }
176 res = pthread_attr_setdetachstate(&__thread_attr, PTHREAD_CREATE_JOINABLE);
177 if (res) {
178 std::cerr << "Thread creation failed: Could not request a joinable thread."
179 << std::endl << std::flush;
180 RunningCondition.Set(false);
181 return res;
182 }
183 res = pthread_attr_setscope(&__thread_attr, PTHREAD_SCOPE_SYSTEM);
184 if (res) {
185 std::cerr << "Thread creation failed: Could not request system scope for thread scheduling."
186 << std::endl << std::flush;
187 RunningCondition.Set(false);
188 return res;
189 }
190 res = pthread_attr_setstacksize(&__thread_attr, MIN_STACK_SIZE);
191 if (res) {
192 std::cerr << "Thread creation failed: Could not set minimum stack size."
193 << std::endl << std::flush;
194 RunningCondition.Set(false);
195 return res;
196 }
197
198 // Create and run the thread
199 res = pthread_create(&this->__thread_id, &__thread_attr, pthreadLauncher, this);
200 switch (res) {
201 case 0: // Success
202 break;
203 case EAGAIN:
204 std::cerr << "Thread creation failed: System doesn't allow to create another thread."
205 << std::endl << std::flush;
206 RunningCondition.Set(false);
207 break;
208 case EPERM:
209 std::cerr << "Thread creation failed: You're lacking permisssions to set required scheduling policy and parameters."
210 << std::endl << std::flush;
211 RunningCondition.Set(false);
212 break;
213 default:
214 std::cerr << "Thread creation failed: Unknown cause."
215 << std::endl << std::flush;
216 RunningCondition.Set(false);
217 break;
218 }
219 return res;
220 #endif
221 }
222
223 /**
224 * Stops the thread synchronously. This method will block until the thread
225 * actually stopped its execution before it will return from this method.
226 *
227 * If the thread is not running when calling this method, this will be detected
228 * and the call will be ignored. So it is safe to call this method both if the
229 * thread never started, as well as if the thread has already been stopped. And
230 * in fact you should explicitly call StopThread() before the Thread object is
231 * going to be destructured!
232 *
233 * @see SignalStopThread()
234 */
235 int Thread::StopThread() {
236 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
237 SignalStopThread();
238 win32isRunning = false;
239 return 0;
240 #else
241 // LockGuard cannot be used here, because this is a bit more tricky here
242 RunningCondition.Lock();
243 #if !defined(WIN32)
244 // if thread was calling StopThread() on itself
245 if (pthread_equal(__thread_id, pthread_self())) {
246 RunningCondition.PreLockedSet(false);
247 state = DETACHED;
248 pthread_detach(__thread_id);
249 RunningCondition.Unlock();
250 pthread_exit(NULL);
251 }
252 #endif
253 // if we are here, then any other thread called StopThread() but not the thread itself
254 if (RunningCondition.GetUnsafe()) {
255 SignalStopThread();
256 // wait until thread stopped execution
257 RunningCondition.PreLockedWaitAndUnlockIf(true);
258 #if !defined(WIN32)
259 pthread_join(__thread_id, NULL);
260 #endif
261 RunningCondition.Lock();
262 }
263 // If the thread terminated on its own (i.e. returned from Main()) without
264 // any thread calling StopThread() yet, then the OS blocks termination of
265 // the thread waiting for a pthread_join() call. So we must detach the
266 // thread in this case, because otherwise it will cause a thread leak.
267 if (state == PENDING_JOIN) {
268 state = DETACHED;
269 #if !defined(WIN32)
270 pthread_detach(__thread_id);
271 #endif
272 }
273 RunningCondition.Unlock();
274 return 0;
275 #endif
276 }
277
278 /**
279 * Stops the thread asynchronously. This method will signal to stop the thread
280 * and return immediately. Note that due to this the thread might still run
281 * when this method returns!
282 *
283 * @b IMPORTANT: You @ MUST still call StopThread() before destructing the
284 * Thread object, even if you called SignalStopThread() before and the thread
285 * is no longer running! Otherwise this may lead to a thread leak!
286 *
287 * @see StopThread()
288 */
289 int Thread::SignalStopThread() {
290 //FIXME: segfaults when thread is not yet running
291 #if defined(WIN32)
292 BOOL res;
293 res = TerminateThread(hThread, 0); // we set ExitCode to 0
294 //res = WaitForSingleObject( hThread, INFINITE);
295 //myprint(("Thread::SignalStopThread: WaitForSingleObject( hThread, INFINITE) res=%d\n",res));
296 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
297 win32isRunning = false;
298 #else
299 RunningCondition.Set(false);
300 #endif
301 #else
302 pthread_cancel(__thread_id);
303 #endif
304 return 0;
305 }
306
307 /**
308 * Returns @c true in case the thread is currently running. This method does not
309 * block and returns immediately.
310 *
311 * Note that no synchronization is performed when calling this method. So the
312 * returned result is a very volatile information which must be processed with
313 * precautions, that is it may not be used for code that might cause a race
314 * condition.
315 */
316 bool Thread::IsRunning() {
317 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
318 return win32isRunning;
319 #else
320 return RunningCondition.GetUnsafe();
321 #endif
322 }
323
324 /**
325 * Sets the process SCHED_FIFO policy, if max=1 then set at max priority,
326 * else use min priority. delta is added to the priority so that we can
327 * for example set 3 SCHED_FIFO tasks to different priorities by specifying
328 * delta 0 , -1 , -2 ( 0 = highest priority because -1 is subtracted to the
329 * current priority).
330 */
331 int Thread::SetSchedulingPriority() {
332 #if defined(WIN32)
333 DWORD dwPriorityClass;
334 int nPriority;
335
336 if(isRealTime) {
337 dwPriorityClass = REALTIME_PRIORITY_CLASS;
338 if (this->PriorityMax == 1) {
339 if(this->PriorityDelta == 0) nPriority = THREAD_PRIORITY_TIME_CRITICAL;
340 else nPriority = 7 + this->PriorityDelta;
341 }
342 else nPriority = THREAD_PRIORITY_NORMAL + this->PriorityDelta;
343 }
344 else {
345 dwPriorityClass = NORMAL_PRIORITY_CLASS;
346 nPriority = THREAD_PRIORITY_NORMAL + this->PriorityDelta;
347 }
348
349 BOOL res;
350 // FIXME: priority class (realtime) does not work yet, gives error. check why.
351 #if 0
352 res = SetPriorityClass( hThread, dwPriorityClass );
353 if(res == false) {
354 std::cerr << "Thread: WARNING, setPriorityClass " << dwPriorityClass << "failed. Error " << GetLastError() << "\n";
355 return -1;
356 }
357
358 res = SetThreadPriority( hThread, nPriority );
359 if(res == false) {
360 std::cerr << "Thread: WARNING, setThreadPriority " << nPriority << "failed. Error " << GetLastError() << "\n";
361 return -1;
362 }
363 #endif
364 return 0;
365 #else
366 #if !defined(__APPLE__)
367 int policy;
368 const char* policyDescription = NULL;
369 if (isRealTime) { // becomes a RT thread
370 policy = SCHED_FIFO;
371 policyDescription = "realtime";
372 } else { // 'normal', non-RT thread
373 policy = SCHED_OTHER;
374 policyDescription = "normal (non-RT)";
375 }
376 // set selected scheduling policy and priority
377 struct sched_param schp;
378 memset(&schp, 0, sizeof(schp));
379 if (isRealTime) { // it is not possible to change priority for the SCHED_OTHER policy
380 if (this->PriorityMax == 1) {
381 schp.sched_priority = sched_get_priority_max(policy) + this->PriorityDelta;
382 }
383 if (this->PriorityMax == -1) {
384 schp.sched_priority = sched_get_priority_min(policy) + this->PriorityDelta;
385 }
386 }
387 if (pthread_setschedparam(__thread_id, policy, &schp) != 0) {
388 std::cerr << "Thread: WARNING, can't assign "
389 << policyDescription
390 << " scheduling to thread!"
391 << std::endl << std::flush;
392 return -1;
393 }
394 #endif
395 return 0;
396 #endif
397 }
398
399 /**
400 * Locks the memory so it will not be swapped out by the operating system.
401 */
402 int Thread::LockMemory() {
403 #if defined(WIN32)
404 return 0;
405 #else
406 #if !defined(__APPLE__)
407 if (!bLockedMemory) return 0;
408 if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0) {
409 std::cerr << "Thread: WARNING, can't mlockall() memory!\n"
410 << std::flush;
411 return -1;
412 }
413 #endif
414 return 0;
415 #endif
416 }
417
418 /**
419 * Registers thread destructor callback function which will be executed when
420 * the thread stops it's execution and sets the 'Running' flag to true. This
421 * method will be called by the pthreadLauncher callback function, DO NOT
422 * CALL THIS METHOD YOURSELF!
423 */
424 void Thread::EnableDestructor() {
425 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
426 win32isRunning = true;
427 return;
428 #endif
429 LockGuard g(RunningCondition);
430 #if !defined(WIN32)
431 pthread_key_create(&__thread_destructor_key, pthreadDestructor);
432 pthread_setspecific(__thread_destructor_key, this);
433 #endif
434 RunningCondition.PreLockedSet(true);
435 }
436
437 /**
438 * May be overridden by deriving classes to add additional custom cleanup
439 * code if necessary for the event when thread terminates. Currently this
440 * default implementation does nothing.
441 */
442 int Thread::onThreadEnd() {
443 return 0;
444 }
445
446 void Thread::TestCancel() {
447 #if !defined(WIN32)
448 pthread_testcancel();
449 #endif
450 }
451
452 #if defined(WIN32)
453 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
454 // make sure stack is 16-byte aligned for SSE instructions
455 __attribute__((force_align_arg_pointer))
456 #endif
457 DWORD WINAPI Thread::win32threadLauncher(LPVOID lpParameter) {
458 Thread* t;
459 t = (Thread*) lpParameter;
460 t->SetSchedulingPriority();
461 t->LockMemory();
462 t->EnableDestructor();
463 t->Main();
464 return 0;
465 }
466 #else
467 /// Callback function for the POSIX thread API
468 void* Thread::pthreadLauncher(void* thread) {
469 #if !CONFIG_PTHREAD_TESTCANCEL
470 // let the thread be killable under any circumstances
471 if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
472 std::cerr << "Thread: WARNING, PTHREAD_CANCEL_ASYNCHRONOUS not supported!\n" << std::flush;
473 }
474 #endif
475 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
476 Thread* t;
477 t = (Thread*) thread;
478 t->SetSchedulingPriority();
479 t->LockMemory();
480 t->EnableDestructor();
481 t->Main();
482 return NULL;
483 }
484 #endif
485
486 #if !defined(WIN32)
487 /// Callback function for the POSIX thread API
488 void Thread::pthreadDestructor(void* thread) {
489 Thread* t;
490 t = (Thread*) thread;
491 LockGuard g(t->RunningCondition);
492 t->onThreadEnd();
493 pthread_key_delete(t->__thread_destructor_key);
494 // inform that thread termination blocks waiting for pthread_join()
495 // (not detaching the thread here already, because otherwise this might lead
496 // to a data race of the vpointer with the Thread object destructor)
497 t->state = PENDING_JOIN;
498 t->RunningCondition.PreLockedSet(false);
499 }
500 #endif
501
502 /**
503 * Allow or prohibit whether the calling thread may be cancelled, remember its
504 * previous setting on a stack.
505 *
506 * @discussion The POSIX standard defines certaing system calls as implied
507 * thread cancellation points. For instance when a thread calls @c usleep(), the
508 * thread would immediately terminate if the thread was requested to be stopped.
509 * The problem with this is that a thread typically has critical sections where
510 * it may not simply terminate unexpectedly, e.g. if a thread is currently
511 * holding a mutex lock and then would call @c usleep() this may end up in a
512 * dead lock, since the lock would then never be unlocked again. For that reason
513 * a thread should first disable itself being cancelable before entering a
514 * critical section by calling @c pushCancelable(false) and it should
515 * re-enable thread cancelation by calling @c popCancelable() after having left
516 * the critical section(s). Refer to the following link for a list of functions
517 * being defined as implied cancelation points:
518 * http://man7.org/linux/man-pages/man7/pthreads.7.html
519 *
520 * @b NOTE: This method is currently not implemented for Windows yet!
521 *
522 * @param cancel - @c true: thread may be cancelled, @c false: thread may not
523 * be cancelled until re-enabled with either pushCancelable() or
524 * popCancelable()
525 * @see popCancelable() as counter part
526 */
527 void Thread::pushCancelable(bool cancel) {
528 #if defined(WIN32)
529 //TODO: implementation for Windows
530 #else
531 int old;
532 pthread_setcancelstate(cancel ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, &old);
533 cancelStates.push_back(old);
534 #endif
535 }
536
537 /**
538 * Restore previous cancellation setting of calling thread by restoring it from
539 * stack.
540 *
541 * @b NOTE: This method is currently not implemented for Windows yet!
542 *
543 * @see pushCancelable() for details
544 */
545 void Thread::popCancelable() {
546 #if defined(WIN32)
547 //TODO: implementation for Windows
548 #else
549 int cancel = cancelStates.back();
550 cancelStates.pop_back();
551 pthread_setcancelstate(cancel ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL);
552 #endif
553 }
554
555 /**
556 * Return this thread's name (intended just for debugging purposes).
557 *
558 * @b NOTE: This method is currently not implemented for Windows yet!
559 */
560 std::string Thread::name() {
561 #if defined(WIN32)
562 //TODO: implementation for Windows
563 return "not implemented";
564 #else
565 char buf[16] = {};
566 pthread_getname_np(__thread_id, buf, 16);
567 std::string s = buf;
568 if (s.empty())
569 s = "tid=" + ToString(__thread_id);
570 return s;
571 #endif
572 }
573
574 /**
575 * Return calling thread's name (intended just for debugging purposes).
576 *
577 * @b NOTE: This method is currently not implemented for Windows yet!
578 */
579 std::string Thread::nameOfCaller() {
580 #if defined(WIN32)
581 //TODO: implementation for Windows
582 return "not implemented";
583 #else
584 char buf[16] = {};
585 pthread_getname_np(pthread_self(), buf, 16);
586 std::string s = buf;
587 if (s.empty())
588 s = "tid=" + ToString(pthread_self());
589 return s;
590 #endif
591 }
592
593 /**
594 * Give calling thread a name (intended just for debugging purposes).
595 *
596 * @b NOTE: POSIX defines a limit of max. 16 characters for @a name.
597 *
598 * @b NOTE: This method is currently not implemented for Windows yet!
599 *
600 * @param name - arbitrary, i.e. human readable name for calling thread
601 */
602 void Thread::setNameOfCaller(std::string name) {
603 #if defined(WIN32)
604 //TODO: implementation for Windows
605 #elif __APPLE__
606 pthread_setname_np(name.c_str());
607 #else // Linux, NetBSD, FreeBSD, ...
608 pthread_setname_np(pthread_self(), name.c_str());
609 #endif
610 }
611
612 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC