/[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 3020 - (show annotations) (download)
Sun Oct 23 07:24:09 2016 UTC (7 years, 6 months ago) by persson
File size: 13389 byte(s)
* windows, 32-bit: fixed potential crashes by making sure the stack in
  sub threads is 16-byte aligned

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2011 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 // this is the minimum stack size a thread will be spawned with
31 // if this value is too small, the OS will allocate memory on demand and
32 // thus might lead to dropouts in realtime threads
33 // TODO: should be up for testing to get a reasonable good value
34 #define MIN_STACK_SIZE 524288
35
36 namespace LinuxSampler {
37
38 #if defined(WIN32)
39 // Callback functions for the WIN32 thread API
40 DWORD WINAPI __win32thread_launcher(LPVOID lpParameter);
41 #else
42 // Callback functions for the POSIX thread API
43 static void* __pthread_launcher(void* thread);
44 static void __pthread_destructor(void* thread);
45 #endif
46
47 Thread::Thread(bool LockMemory, bool RealTime, int PriorityMax, int PriorityDelta) {
48 this->bLockedMemory = LockMemory;
49 this->isRealTime = RealTime;
50 this->PriorityDelta = PriorityDelta;
51 this->PriorityMax = PriorityMax;
52 #if defined(WIN32)
53 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
54 win32isRunning = false;
55 #endif
56 #else
57 __thread_destructor_key = 0;
58 pthread_attr_init(&__thread_attr);
59 #endif
60 }
61
62 Thread::~Thread() {
63 StopThread();
64 #if defined(WIN32)
65 #else
66 pthread_attr_destroy(&__thread_attr);
67 #endif
68 }
69
70 /**
71 * Starts the thread. This method will wait until the thread actually
72 * started it's execution before it will return. The abstract method
73 * Main() is the entry point for the new thread. You have to implement the
74 * Main() method in your subclass.
75 */
76 int Thread::StartThread() {
77 #if defined (WIN32_SIGNALSTARTTHREAD_WORKAROUND)
78 // poll the win32isRunning variable and sleep 1msec inbetween
79 if(!win32isRunning) {
80 SignalStartThread();
81 while(1) {
82 Sleep(1);
83 if(win32isRunning) break;
84 }
85 }
86 return 0;
87 #else
88 RunningCondition.Lock();
89 if (!RunningCondition.GetUnsafe()) {
90 SignalStartThread();
91 // wait until thread started execution
92 RunningCondition.WaitIf(false);
93 }
94 RunningCondition.Unlock();
95 return 0;
96 #endif
97 }
98
99 /**
100 * Starts the thread. This method will signal to start the thread and
101 * return immediately. Note that the thread might not yet run when this
102 * method returns! The abstract method Main() is the entry point for the
103 * new thread. You have to implement the Main() method in your subclass.
104 *
105 * @see StartThread()
106 */
107 int Thread::SignalStartThread() {
108 #if defined(WIN32)
109 LPVOID lpParameter;
110 hThread = CreateThread(
111 NULL, // no security attributes
112 MIN_STACK_SIZE,
113 __win32thread_launcher,
114 this,
115 0,
116 &lpThreadId);
117 if(hThread == NULL) {
118 std::cerr << "Thread creation failed: Error" << GetLastError() << std::endl << std::flush;
119 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
120 win32isRunning = false;
121 #else
122 RunningCondition.Set(false);
123 #endif
124 return -1;
125 }
126 return 0;
127 #else
128 // prepare the thread properties
129 int res = pthread_attr_setinheritsched(&__thread_attr, PTHREAD_EXPLICIT_SCHED);
130 if (res) {
131 std::cerr << "Thread creation failed: Could not inherit thread properties."
132 << std::endl << std::flush;
133 RunningCondition.Set(false);
134 return res;
135 }
136 res = pthread_attr_setdetachstate(&__thread_attr, PTHREAD_CREATE_JOINABLE);
137 if (res) {
138 std::cerr << "Thread creation failed: Could not request a joinable thread."
139 << std::endl << std::flush;
140 RunningCondition.Set(false);
141 return res;
142 }
143 res = pthread_attr_setscope(&__thread_attr, PTHREAD_SCOPE_SYSTEM);
144 if (res) {
145 std::cerr << "Thread creation failed: Could not request system scope for thread scheduling."
146 << std::endl << std::flush;
147 RunningCondition.Set(false);
148 return res;
149 }
150 res = pthread_attr_setstacksize(&__thread_attr, MIN_STACK_SIZE);
151 if (res) {
152 std::cerr << "Thread creation failed: Could not set minimum stack size."
153 << std::endl << std::flush;
154 RunningCondition.Set(false);
155 return res;
156 }
157
158 // Create and run the thread
159 res = pthread_create(&this->__thread_id, &__thread_attr, __pthread_launcher, this);
160 switch (res) {
161 case 0: // Success
162 break;
163 case EAGAIN:
164 std::cerr << "Thread creation failed: System doesn't allow to create another thread."
165 << std::endl << std::flush;
166 RunningCondition.Set(false);
167 break;
168 case EPERM:
169 std::cerr << "Thread creation failed: You're lacking permisssions to set required scheduling policy and parameters."
170 << std::endl << std::flush;
171 RunningCondition.Set(false);
172 break;
173 default:
174 std::cerr << "Thread creation failed: Unknown cause."
175 << std::endl << std::flush;
176 RunningCondition.Set(false);
177 break;
178 }
179 return res;
180 #endif
181 }
182
183 /**
184 * Stops the thread. This method will wait until the thread actually stopped
185 * it's execution before it will return.
186 */
187 int Thread::StopThread() {
188 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
189 SignalStopThread();
190 win32isRunning = false;
191 return 0;
192 #endif
193 RunningCondition.Lock();
194 if (RunningCondition.GetUnsafe()) {
195 SignalStopThread();
196 // wait until thread stopped execution
197 RunningCondition.WaitIf(true);
198 #if defined(WIN32)
199 #else
200 pthread_detach(__thread_id);
201 #endif
202 }
203 RunningCondition.Unlock();
204 return 0;
205 }
206
207 /**
208 * Stops the thread. This method will signal to stop the thread and return
209 * immediately. Note that the thread might still run when this method
210 * returns!
211 *
212 * @see StopThread()
213 */
214 int Thread::SignalStopThread() {
215 //FIXME: segfaults when thread is not yet running
216 #if defined(WIN32)
217 BOOL res;
218 res = TerminateThread(hThread, 0); // we set ExitCode to 0
219 //res = WaitForSingleObject( hThread, INFINITE);
220 //myprint(("Thread::SignalStopThread: WaitForSingleObject( hThread, INFINITE) res=%d\n",res));
221 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
222 win32isRunning = false;
223 #else
224 RunningCondition.Set(false);
225 #endif
226 #else
227 pthread_cancel(__thread_id);
228 #endif
229 return 0;
230 }
231
232 /**
233 * Returns @c true in case the thread is currently running.
234 */
235 bool Thread::IsRunning() {
236 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
237 return win32isRunning;
238 #else
239 return RunningCondition.GetUnsafe();
240 #endif
241 }
242
243 /**
244 * Sets the process SCHED_FIFO policy, if max=1 then set at max priority,
245 * else use min priority. delta is added to the priority so that we can
246 * for example set 3 SCHED_FIFO tasks to different priorities by specifying
247 * delta 0 , -1 , -2 ( 0 = highest priority because -1 is subtracted to the
248 * current priority).
249 */
250 int Thread::SetSchedulingPriority() {
251 #if defined(WIN32)
252 DWORD dwPriorityClass;
253 int nPriority;
254
255 if(isRealTime) {
256 dwPriorityClass = REALTIME_PRIORITY_CLASS;
257 if (this->PriorityMax == 1) {
258 if(this->PriorityDelta == 0) nPriority = THREAD_PRIORITY_TIME_CRITICAL;
259 else nPriority = 7 + this->PriorityDelta;
260 }
261 else nPriority = THREAD_PRIORITY_NORMAL + this->PriorityDelta;
262 }
263 else {
264 dwPriorityClass = NORMAL_PRIORITY_CLASS;
265 nPriority = THREAD_PRIORITY_NORMAL + this->PriorityDelta;
266 }
267
268 BOOL res;
269 // FIXME: priority class (realtime) does not work yet, gives error. check why.
270 #if 0
271 res = SetPriorityClass( hThread, dwPriorityClass );
272 if(res == false) {
273 std::cerr << "Thread: WARNING, setPriorityClass " << dwPriorityClass << "failed. Error " << GetLastError() << "\n";
274 return -1;
275 }
276
277 res = SetThreadPriority( hThread, nPriority );
278 if(res == false) {
279 std::cerr << "Thread: WARNING, setThreadPriority " << nPriority << "failed. Error " << GetLastError() << "\n";
280 return -1;
281 }
282 #endif
283 return 0;
284 #else
285 #if !defined(__APPLE__)
286 int policy;
287 const char* policyDescription = NULL;
288 if (isRealTime) { // becomes a RT thread
289 policy = SCHED_FIFO;
290 policyDescription = "realtime";
291 } else { // 'normal', non-RT thread
292 policy = SCHED_OTHER;
293 policyDescription = "normal (non-RT)";
294 }
295 // set selected scheduling policy and priority
296 struct sched_param schp;
297 memset(&schp, 0, sizeof(schp));
298 if (isRealTime) { // it is not possible to change priority for the SCHED_OTHER policy
299 if (this->PriorityMax == 1) {
300 schp.sched_priority = sched_get_priority_max(policy) + this->PriorityDelta;
301 }
302 if (this->PriorityMax == -1) {
303 schp.sched_priority = sched_get_priority_min(policy) + this->PriorityDelta;
304 }
305 }
306 if (pthread_setschedparam(__thread_id, policy, &schp) != 0) {
307 std::cerr << "Thread: WARNING, can't assign "
308 << policyDescription
309 << " scheduling to thread!"
310 << std::endl << std::flush;
311 return -1;
312 }
313 #endif
314 return 0;
315 #endif
316 }
317
318 /**
319 * Locks the memory so it will not be swapped out by the operating system.
320 */
321 int Thread::LockMemory() {
322 #if defined(WIN32)
323 return 0;
324 #else
325 #if !defined(__APPLE__)
326 if (!bLockedMemory) return 0;
327 if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0) {
328 std::cerr << "Thread: WARNING, can't mlockall() memory!\n"
329 << std::flush;
330 return -1;
331 }
332 #endif
333 return 0;
334 #endif
335 }
336
337 /**
338 * Registers thread destructor callback function which will be executed when
339 * the thread stops it's execution and sets the 'Running' flag to true. This
340 * method will be called by the __pthread_launcher callback function, DO NOT
341 * CALL THIS METHOD YOURSELF!
342 */
343 void Thread::EnableDestructor() {
344 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
345 win32isRunning = true;
346 return;
347 #endif
348 RunningCondition.Lock();
349 #if defined(WIN32)
350 #else
351 pthread_key_create(&__thread_destructor_key, __pthread_destructor);
352 pthread_setspecific(__thread_destructor_key, this);
353 #endif
354 RunningCondition.Set(true);
355 RunningCondition.Unlock();
356 }
357
358 /**
359 * Will be called by the kernel when the thread stops it's execution.
360 */
361 int Thread::Destructor() {
362 #if defined(WIN32)
363 #else
364 pthread_key_delete(__thread_destructor_key);
365 RunningCondition.Set(false);
366 #endif
367 return 0;
368 }
369
370 void Thread::TestCancel() {
371 #if CONFIG_PTHREAD_TESTCANCEL
372 pthread_testcancel();
373 #endif
374 }
375
376 #if defined(WIN32)
377 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
378 // make sure stack is 16-byte aligned for SSE instructions
379 __attribute__((force_align_arg_pointer))
380 #endif
381 DWORD WINAPI __win32thread_launcher(LPVOID lpParameter) {
382 Thread* t;
383 t = (Thread*) lpParameter;
384 t->SetSchedulingPriority();
385 t->LockMemory();
386 t->EnableDestructor();
387 t->Main();
388 return 0;
389 }
390 #else
391 /// Callback function for the POSIX thread API
392 static void* __pthread_launcher(void* thread) {
393 #if !CONFIG_PTHREAD_TESTCANCEL
394 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); // let the thread be killable under any circumstances
395 #endif
396
397 Thread* t;
398 t = (Thread*) thread;
399 t->SetSchedulingPriority();
400 t->LockMemory();
401 t->EnableDestructor();
402 t->Main();
403 return NULL;
404 }
405 #endif
406
407 #if defined(WIN32)
408 #else
409 /// Callback function for the POSIX thread API
410 static void __pthread_destructor(void* thread) {
411 Thread* t;
412 t = (Thread*) thread;
413 t->Destructor();
414 }
415 #endif
416
417 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC