rev |
line source |
nuclear@3
|
1
|
nuclear@3
|
2 #include "OVR_Threads.h"
|
nuclear@3
|
3 #include "OVR_Hash.h"
|
nuclear@3
|
4
|
nuclear@3
|
5 #ifdef OVR_ENABLE_THREADS
|
nuclear@3
|
6
|
nuclear@3
|
7 #include "OVR_Timer.h"
|
nuclear@3
|
8 #include "OVR_Log.h"
|
nuclear@3
|
9
|
nuclear@3
|
10 #include <pthread.h>
|
nuclear@3
|
11 #include <time.h>
|
nuclear@3
|
12
|
nuclear@3
|
13 #ifdef OVR_OS_PS3
|
nuclear@3
|
14 #include <sys/sys_time.h>
|
nuclear@3
|
15 #include <sys/timer.h>
|
nuclear@3
|
16 #include <sys/synchronization.h>
|
nuclear@3
|
17 #define sleep(x) sys_timer_sleep(x)
|
nuclear@3
|
18 #define usleep(x) sys_timer_usleep(x)
|
nuclear@3
|
19 using std::timespec;
|
nuclear@3
|
20 #else
|
nuclear@3
|
21 #include <unistd.h>
|
nuclear@3
|
22 #include <sys/time.h>
|
nuclear@3
|
23 #include <errno.h>
|
nuclear@3
|
24 #endif
|
nuclear@3
|
25
|
nuclear@3
|
26 namespace OVR {
|
nuclear@3
|
27
|
nuclear@3
|
28 // ***** Mutex implementation
|
nuclear@3
|
29
|
nuclear@3
|
30
|
nuclear@3
|
31 // *** Internal Mutex implementation structure
|
nuclear@3
|
32
|
nuclear@3
|
33 class MutexImpl : public NewOverrideBase
|
nuclear@3
|
34 {
|
nuclear@3
|
35 // System mutex or semaphore
|
nuclear@3
|
36 pthread_mutex_t SMutex;
|
nuclear@3
|
37 bool Recursive;
|
nuclear@3
|
38 unsigned LockCount;
|
nuclear@3
|
39 pthread_t LockedBy;
|
nuclear@3
|
40
|
nuclear@3
|
41 friend class WaitConditionImpl;
|
nuclear@3
|
42
|
nuclear@3
|
43 public:
|
nuclear@3
|
44 // Constructor/destructor
|
nuclear@3
|
45 MutexImpl(Mutex* pmutex, bool recursive = 1);
|
nuclear@3
|
46 ~MutexImpl();
|
nuclear@3
|
47
|
nuclear@3
|
48 // Locking functions
|
nuclear@3
|
49 void DoLock();
|
nuclear@3
|
50 bool TryLock();
|
nuclear@3
|
51 void Unlock(Mutex* pmutex);
|
nuclear@3
|
52 // Returns 1 if the mutes is currently locked
|
nuclear@3
|
53 bool IsLockedByAnotherThread(Mutex* pmutex);
|
nuclear@3
|
54 bool IsSignaled() const;
|
nuclear@3
|
55 };
|
nuclear@3
|
56
|
nuclear@3
|
57 pthread_mutexattr_t Lock::RecursiveAttr;
|
nuclear@3
|
58 bool Lock::RecursiveAttrInit = 0;
|
nuclear@3
|
59
|
nuclear@3
|
60 // *** Constructor/destructor
|
nuclear@3
|
61 MutexImpl::MutexImpl(Mutex* pmutex, bool recursive)
|
nuclear@3
|
62 {
|
nuclear@3
|
63 Recursive = recursive;
|
nuclear@3
|
64 LockCount = 0;
|
nuclear@3
|
65
|
nuclear@3
|
66 if (Recursive)
|
nuclear@3
|
67 {
|
nuclear@3
|
68 if (!Lock::RecursiveAttrInit)
|
nuclear@3
|
69 {
|
nuclear@3
|
70 pthread_mutexattr_init(&Lock::RecursiveAttr);
|
nuclear@3
|
71 pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
|
nuclear@3
|
72 Lock::RecursiveAttrInit = 1;
|
nuclear@3
|
73 }
|
nuclear@3
|
74
|
nuclear@3
|
75 pthread_mutex_init(&SMutex, &Lock::RecursiveAttr);
|
nuclear@3
|
76 }
|
nuclear@3
|
77 else
|
nuclear@3
|
78 pthread_mutex_init(&SMutex, 0);
|
nuclear@3
|
79 }
|
nuclear@3
|
80
|
nuclear@3
|
81 MutexImpl::~MutexImpl()
|
nuclear@3
|
82 {
|
nuclear@3
|
83 pthread_mutex_destroy(&SMutex);
|
nuclear@3
|
84 }
|
nuclear@3
|
85
|
nuclear@3
|
86
|
nuclear@3
|
87 // Lock and try lock
|
nuclear@3
|
88 void MutexImpl::DoLock()
|
nuclear@3
|
89 {
|
nuclear@3
|
90 while (pthread_mutex_lock(&SMutex));
|
nuclear@3
|
91 LockCount++;
|
nuclear@3
|
92 LockedBy = pthread_self();
|
nuclear@3
|
93 }
|
nuclear@3
|
94
|
nuclear@3
|
95 bool MutexImpl::TryLock()
|
nuclear@3
|
96 {
|
nuclear@3
|
97 if (!pthread_mutex_trylock(&SMutex))
|
nuclear@3
|
98 {
|
nuclear@3
|
99 LockCount++;
|
nuclear@3
|
100 LockedBy = pthread_self();
|
nuclear@3
|
101 return 1;
|
nuclear@3
|
102 }
|
nuclear@3
|
103
|
nuclear@3
|
104 return 0;
|
nuclear@3
|
105 }
|
nuclear@3
|
106
|
nuclear@3
|
107 void MutexImpl::Unlock(Mutex* pmutex)
|
nuclear@3
|
108 {
|
nuclear@3
|
109 OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0);
|
nuclear@3
|
110
|
nuclear@3
|
111 unsigned lockCount;
|
nuclear@3
|
112 LockCount--;
|
nuclear@3
|
113 lockCount = LockCount;
|
nuclear@3
|
114
|
nuclear@3
|
115 pthread_mutex_unlock(&SMutex);
|
nuclear@3
|
116 }
|
nuclear@3
|
117
|
nuclear@3
|
118 bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
|
nuclear@3
|
119 {
|
nuclear@3
|
120 // There could be multiple interpretations of IsLocked with respect to current thread
|
nuclear@3
|
121 if (LockCount == 0)
|
nuclear@3
|
122 return 0;
|
nuclear@3
|
123 if (pthread_self() != LockedBy)
|
nuclear@3
|
124 return 1;
|
nuclear@3
|
125 return 0;
|
nuclear@3
|
126 }
|
nuclear@3
|
127
|
nuclear@3
|
128 bool MutexImpl::IsSignaled() const
|
nuclear@3
|
129 {
|
nuclear@3
|
130 // An mutex is signaled if it is not locked ANYWHERE
|
nuclear@3
|
131 // Note that this is different from IsLockedByAnotherThread function,
|
nuclear@3
|
132 // that takes current thread into account
|
nuclear@3
|
133 return LockCount == 0;
|
nuclear@3
|
134 }
|
nuclear@3
|
135
|
nuclear@3
|
136
|
nuclear@3
|
137 // *** Actual Mutex class implementation
|
nuclear@3
|
138
|
nuclear@3
|
139 Mutex::Mutex(bool recursive)
|
nuclear@3
|
140 {
|
nuclear@3
|
141 // NOTE: RefCount mode already thread-safe for all waitables.
|
nuclear@3
|
142 pImpl = new MutexImpl(this, recursive);
|
nuclear@3
|
143 }
|
nuclear@3
|
144
|
nuclear@3
|
145 Mutex::~Mutex()
|
nuclear@3
|
146 {
|
nuclear@3
|
147 delete pImpl;
|
nuclear@3
|
148 }
|
nuclear@3
|
149
|
nuclear@3
|
150 // Lock and try lock
|
nuclear@3
|
151 void Mutex::DoLock()
|
nuclear@3
|
152 {
|
nuclear@3
|
153 pImpl->DoLock();
|
nuclear@3
|
154 }
|
nuclear@3
|
155 bool Mutex::TryLock()
|
nuclear@3
|
156 {
|
nuclear@3
|
157 return pImpl->TryLock();
|
nuclear@3
|
158 }
|
nuclear@3
|
159 void Mutex::Unlock()
|
nuclear@3
|
160 {
|
nuclear@3
|
161 pImpl->Unlock(this);
|
nuclear@3
|
162 }
|
nuclear@3
|
163 bool Mutex::IsLockedByAnotherThread()
|
nuclear@3
|
164 {
|
nuclear@3
|
165 return pImpl->IsLockedByAnotherThread(this);
|
nuclear@3
|
166 }
|
nuclear@3
|
167
|
nuclear@3
|
168
|
nuclear@3
|
169
|
nuclear@3
|
170 //-----------------------------------------------------------------------------------
|
nuclear@3
|
171 // ***** Event
|
nuclear@3
|
172
|
nuclear@3
|
173 bool Event::Wait(unsigned delay)
|
nuclear@3
|
174 {
|
nuclear@3
|
175 Mutex::Locker lock(&StateMutex);
|
nuclear@3
|
176
|
nuclear@3
|
177 // Do the correct amount of waiting
|
nuclear@3
|
178 if (delay == OVR_WAIT_INFINITE)
|
nuclear@3
|
179 {
|
nuclear@3
|
180 while(!State)
|
nuclear@3
|
181 StateWaitCondition.Wait(&StateMutex);
|
nuclear@3
|
182 }
|
nuclear@3
|
183 else if (delay)
|
nuclear@3
|
184 {
|
nuclear@3
|
185 if (!State)
|
nuclear@3
|
186 StateWaitCondition.Wait(&StateMutex, delay);
|
nuclear@3
|
187 }
|
nuclear@3
|
188
|
nuclear@3
|
189 bool state = State;
|
nuclear@3
|
190 // Take care of temporary 'pulsing' of a state
|
nuclear@3
|
191 if (Temporary)
|
nuclear@3
|
192 {
|
nuclear@3
|
193 Temporary = false;
|
nuclear@3
|
194 State = false;
|
nuclear@3
|
195 }
|
nuclear@3
|
196 return state;
|
nuclear@3
|
197 }
|
nuclear@3
|
198
|
nuclear@3
|
199 void Event::updateState(bool newState, bool newTemp, bool mustNotify)
|
nuclear@3
|
200 {
|
nuclear@3
|
201 Mutex::Locker lock(&StateMutex);
|
nuclear@3
|
202 State = newState;
|
nuclear@3
|
203 Temporary = newTemp;
|
nuclear@3
|
204 if (mustNotify)
|
nuclear@3
|
205 StateWaitCondition.NotifyAll();
|
nuclear@3
|
206 }
|
nuclear@3
|
207
|
nuclear@3
|
208
|
nuclear@3
|
209
|
nuclear@3
|
210 // ***** Wait Condition Implementation
|
nuclear@3
|
211
|
nuclear@3
|
212 // Internal implementation class
|
nuclear@3
|
213 class WaitConditionImpl : public NewOverrideBase
|
nuclear@3
|
214 {
|
nuclear@3
|
215 pthread_mutex_t SMutex;
|
nuclear@3
|
216 pthread_cond_t Condv;
|
nuclear@3
|
217
|
nuclear@3
|
218 public:
|
nuclear@3
|
219
|
nuclear@3
|
220 // Constructor/destructor
|
nuclear@3
|
221 WaitConditionImpl();
|
nuclear@3
|
222 ~WaitConditionImpl();
|
nuclear@3
|
223
|
nuclear@3
|
224 // Release mutex and wait for condition. The mutex is re-aqured after the wait.
|
nuclear@3
|
225 bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
|
nuclear@3
|
226
|
nuclear@3
|
227 // Notify a condition, releasing at one object waiting
|
nuclear@3
|
228 void Notify();
|
nuclear@3
|
229 // Notify a condition, releasing all objects waiting
|
nuclear@3
|
230 void NotifyAll();
|
nuclear@3
|
231 };
|
nuclear@3
|
232
|
nuclear@3
|
233
|
nuclear@3
|
234 WaitConditionImpl::WaitConditionImpl()
|
nuclear@3
|
235 {
|
nuclear@3
|
236 pthread_mutex_init(&SMutex, 0);
|
nuclear@3
|
237 pthread_cond_init(&Condv, 0);
|
nuclear@3
|
238 }
|
nuclear@3
|
239
|
nuclear@3
|
240 WaitConditionImpl::~WaitConditionImpl()
|
nuclear@3
|
241 {
|
nuclear@3
|
242 pthread_mutex_destroy(&SMutex);
|
nuclear@3
|
243 pthread_cond_destroy(&Condv);
|
nuclear@3
|
244 }
|
nuclear@3
|
245
|
nuclear@3
|
246 bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
|
nuclear@3
|
247 {
|
nuclear@3
|
248 bool result = 1;
|
nuclear@3
|
249 unsigned lockCount = pmutex->pImpl->LockCount;
|
nuclear@3
|
250
|
nuclear@3
|
251 // Mutex must have been locked
|
nuclear@3
|
252 if (lockCount == 0)
|
nuclear@3
|
253 return 0;
|
nuclear@3
|
254
|
nuclear@3
|
255 pthread_mutex_lock(&SMutex);
|
nuclear@3
|
256
|
nuclear@3
|
257 // Finally, release a mutex or semaphore
|
nuclear@3
|
258 if (pmutex->pImpl->Recursive)
|
nuclear@3
|
259 {
|
nuclear@3
|
260 // Release the recursive mutex N times
|
nuclear@3
|
261 pmutex->pImpl->LockCount = 0;
|
nuclear@3
|
262 for(unsigned i=0; i<lockCount; i++)
|
nuclear@3
|
263 pthread_mutex_unlock(&pmutex->pImpl->SMutex);
|
nuclear@3
|
264 }
|
nuclear@3
|
265 else
|
nuclear@3
|
266 {
|
nuclear@3
|
267 pmutex->pImpl->LockCount = 0;
|
nuclear@3
|
268 pthread_mutex_unlock(&pmutex->pImpl->SMutex);
|
nuclear@3
|
269 }
|
nuclear@3
|
270
|
nuclear@3
|
271 // Note that there is a gap here between mutex.Unlock() and Wait().
|
nuclear@3
|
272 // The other mutex protects this gap.
|
nuclear@3
|
273
|
nuclear@3
|
274 if (delay == OVR_WAIT_INFINITE)
|
nuclear@3
|
275 pthread_cond_wait(&Condv,&SMutex);
|
nuclear@3
|
276 else
|
nuclear@3
|
277 {
|
nuclear@3
|
278 timespec ts;
|
nuclear@3
|
279 #ifdef OVR_OS_PS3
|
nuclear@3
|
280 sys_time_sec_t s;
|
nuclear@3
|
281 sys_time_nsec_t ns;
|
nuclear@3
|
282 sys_time_get_current_time(&s, &ns);
|
nuclear@3
|
283
|
nuclear@3
|
284 ts.tv_sec = s + (delay / 1000);
|
nuclear@3
|
285 ts.tv_nsec = ns + (delay % 1000) * 1000000;
|
nuclear@3
|
286
|
nuclear@3
|
287 #else
|
nuclear@3
|
288 struct timeval tv;
|
nuclear@3
|
289 gettimeofday(&tv, 0);
|
nuclear@3
|
290
|
nuclear@3
|
291 ts.tv_sec = tv.tv_sec + (delay / 1000);
|
nuclear@3
|
292 ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000;
|
nuclear@3
|
293 #endif
|
nuclear@3
|
294 if (ts.tv_nsec > 999999999)
|
nuclear@3
|
295 {
|
nuclear@3
|
296 ts.tv_sec++;
|
nuclear@3
|
297 ts.tv_nsec -= 1000000000;
|
nuclear@3
|
298 }
|
nuclear@3
|
299 int r = pthread_cond_timedwait(&Condv,&SMutex, &ts);
|
nuclear@3
|
300 OVR_ASSERT(r == 0 || r == ETIMEDOUT);
|
nuclear@3
|
301 if (r)
|
nuclear@3
|
302 result = 0;
|
nuclear@3
|
303 }
|
nuclear@3
|
304
|
nuclear@3
|
305 pthread_mutex_unlock(&SMutex);
|
nuclear@3
|
306
|
nuclear@3
|
307 // Re-aquire the mutex
|
nuclear@3
|
308 for(unsigned i=0; i<lockCount; i++)
|
nuclear@3
|
309 pmutex->DoLock();
|
nuclear@3
|
310
|
nuclear@3
|
311 // Return the result
|
nuclear@3
|
312 return result;
|
nuclear@3
|
313 }
|
nuclear@3
|
314
|
nuclear@3
|
315 // Notify a condition, releasing the least object in a queue
|
nuclear@3
|
316 void WaitConditionImpl::Notify()
|
nuclear@3
|
317 {
|
nuclear@3
|
318 pthread_mutex_lock(&SMutex);
|
nuclear@3
|
319 pthread_cond_signal(&Condv);
|
nuclear@3
|
320 pthread_mutex_unlock(&SMutex);
|
nuclear@3
|
321 }
|
nuclear@3
|
322
|
nuclear@3
|
323 // Notify a condition, releasing all objects waiting
|
nuclear@3
|
324 void WaitConditionImpl::NotifyAll()
|
nuclear@3
|
325 {
|
nuclear@3
|
326 pthread_mutex_lock(&SMutex);
|
nuclear@3
|
327 pthread_cond_broadcast(&Condv);
|
nuclear@3
|
328 pthread_mutex_unlock(&SMutex);
|
nuclear@3
|
329 }
|
nuclear@3
|
330
|
nuclear@3
|
331
|
nuclear@3
|
332
|
nuclear@3
|
333 // *** Actual implementation of WaitCondition
|
nuclear@3
|
334
|
nuclear@3
|
335 WaitCondition::WaitCondition()
|
nuclear@3
|
336 {
|
nuclear@3
|
337 pImpl = new WaitConditionImpl;
|
nuclear@3
|
338 }
|
nuclear@3
|
339 WaitCondition::~WaitCondition()
|
nuclear@3
|
340 {
|
nuclear@3
|
341 delete pImpl;
|
nuclear@3
|
342 }
|
nuclear@3
|
343
|
nuclear@3
|
344 bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
|
nuclear@3
|
345 {
|
nuclear@3
|
346 return pImpl->Wait(pmutex, delay);
|
nuclear@3
|
347 }
|
nuclear@3
|
348 // Notification
|
nuclear@3
|
349 void WaitCondition::Notify()
|
nuclear@3
|
350 {
|
nuclear@3
|
351 pImpl->Notify();
|
nuclear@3
|
352 }
|
nuclear@3
|
353 void WaitCondition::NotifyAll()
|
nuclear@3
|
354 {
|
nuclear@3
|
355 pImpl->NotifyAll();
|
nuclear@3
|
356 }
|
nuclear@3
|
357
|
nuclear@3
|
358
|
nuclear@3
|
359 // ***** Current thread
|
nuclear@3
|
360
|
nuclear@3
|
361 // Per-thread variable
|
nuclear@3
|
362 /*
|
nuclear@3
|
363 static __thread Thread* pCurrentThread = 0;
|
nuclear@3
|
364
|
nuclear@3
|
365 // Static function to return a pointer to the current thread
|
nuclear@3
|
366 void Thread::InitCurrentThread(Thread *pthread)
|
nuclear@3
|
367 {
|
nuclear@3
|
368 pCurrentThread = pthread;
|
nuclear@3
|
369 }
|
nuclear@3
|
370
|
nuclear@3
|
371 // Static function to return a pointer to the current thread
|
nuclear@3
|
372 Thread* Thread::GetThread()
|
nuclear@3
|
373 {
|
nuclear@3
|
374 return pCurrentThread;
|
nuclear@3
|
375 }
|
nuclear@3
|
376 */
|
nuclear@3
|
377
|
nuclear@3
|
378
|
nuclear@3
|
379 // *** Thread constructors.
|
nuclear@3
|
380
|
nuclear@3
|
381 Thread::Thread(UPInt stackSize, int processor)
|
nuclear@3
|
382 {
|
nuclear@3
|
383 // NOTE: RefCount mode already thread-safe for all Waitable objects.
|
nuclear@3
|
384 CreateParams params;
|
nuclear@3
|
385 params.stackSize = stackSize;
|
nuclear@3
|
386 params.processor = processor;
|
nuclear@3
|
387 Init(params);
|
nuclear@3
|
388 }
|
nuclear@3
|
389
|
nuclear@3
|
390 Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
|
nuclear@3
|
391 int processor, Thread::ThreadState initialState)
|
nuclear@3
|
392 {
|
nuclear@3
|
393 CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
|
nuclear@3
|
394 Init(params);
|
nuclear@3
|
395 }
|
nuclear@3
|
396
|
nuclear@3
|
397 Thread::Thread(const CreateParams& params)
|
nuclear@3
|
398 {
|
nuclear@3
|
399 Init(params);
|
nuclear@3
|
400 }
|
nuclear@3
|
401
|
nuclear@3
|
402 void Thread::Init(const CreateParams& params)
|
nuclear@3
|
403 {
|
nuclear@3
|
404 // Clear the variables
|
nuclear@3
|
405 ThreadFlags = 0;
|
nuclear@3
|
406 ThreadHandle = 0;
|
nuclear@3
|
407 ExitCode = 0;
|
nuclear@3
|
408 SuspendCount = 0;
|
nuclear@3
|
409 StackSize = params.stackSize;
|
nuclear@3
|
410 Processor = params.processor;
|
nuclear@3
|
411 Priority = params.priority;
|
nuclear@3
|
412
|
nuclear@3
|
413 // Clear Function pointers
|
nuclear@3
|
414 ThreadFunction = params.threadFunction;
|
nuclear@3
|
415 UserHandle = params.userHandle;
|
nuclear@3
|
416 if (params.initialState != NotRunning)
|
nuclear@3
|
417 Start(params.initialState);
|
nuclear@3
|
418 }
|
nuclear@3
|
419
|
nuclear@3
|
420 Thread::~Thread()
|
nuclear@3
|
421 {
|
nuclear@3
|
422 // Thread should not running while object is being destroyed,
|
nuclear@3
|
423 // this would indicate ref-counting issue.
|
nuclear@3
|
424 //OVR_ASSERT(IsRunning() == 0);
|
nuclear@3
|
425
|
nuclear@3
|
426 // Clean up thread.
|
nuclear@3
|
427 ThreadHandle = 0;
|
nuclear@3
|
428 }
|
nuclear@3
|
429
|
nuclear@3
|
430
|
nuclear@3
|
431
|
nuclear@3
|
432 // *** Overridable User functions.
|
nuclear@3
|
433
|
nuclear@3
|
434 // Default Run implementation
|
nuclear@3
|
435 int Thread::Run()
|
nuclear@3
|
436 {
|
nuclear@3
|
437 // Call pointer to function, if available.
|
nuclear@3
|
438 return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
|
nuclear@3
|
439 }
|
nuclear@3
|
440 void Thread::OnExit()
|
nuclear@3
|
441 {
|
nuclear@3
|
442 }
|
nuclear@3
|
443
|
nuclear@3
|
444
|
nuclear@3
|
445 // Finishes the thread and releases internal reference to it.
|
nuclear@3
|
446 void Thread::FinishAndRelease()
|
nuclear@3
|
447 {
|
nuclear@3
|
448 // Note: thread must be US.
|
nuclear@3
|
449 ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
|
nuclear@3
|
450 ThreadFlags |= OVR_THREAD_FINISHED;
|
nuclear@3
|
451
|
nuclear@3
|
452 // Release our reference; this is equivalent to 'delete this'
|
nuclear@3
|
453 // from the point of view of our thread.
|
nuclear@3
|
454 Release();
|
nuclear@3
|
455 }
|
nuclear@3
|
456
|
nuclear@3
|
457
|
nuclear@3
|
458
|
nuclear@3
|
459 // *** ThreadList - used to track all created threads
|
nuclear@3
|
460
|
nuclear@3
|
461 class ThreadList : public NewOverrideBase
|
nuclear@3
|
462 {
|
nuclear@3
|
463 //------------------------------------------------------------------------
|
nuclear@3
|
464 struct ThreadHashOp
|
nuclear@3
|
465 {
|
nuclear@3
|
466 size_t operator()(const Thread* ptr)
|
nuclear@3
|
467 {
|
nuclear@3
|
468 return (((size_t)ptr) >> 6) ^ (size_t)ptr;
|
nuclear@3
|
469 }
|
nuclear@3
|
470 };
|
nuclear@3
|
471
|
nuclear@3
|
472 HashSet<Thread*, ThreadHashOp> ThreadSet;
|
nuclear@3
|
473 Mutex ThreadMutex;
|
nuclear@3
|
474 WaitCondition ThreadsEmpty;
|
nuclear@3
|
475 // Track the root thread that created us.
|
nuclear@3
|
476 pthread_t RootThreadId;
|
nuclear@3
|
477
|
nuclear@3
|
478 static ThreadList* volatile pRunningThreads;
|
nuclear@3
|
479
|
nuclear@3
|
480 void addThread(Thread *pthread)
|
nuclear@3
|
481 {
|
nuclear@3
|
482 Mutex::Locker lock(&ThreadMutex);
|
nuclear@3
|
483 ThreadSet.Add(pthread);
|
nuclear@3
|
484 }
|
nuclear@3
|
485
|
nuclear@3
|
486 void removeThread(Thread *pthread)
|
nuclear@3
|
487 {
|
nuclear@3
|
488 Mutex::Locker lock(&ThreadMutex);
|
nuclear@3
|
489 ThreadSet.Remove(pthread);
|
nuclear@3
|
490 if (ThreadSet.GetSize() == 0)
|
nuclear@3
|
491 ThreadsEmpty.Notify();
|
nuclear@3
|
492 }
|
nuclear@3
|
493
|
nuclear@3
|
494 void finishAllThreads()
|
nuclear@3
|
495 {
|
nuclear@3
|
496 // Only original root thread can call this.
|
nuclear@3
|
497 OVR_ASSERT(pthread_self() == RootThreadId);
|
nuclear@3
|
498
|
nuclear@3
|
499 Mutex::Locker lock(&ThreadMutex);
|
nuclear@3
|
500 while (ThreadSet.GetSize() != 0)
|
nuclear@3
|
501 ThreadsEmpty.Wait(&ThreadMutex);
|
nuclear@3
|
502 }
|
nuclear@3
|
503
|
nuclear@3
|
504 public:
|
nuclear@3
|
505
|
nuclear@3
|
506 ThreadList()
|
nuclear@3
|
507 {
|
nuclear@3
|
508 RootThreadId = pthread_self();
|
nuclear@3
|
509 }
|
nuclear@3
|
510 ~ThreadList() { }
|
nuclear@3
|
511
|
nuclear@3
|
512
|
nuclear@3
|
513 static void AddRunningThread(Thread *pthread)
|
nuclear@3
|
514 {
|
nuclear@3
|
515 // Non-atomic creation ok since only the root thread
|
nuclear@3
|
516 if (!pRunningThreads)
|
nuclear@3
|
517 {
|
nuclear@3
|
518 pRunningThreads = new ThreadList;
|
nuclear@3
|
519 OVR_ASSERT(pRunningThreads);
|
nuclear@3
|
520 }
|
nuclear@3
|
521 pRunningThreads->addThread(pthread);
|
nuclear@3
|
522 }
|
nuclear@3
|
523
|
nuclear@3
|
524 // NOTE: 'pthread' might be a dead pointer when this is
|
nuclear@3
|
525 // called so it should not be accessed; it is only used
|
nuclear@3
|
526 // for removal.
|
nuclear@3
|
527 static void RemoveRunningThread(Thread *pthread)
|
nuclear@3
|
528 {
|
nuclear@3
|
529 OVR_ASSERT(pRunningThreads);
|
nuclear@3
|
530 pRunningThreads->removeThread(pthread);
|
nuclear@3
|
531 }
|
nuclear@3
|
532
|
nuclear@3
|
533 static void FinishAllThreads()
|
nuclear@3
|
534 {
|
nuclear@3
|
535 // This is ok because only root thread can wait for other thread finish.
|
nuclear@3
|
536 if (pRunningThreads)
|
nuclear@3
|
537 {
|
nuclear@3
|
538 pRunningThreads->finishAllThreads();
|
nuclear@3
|
539 delete pRunningThreads;
|
nuclear@3
|
540 pRunningThreads = 0;
|
nuclear@3
|
541 }
|
nuclear@3
|
542 }
|
nuclear@3
|
543 };
|
nuclear@3
|
544
|
nuclear@3
|
545 // By default, we have no thread list.
|
nuclear@3
|
546 ThreadList* volatile ThreadList::pRunningThreads = 0;
|
nuclear@3
|
547
|
nuclear@3
|
548
|
nuclear@3
|
549 // FinishAllThreads - exposed publicly in Thread.
|
nuclear@3
|
550 void Thread::FinishAllThreads()
|
nuclear@3
|
551 {
|
nuclear@3
|
552 ThreadList::FinishAllThreads();
|
nuclear@3
|
553 }
|
nuclear@3
|
554
|
nuclear@3
|
555 // *** Run override
|
nuclear@3
|
556
|
nuclear@3
|
557 int Thread::PRun()
|
nuclear@3
|
558 {
|
nuclear@3
|
559 // Suspend us on start, if requested
|
nuclear@3
|
560 if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
|
nuclear@3
|
561 {
|
nuclear@3
|
562 Suspend();
|
nuclear@3
|
563 ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
|
nuclear@3
|
564 }
|
nuclear@3
|
565
|
nuclear@3
|
566 // Call the virtual run function
|
nuclear@3
|
567 ExitCode = Run();
|
nuclear@3
|
568 return ExitCode;
|
nuclear@3
|
569 }
|
nuclear@3
|
570
|
nuclear@3
|
571
|
nuclear@3
|
572
|
nuclear@3
|
573
|
nuclear@3
|
574 // *** User overridables
|
nuclear@3
|
575
|
nuclear@3
|
576 bool Thread::GetExitFlag() const
|
nuclear@3
|
577 {
|
nuclear@3
|
578 return (ThreadFlags & OVR_THREAD_EXIT) != 0;
|
nuclear@3
|
579 }
|
nuclear@3
|
580
|
nuclear@3
|
581 void Thread::SetExitFlag(bool exitFlag)
|
nuclear@3
|
582 {
|
nuclear@3
|
583 // The below is atomic since ThreadFlags is AtomicInt.
|
nuclear@3
|
584 if (exitFlag)
|
nuclear@3
|
585 ThreadFlags |= OVR_THREAD_EXIT;
|
nuclear@3
|
586 else
|
nuclear@3
|
587 ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
|
nuclear@3
|
588 }
|
nuclear@3
|
589
|
nuclear@3
|
590
|
nuclear@3
|
591 // Determines whether the thread was running and is now finished
|
nuclear@3
|
592 bool Thread::IsFinished() const
|
nuclear@3
|
593 {
|
nuclear@3
|
594 return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
|
nuclear@3
|
595 }
|
nuclear@3
|
596 // Determines whether the thread is suspended
|
nuclear@3
|
597 bool Thread::IsSuspended() const
|
nuclear@3
|
598 {
|
nuclear@3
|
599 return SuspendCount > 0;
|
nuclear@3
|
600 }
|
nuclear@3
|
601 // Returns current thread state
|
nuclear@3
|
602 Thread::ThreadState Thread::GetThreadState() const
|
nuclear@3
|
603 {
|
nuclear@3
|
604 if (IsSuspended())
|
nuclear@3
|
605 return Suspended;
|
nuclear@3
|
606 if (ThreadFlags & OVR_THREAD_STARTED)
|
nuclear@3
|
607 return Running;
|
nuclear@3
|
608 return NotRunning;
|
nuclear@3
|
609 }
|
nuclear@3
|
610 /*
|
nuclear@3
|
611 static const char* mapsched_policy(int policy)
|
nuclear@3
|
612 {
|
nuclear@3
|
613 switch(policy)
|
nuclear@3
|
614 {
|
nuclear@3
|
615 case SCHED_OTHER:
|
nuclear@3
|
616 return "SCHED_OTHER";
|
nuclear@3
|
617 case SCHED_RR:
|
nuclear@3
|
618 return "SCHED_RR";
|
nuclear@3
|
619 case SCHED_FIFO:
|
nuclear@3
|
620 return "SCHED_FIFO";
|
nuclear@3
|
621
|
nuclear@3
|
622 }
|
nuclear@3
|
623 return "UNKNOWN";
|
nuclear@3
|
624 }
|
nuclear@3
|
625 int policy;
|
nuclear@3
|
626 sched_param sparam;
|
nuclear@3
|
627 pthread_getschedparam(pthread_self(), &policy, &sparam);
|
nuclear@3
|
628 int max_prior = sched_get_priority_max(policy);
|
nuclear@3
|
629 int min_prior = sched_get_priority_min(policy);
|
nuclear@3
|
630 printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior);
|
nuclear@3
|
631 #include <stdio.h>
|
nuclear@3
|
632 */
|
nuclear@3
|
633 // ***** Thread management
|
nuclear@3
|
634
|
nuclear@3
|
635 // The actual first function called on thread start
|
nuclear@3
|
636 void* Thread_PthreadStartFn(void* phandle)
|
nuclear@3
|
637 {
|
nuclear@3
|
638 Thread* pthread = (Thread*)phandle;
|
nuclear@3
|
639 int result = pthread->PRun();
|
nuclear@3
|
640 // Signal the thread as done and release it atomically.
|
nuclear@3
|
641 pthread->FinishAndRelease();
|
nuclear@3
|
642 // At this point Thread object might be dead; however we can still pass
|
nuclear@3
|
643 // it to RemoveRunningThread since it is only used as a key there.
|
nuclear@3
|
644 ThreadList::RemoveRunningThread(pthread);
|
nuclear@3
|
645 return (void*) result;
|
nuclear@3
|
646 }
|
nuclear@3
|
647
|
nuclear@3
|
648 int Thread::InitAttr = 0;
|
nuclear@3
|
649 pthread_attr_t Thread::Attr;
|
nuclear@3
|
650
|
nuclear@3
|
651 /* static */
|
nuclear@3
|
652 int Thread::GetOSPriority(ThreadPriority p)
|
nuclear@3
|
653 //static inline int MapToSystemPrority(Thread::ThreadPriority p)
|
nuclear@3
|
654 {
|
nuclear@3
|
655 #ifdef OVR_OS_PS3
|
nuclear@3
|
656 switch(p)
|
nuclear@3
|
657 {
|
nuclear@3
|
658 case Thread::CriticalPriority: return 0;
|
nuclear@3
|
659 case Thread::HighestPriority: return 300;
|
nuclear@3
|
660 case Thread::AboveNormalPriority: return 600;
|
nuclear@3
|
661 case Thread::NormalPriority: return 1000;
|
nuclear@3
|
662 case Thread::BelowNormalPriority: return 1500;
|
nuclear@3
|
663 case Thread::LowestPriority: return 2500;
|
nuclear@3
|
664 case Thread::IdlePriority: return 3071;
|
nuclear@3
|
665 } return 1000;
|
nuclear@3
|
666 #else
|
nuclear@3
|
667 OVR_UNUSED(p);
|
nuclear@3
|
668 return -1;
|
nuclear@3
|
669 #endif
|
nuclear@3
|
670 }
|
nuclear@3
|
671
|
nuclear@3
|
672 bool Thread::Start(ThreadState initialState)
|
nuclear@3
|
673 {
|
nuclear@3
|
674 if (initialState == NotRunning)
|
nuclear@3
|
675 return 0;
|
nuclear@3
|
676 if (GetThreadState() != NotRunning)
|
nuclear@3
|
677 {
|
nuclear@3
|
678 OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
|
nuclear@3
|
679 return 0;
|
nuclear@3
|
680 }
|
nuclear@3
|
681
|
nuclear@3
|
682 if (!InitAttr)
|
nuclear@3
|
683 {
|
nuclear@3
|
684 pthread_attr_init(&Attr);
|
nuclear@3
|
685 pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED);
|
nuclear@3
|
686 pthread_attr_setstacksize(&Attr, 128 * 1024);
|
nuclear@3
|
687 sched_param sparam;
|
nuclear@3
|
688 sparam.sched_priority = Thread::GetOSPriority(NormalPriority);
|
nuclear@3
|
689 pthread_attr_setschedparam(&Attr, &sparam);
|
nuclear@3
|
690 InitAttr = 1;
|
nuclear@3
|
691 }
|
nuclear@3
|
692
|
nuclear@3
|
693 ExitCode = 0;
|
nuclear@3
|
694 SuspendCount = 0;
|
nuclear@3
|
695 ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
|
nuclear@3
|
696
|
nuclear@3
|
697 // AddRef to us until the thread is finished
|
nuclear@3
|
698 AddRef();
|
nuclear@3
|
699 ThreadList::AddRunningThread(this);
|
nuclear@3
|
700
|
nuclear@3
|
701 int result;
|
nuclear@3
|
702 if (StackSize != 128 * 1024 || Priority != NormalPriority)
|
nuclear@3
|
703 {
|
nuclear@3
|
704 pthread_attr_t attr;
|
nuclear@3
|
705
|
nuclear@3
|
706 pthread_attr_init(&attr);
|
nuclear@3
|
707 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
nuclear@3
|
708 pthread_attr_setstacksize(&attr, StackSize);
|
nuclear@3
|
709 sched_param sparam;
|
nuclear@3
|
710 sparam.sched_priority = Thread::GetOSPriority(Priority);
|
nuclear@3
|
711 pthread_attr_setschedparam(&attr, &sparam);
|
nuclear@3
|
712 result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this);
|
nuclear@3
|
713 pthread_attr_destroy(&attr);
|
nuclear@3
|
714 }
|
nuclear@3
|
715 else
|
nuclear@3
|
716 result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this);
|
nuclear@3
|
717
|
nuclear@3
|
718 if (result)
|
nuclear@3
|
719 {
|
nuclear@3
|
720 ThreadFlags = 0;
|
nuclear@3
|
721 Release();
|
nuclear@3
|
722 ThreadList::RemoveRunningThread(this);
|
nuclear@3
|
723 return 0;
|
nuclear@3
|
724 }
|
nuclear@3
|
725 return 1;
|
nuclear@3
|
726 }
|
nuclear@3
|
727
|
nuclear@3
|
728
|
nuclear@3
|
729 // Suspend the thread until resumed
|
nuclear@3
|
730 bool Thread::Suspend()
|
nuclear@3
|
731 {
|
nuclear@3
|
732 OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system"));
|
nuclear@3
|
733 return 0;
|
nuclear@3
|
734 }
|
nuclear@3
|
735
|
nuclear@3
|
736 // Resumes currently suspended thread
|
nuclear@3
|
737 bool Thread::Resume()
|
nuclear@3
|
738 {
|
nuclear@3
|
739 return 0;
|
nuclear@3
|
740 }
|
nuclear@3
|
741
|
nuclear@3
|
742
|
nuclear@3
|
743 // Quits with an exit code
|
nuclear@3
|
744 void Thread::Exit(int exitCode)
|
nuclear@3
|
745 {
|
nuclear@3
|
746 // Can only exist the current thread
|
nuclear@3
|
747 // if (GetThread() != this)
|
nuclear@3
|
748 // return;
|
nuclear@3
|
749
|
nuclear@3
|
750 // Call the virtual OnExit function
|
nuclear@3
|
751 OnExit();
|
nuclear@3
|
752
|
nuclear@3
|
753 // Signal this thread object as done and release it's references.
|
nuclear@3
|
754 FinishAndRelease();
|
nuclear@3
|
755 ThreadList::RemoveRunningThread(this);
|
nuclear@3
|
756
|
nuclear@3
|
757 pthread_exit((void *) exitCode);
|
nuclear@3
|
758 }
|
nuclear@3
|
759
|
nuclear@3
|
760 ThreadId GetCurrentThreadId()
|
nuclear@3
|
761 {
|
nuclear@3
|
762 return (void*)pthread_self();
|
nuclear@3
|
763 }
|
nuclear@3
|
764
|
nuclear@3
|
765 // *** Sleep functions
|
nuclear@3
|
766
|
nuclear@3
|
767 /* static */
|
nuclear@3
|
768 bool Thread::Sleep(unsigned secs)
|
nuclear@3
|
769 {
|
nuclear@3
|
770 sleep(secs);
|
nuclear@3
|
771 return 1;
|
nuclear@3
|
772 }
|
nuclear@3
|
773 /* static */
|
nuclear@3
|
774 bool Thread::MSleep(unsigned msecs)
|
nuclear@3
|
775 {
|
nuclear@3
|
776 usleep(msecs*1000);
|
nuclear@3
|
777 return 1;
|
nuclear@3
|
778 }
|
nuclear@3
|
779
|
nuclear@3
|
780 /* static */
|
nuclear@3
|
781 int Thread::GetCPUCount()
|
nuclear@3
|
782 {
|
nuclear@3
|
783 return 1;
|
nuclear@3
|
784 }
|
nuclear@3
|
785
|
nuclear@3
|
786
|
nuclear@3
|
787 #ifdef OVR_OS_PS3
|
nuclear@3
|
788
|
nuclear@3
|
789 sys_lwmutex_attribute_t Lock::LockAttr = { SYS_SYNC_PRIORITY, SYS_SYNC_RECURSIVE };
|
nuclear@3
|
790
|
nuclear@3
|
791 #endif
|
nuclear@3
|
792
|
nuclear@3
|
793 }
|
nuclear@3
|
794
|
nuclear@3
|
795 #endif // OVR_ENABLE_THREADS
|