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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1649 - (hide 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 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 1212 * Copyright (C) 2005 - 2007 Christian Schoenebeck *
7 schoenebeck 53 * *
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 schoenebeck 1221 // 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 schoenebeck 1212 namespace LinuxSampler {
33    
34 senoner 1481 #if defined(WIN32)
35     // Callback functions for the WIN32 thread API
36     DWORD WINAPI __win32thread_launcher(LPVOID lpParameter);
37     #else
38 schoenebeck 1424 // Callback functions for the POSIX thread API
39     static void* __pthread_launcher(void* thread);
40     static void __pthread_destructor(void* thread);
41 senoner 1481 #endif
42 schoenebeck 1424
43 schoenebeck 392 Thread::Thread(bool LockMemory, bool RealTime, int PriorityMax, int PriorityDelta) {
44     this->bLockedMemory = LockMemory;
45 schoenebeck 53 this->isRealTime = RealTime;
46     this->PriorityDelta = PriorityDelta;
47     this->PriorityMax = PriorityMax;
48 senoner 1481 #if defined(WIN32)
49     #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
50     win32isRunning = false;
51     #endif
52     #else
53 schoenebeck 53 __thread_destructor_key = 0;
54 schoenebeck 1221 pthread_attr_init(&__thread_attr);
55 senoner 1481 #endif
56 schoenebeck 53 }
57    
58     Thread::~Thread() {
59 schoenebeck 57 StopThread();
60 senoner 1481 #if defined(WIN32)
61     #else
62 schoenebeck 1221 pthread_attr_destroy(&__thread_attr);
63 senoner 1481 #endif
64 schoenebeck 53 }
65    
66     /**
67 schoenebeck 57 * 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 schoenebeck 53 */
72     int Thread::StartThread() {
73 senoner 1481 #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 schoenebeck 1221 RunningCondition.Lock();
85     if (!RunningCondition.GetUnsafe()) {
86 schoenebeck 57 SignalStartThread();
87 schoenebeck 1221 // wait until thread started execution
88     RunningCondition.WaitIf(false);
89 schoenebeck 57 }
90 schoenebeck 1221 RunningCondition.Unlock();
91 schoenebeck 57 return 0;
92 senoner 1481 #endif
93 schoenebeck 57 }
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 senoner 1481 #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 schoenebeck 1221 // 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 nagata 1649
154 schoenebeck 53 // Create and run the thread
155 schoenebeck 1221 res = pthread_create(&this->__thread_id, &__thread_attr, __pthread_launcher, this);
156 schoenebeck 53 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 schoenebeck 1221 RunningCondition.Set(false);
163 schoenebeck 53 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 schoenebeck 1221 RunningCondition.Set(false);
168 schoenebeck 53 break;
169     default:
170     std::cerr << "Thread creation failed: Unknown cause."
171     << std::endl << std::flush;
172 schoenebeck 1221 RunningCondition.Set(false);
173 schoenebeck 53 break;
174     }
175     return res;
176 senoner 1481 #endif
177 schoenebeck 53 }
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 senoner 1481 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
185     SignalStopThread();
186     win32isRunning = false;
187     return 0;
188     #endif
189 schoenebeck 1221 RunningCondition.Lock();
190     if (RunningCondition.GetUnsafe()) {
191 schoenebeck 53 SignalStopThread();
192 schoenebeck 1221 // wait until thread stopped execution
193     RunningCondition.WaitIf(true);
194 senoner 1481 #if defined(WIN32)
195     #else
196 schoenebeck 351 pthread_detach(__thread_id);
197 senoner 1481 #endif
198 schoenebeck 53 }
199 schoenebeck 1221 RunningCondition.Unlock();
200 schoenebeck 53 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 schoenebeck 57 *
208     * @see StopThread()
209 schoenebeck 53 */
210     int Thread::SignalStopThread() {
211 schoenebeck 1221 //FIXME: segfaults when thread is not yet running
212 senoner 1481 #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 nagata 1649 #else
223 schoenebeck 53 pthread_cancel(__thread_id);
224 senoner 1481 #endif
225 schoenebeck 53 return 0;
226     }
227    
228     /**
229 schoenebeck 1212 * Returns @c true in case the thread is currently running.
230     */
231     bool Thread::IsRunning() {
232 senoner 1481 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
233     return win32isRunning;
234     #else
235 schoenebeck 1221 return RunningCondition.GetUnsafe();
236 senoner 1481 #endif
237 schoenebeck 1212 }
238    
239     /**
240 schoenebeck 53 * 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 senoner 1481 #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 schoenebeck 361 #if !defined(__APPLE__)
282 schoenebeck 1221 int policy;
283 persson 1222 const char* policyDescription = NULL;
284 schoenebeck 1221 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 schoenebeck 53 struct sched_param schp;
293     memset(&schp, 0, sizeof(schp));
294 persson 1222 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 schoenebeck 53 }
302 schoenebeck 1221 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 schoenebeck 53 return -1;
308     }
309 schoenebeck 361 #endif
310 schoenebeck 53 return 0;
311 senoner 1481 #endif
312 schoenebeck 53 }
313    
314     /**
315 schoenebeck 392 * Locks the memory so it will not be swapped out by the operating system.
316     */
317     int Thread::LockMemory() {
318 senoner 1481 #if defined(WIN32)
319     return 0;
320     #else
321 letz 399 #if !defined(__APPLE__)
322 schoenebeck 392 if (!bLockedMemory) return 0;
323     if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0) {
324 schoenebeck 1221 std::cerr << "Thread: WARNING, can't mlockall() memory!\n"
325     << std::flush;
326 schoenebeck 392 return -1;
327     }
328 letz 399 #endif
329 schoenebeck 392 return 0;
330 senoner 1481 #endif
331 schoenebeck 392 }
332    
333     /**
334 schoenebeck 53 * 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 senoner 1481 #if defined(WIN32_SIGNALSTARTTHREAD_WORKAROUND)
341     win32isRunning = true;
342     return;
343     #endif
344 schoenebeck 1221 RunningCondition.Lock();
345 senoner 1481 #if defined(WIN32)
346     #else
347 schoenebeck 53 pthread_key_create(&__thread_destructor_key, __pthread_destructor);
348     pthread_setspecific(__thread_destructor_key, this);
349 senoner 1481 #endif
350 schoenebeck 1221 RunningCondition.Set(true);
351     RunningCondition.Unlock();
352 schoenebeck 53 }
353    
354     /**
355     * Will be called by the kernel when the thread stops it's execution.
356     */
357     int Thread::Destructor() {
358 senoner 1481 #if defined(WIN32)
359     #else
360 schoenebeck 53 pthread_key_delete(__thread_destructor_key);
361 schoenebeck 1221 RunningCondition.Set(false);
362 senoner 1481 #endif
363 persson 497 return 0;
364 schoenebeck 53 }
365    
366 senoner 1481 #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 schoenebeck 53 /// Callback function for the POSIX thread API
378 schoenebeck 1424 static void* __pthread_launcher(void* thread) {
379 nagata 1649 #if !CONFIG_PTHREAD_TESTCANCEL
380 schoenebeck 63 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); // let the thread be killable under any circumstances
381 nagata 1649 #endif
382    
383 schoenebeck 53 Thread* t;
384     t = (Thread*) thread;
385 schoenebeck 57 t->SetSchedulingPriority();
386 schoenebeck 392 t->LockMemory();
387 schoenebeck 53 t->EnableDestructor();
388     t->Main();
389 persson 497 return NULL;
390 schoenebeck 53 }
391 senoner 1481 #endif
392 schoenebeck 53
393 senoner 1481 #if defined(WIN32)
394     #else
395 schoenebeck 53 /// Callback function for the POSIX thread API
396 schoenebeck 1424 static void __pthread_destructor(void* thread) {
397 schoenebeck 53 Thread* t;
398     t = (Thread*) thread;
399     t->Destructor();
400     }
401 senoner 1481 #endif
402 schoenebeck 1212
403     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC