/[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 1649 - (show annotations) (download)
Fri Jan 25 15:06:02 2008 UTC (16 years, 3 months ago) by nagata
File size: 13082 byte(s)
* added a new config option --enable-pthread-testcancel, which uses
pthread_testcancel() instead of asynchronous canceling (needed for OSX)

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

  ViewVC Help
Powered by ViewVC