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