oculus1

annotate libovr/Src/win32/OVR_ThreadsWinAPI.cpp @ 1:e2f9e4603129

added LibOVR and started a simple vr wrapper.
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 14 Sep 2013 16:14:59 +0300
parents
children b069a5c27388
rev   line source
nuclear@1 1 /************************************************************************************
nuclear@1 2
nuclear@1 3 Filename : OVR_ThreadsWinAPI.cpp
nuclear@1 4 Platform : WinAPI
nuclear@1 5 Content : Windows specific thread-related (safe) functionality
nuclear@1 6 Created : September 19, 2012
nuclear@1 7 Notes :
nuclear@1 8
nuclear@1 9 Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
nuclear@1 10
nuclear@1 11 Use of this software is subject to the terms of the Oculus license
nuclear@1 12 agreement provided at the time of installation or download, or which
nuclear@1 13 otherwise accompanies this software in either electronic or hard copy form.
nuclear@1 14
nuclear@1 15 ************************************************************************************/
nuclear@1 16
nuclear@1 17 #include "OVR_Threads.h"
nuclear@1 18 #include "OVR_Hash.h"
nuclear@1 19 #include "OVR_Log.h"
nuclear@1 20
nuclear@1 21 #ifdef OVR_ENABLE_THREADS
nuclear@1 22
nuclear@1 23 // For _beginthreadex / _endtheadex
nuclear@1 24 #include <process.h>
nuclear@1 25
nuclear@1 26 namespace OVR {
nuclear@1 27
nuclear@1 28
nuclear@1 29 //-----------------------------------------------------------------------------------
nuclear@1 30 // *** Internal Mutex implementation class
nuclear@1 31
nuclear@1 32 class MutexImpl : public NewOverrideBase
nuclear@1 33 {
nuclear@1 34 // System mutex or semaphore
nuclear@1 35 HANDLE hMutexOrSemaphore;
nuclear@1 36 bool Recursive;
nuclear@1 37 volatile unsigned LockCount;
nuclear@1 38
nuclear@1 39 friend class WaitConditionImpl;
nuclear@1 40
nuclear@1 41 public:
nuclear@1 42 // Constructor/destructor
nuclear@1 43 MutexImpl(bool recursive = 1);
nuclear@1 44 ~MutexImpl();
nuclear@1 45
nuclear@1 46 // Locking functions
nuclear@1 47 void DoLock();
nuclear@1 48 bool TryLock();
nuclear@1 49 void Unlock(Mutex* pmutex);
nuclear@1 50 // Returns 1 if the mutes is currently locked
nuclear@1 51 bool IsLockedByAnotherThread(Mutex* pmutex);
nuclear@1 52 };
nuclear@1 53
nuclear@1 54 // *** Constructor/destructor
nuclear@1 55 MutexImpl::MutexImpl(bool recursive)
nuclear@1 56 {
nuclear@1 57 Recursive = recursive;
nuclear@1 58 LockCount = 0;
nuclear@1 59 hMutexOrSemaphore = Recursive ? CreateMutex(NULL, 0, NULL) : CreateSemaphore(NULL, 1, 1, NULL);
nuclear@1 60 }
nuclear@1 61 MutexImpl::~MutexImpl()
nuclear@1 62 {
nuclear@1 63 CloseHandle(hMutexOrSemaphore);
nuclear@1 64 }
nuclear@1 65
nuclear@1 66
nuclear@1 67 // Lock and try lock
nuclear@1 68 void MutexImpl::DoLock()
nuclear@1 69 {
nuclear@1 70 if (::WaitForSingleObject(hMutexOrSemaphore, INFINITE) != WAIT_OBJECT_0)
nuclear@1 71 return;
nuclear@1 72 LockCount++;
nuclear@1 73 }
nuclear@1 74
nuclear@1 75 bool MutexImpl::TryLock()
nuclear@1 76 {
nuclear@1 77 DWORD ret;
nuclear@1 78 if ((ret=::WaitForSingleObject(hMutexOrSemaphore, 0)) != WAIT_OBJECT_0)
nuclear@1 79 return 0;
nuclear@1 80 LockCount++;
nuclear@1 81 return 1;
nuclear@1 82 }
nuclear@1 83
nuclear@1 84 void MutexImpl::Unlock(Mutex* pmutex)
nuclear@1 85 {
nuclear@1 86 OVR_UNUSED(pmutex);
nuclear@1 87
nuclear@1 88 unsigned lockCount;
nuclear@1 89 LockCount--;
nuclear@1 90 lockCount = LockCount;
nuclear@1 91
nuclear@1 92 // Release mutex
nuclear@1 93 if ((Recursive ? ReleaseMutex(hMutexOrSemaphore) :
nuclear@1 94 ReleaseSemaphore(hMutexOrSemaphore, 1, NULL)) != 0)
nuclear@1 95 {
nuclear@1 96 // This used to call Wait handlers if lockCount == 0.
nuclear@1 97 }
nuclear@1 98 }
nuclear@1 99
nuclear@1 100 bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
nuclear@1 101 {
nuclear@1 102 // There could be multiple interpretations of IsLocked with respect to current thread
nuclear@1 103 if (LockCount == 0)
nuclear@1 104 return 0;
nuclear@1 105 if (!TryLock())
nuclear@1 106 return 1;
nuclear@1 107 Unlock(pmutex);
nuclear@1 108 return 0;
nuclear@1 109 }
nuclear@1 110
nuclear@1 111 /*
nuclear@1 112 bool MutexImpl::IsSignaled() const
nuclear@1 113 {
nuclear@1 114 // An mutex is signaled if it is not locked ANYWHERE
nuclear@1 115 // Note that this is different from IsLockedByAnotherThread function,
nuclear@1 116 // that takes current thread into account
nuclear@1 117 return LockCount == 0;
nuclear@1 118 }
nuclear@1 119 */
nuclear@1 120
nuclear@1 121
nuclear@1 122 // *** Actual Mutex class implementation
nuclear@1 123
nuclear@1 124 Mutex::Mutex(bool recursive)
nuclear@1 125 {
nuclear@1 126 pImpl = new MutexImpl(recursive);
nuclear@1 127 }
nuclear@1 128 Mutex::~Mutex()
nuclear@1 129 {
nuclear@1 130 delete pImpl;
nuclear@1 131 }
nuclear@1 132
nuclear@1 133 // Lock and try lock
nuclear@1 134 void Mutex::DoLock()
nuclear@1 135 {
nuclear@1 136 pImpl->DoLock();
nuclear@1 137 }
nuclear@1 138 bool Mutex::TryLock()
nuclear@1 139 {
nuclear@1 140 return pImpl->TryLock();
nuclear@1 141 }
nuclear@1 142 void Mutex::Unlock()
nuclear@1 143 {
nuclear@1 144 pImpl->Unlock(this);
nuclear@1 145 }
nuclear@1 146 bool Mutex::IsLockedByAnotherThread()
nuclear@1 147 {
nuclear@1 148 return pImpl->IsLockedByAnotherThread(this);
nuclear@1 149 }
nuclear@1 150
nuclear@1 151 //-----------------------------------------------------------------------------------
nuclear@1 152 // ***** Event
nuclear@1 153
nuclear@1 154 bool Event::Wait(unsigned delay)
nuclear@1 155 {
nuclear@1 156 Mutex::Locker lock(&StateMutex);
nuclear@1 157
nuclear@1 158 // Do the correct amount of waiting
nuclear@1 159 if (delay == OVR_WAIT_INFINITE)
nuclear@1 160 {
nuclear@1 161 while(!State)
nuclear@1 162 StateWaitCondition.Wait(&StateMutex);
nuclear@1 163 }
nuclear@1 164 else if (delay)
nuclear@1 165 {
nuclear@1 166 if (!State)
nuclear@1 167 StateWaitCondition.Wait(&StateMutex, delay);
nuclear@1 168 }
nuclear@1 169
nuclear@1 170 bool state = State;
nuclear@1 171 // Take care of temporary 'pulsing' of a state
nuclear@1 172 if (Temporary)
nuclear@1 173 {
nuclear@1 174 Temporary = false;
nuclear@1 175 State = false;
nuclear@1 176 }
nuclear@1 177 return state;
nuclear@1 178 }
nuclear@1 179
nuclear@1 180 void Event::updateState(bool newState, bool newTemp, bool mustNotify)
nuclear@1 181 {
nuclear@1 182 Mutex::Locker lock(&StateMutex);
nuclear@1 183 State = newState;
nuclear@1 184 Temporary = newTemp;
nuclear@1 185 if (mustNotify)
nuclear@1 186 StateWaitCondition.NotifyAll();
nuclear@1 187 }
nuclear@1 188
nuclear@1 189
nuclear@1 190 //-----------------------------------------------------------------------------------
nuclear@1 191 // ***** Win32 Wait Condition Implementation
nuclear@1 192
nuclear@1 193 // Internal implementation class
nuclear@1 194 class WaitConditionImpl : public NewOverrideBase
nuclear@1 195 {
nuclear@1 196 // Event pool entries for extra events
nuclear@1 197 struct EventPoolEntry : public NewOverrideBase
nuclear@1 198 {
nuclear@1 199 HANDLE hEvent;
nuclear@1 200 EventPoolEntry *pNext;
nuclear@1 201 EventPoolEntry *pPrev;
nuclear@1 202 };
nuclear@1 203
nuclear@1 204 Lock WaitQueueLoc;
nuclear@1 205 // Stores free events that can be used later
nuclear@1 206 EventPoolEntry * pFreeEventList;
nuclear@1 207
nuclear@1 208 // A queue of waiting objects to be signaled
nuclear@1 209 EventPoolEntry* pQueueHead;
nuclear@1 210 EventPoolEntry* pQueueTail;
nuclear@1 211
nuclear@1 212 // Allocation functions for free events
nuclear@1 213 EventPoolEntry* GetNewEvent();
nuclear@1 214 void ReleaseEvent(EventPoolEntry* pevent);
nuclear@1 215
nuclear@1 216 // Queue operations
nuclear@1 217 void QueuePush(EventPoolEntry* pentry);
nuclear@1 218 EventPoolEntry* QueuePop();
nuclear@1 219 void QueueFindAndRemove(EventPoolEntry* pentry);
nuclear@1 220
nuclear@1 221 public:
nuclear@1 222
nuclear@1 223 // Constructor/destructor
nuclear@1 224 WaitConditionImpl();
nuclear@1 225 ~WaitConditionImpl();
nuclear@1 226
nuclear@1 227 // Release mutex and wait for condition. The mutex is re-acqured after the wait.
nuclear@1 228 bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
nuclear@1 229
nuclear@1 230 // Notify a condition, releasing at one object waiting
nuclear@1 231 void Notify();
nuclear@1 232 // Notify a condition, releasing all objects waiting
nuclear@1 233 void NotifyAll();
nuclear@1 234 };
nuclear@1 235
nuclear@1 236
nuclear@1 237
nuclear@1 238 WaitConditionImpl::WaitConditionImpl()
nuclear@1 239 {
nuclear@1 240 pFreeEventList = 0;
nuclear@1 241 pQueueHead =
nuclear@1 242 pQueueTail = 0;
nuclear@1 243 }
nuclear@1 244
nuclear@1 245 WaitConditionImpl::~WaitConditionImpl()
nuclear@1 246 {
nuclear@1 247 // Free all the resources
nuclear@1 248 EventPoolEntry* p = pFreeEventList;
nuclear@1 249 EventPoolEntry* pentry;
nuclear@1 250
nuclear@1 251 while(p)
nuclear@1 252 {
nuclear@1 253 // Move to next
nuclear@1 254 pentry = p;
nuclear@1 255 p = p->pNext;
nuclear@1 256 // Delete old
nuclear@1 257 ::CloseHandle(pentry->hEvent);
nuclear@1 258 delete pentry;
nuclear@1 259 }
nuclear@1 260 // Shouldn't we also consider the queue?
nuclear@1 261
nuclear@1 262 // To be safe
nuclear@1 263 pFreeEventList = 0;
nuclear@1 264 pQueueHead =
nuclear@1 265 pQueueTail = 0;
nuclear@1 266 }
nuclear@1 267
nuclear@1 268
nuclear@1 269 // Allocation functions for free events
nuclear@1 270 WaitConditionImpl::EventPoolEntry* WaitConditionImpl::GetNewEvent()
nuclear@1 271 {
nuclear@1 272 EventPoolEntry* pentry;
nuclear@1 273
nuclear@1 274 // If there are any free nodes, use them
nuclear@1 275 if (pFreeEventList)
nuclear@1 276 {
nuclear@1 277 pentry = pFreeEventList;
nuclear@1 278 pFreeEventList = pFreeEventList->pNext;
nuclear@1 279 }
nuclear@1 280 else
nuclear@1 281 {
nuclear@1 282 // Allocate a new node
nuclear@1 283 pentry = new EventPoolEntry;
nuclear@1 284 pentry->pNext = 0;
nuclear@1 285 pentry->pPrev = 0;
nuclear@1 286 // Non-signaled manual event
nuclear@1 287 pentry->hEvent = ::CreateEvent(NULL, TRUE, 0, NULL);
nuclear@1 288 }
nuclear@1 289
nuclear@1 290 return pentry;
nuclear@1 291 }
nuclear@1 292
nuclear@1 293 void WaitConditionImpl::ReleaseEvent(EventPoolEntry* pevent)
nuclear@1 294 {
nuclear@1 295 // Mark event as non-signaled
nuclear@1 296 ::ResetEvent(pevent->hEvent);
nuclear@1 297 // And add it to free pool
nuclear@1 298 pevent->pNext = pFreeEventList;
nuclear@1 299 pevent->pPrev = 0;
nuclear@1 300 pFreeEventList = pevent;
nuclear@1 301 }
nuclear@1 302
nuclear@1 303 // Queue operations
nuclear@1 304 void WaitConditionImpl::QueuePush(EventPoolEntry* pentry)
nuclear@1 305 {
nuclear@1 306 // Items already exist? Just add to tail
nuclear@1 307 if (pQueueTail)
nuclear@1 308 {
nuclear@1 309 pentry->pPrev = pQueueTail;
nuclear@1 310 pQueueTail->pNext = pentry;
nuclear@1 311 pentry->pNext = 0;
nuclear@1 312 pQueueTail = pentry;
nuclear@1 313 }
nuclear@1 314 else
nuclear@1 315 {
nuclear@1 316 // No items in queue
nuclear@1 317 pentry->pNext =
nuclear@1 318 pentry->pPrev = 0;
nuclear@1 319 pQueueHead =
nuclear@1 320 pQueueTail = pentry;
nuclear@1 321 }
nuclear@1 322 }
nuclear@1 323
nuclear@1 324 WaitConditionImpl::EventPoolEntry* WaitConditionImpl::QueuePop()
nuclear@1 325 {
nuclear@1 326 EventPoolEntry* pentry = pQueueHead;
nuclear@1 327
nuclear@1 328 // No items, null pointer
nuclear@1 329 if (pentry)
nuclear@1 330 {
nuclear@1 331 // More items after this one? just grab the first item
nuclear@1 332 if (pQueueHead->pNext)
nuclear@1 333 {
nuclear@1 334 pQueueHead = pentry->pNext;
nuclear@1 335 pQueueHead->pPrev = 0;
nuclear@1 336 }
nuclear@1 337 else
nuclear@1 338 {
nuclear@1 339 // Last item left
nuclear@1 340 pQueueTail =
nuclear@1 341 pQueueHead = 0;
nuclear@1 342 }
nuclear@1 343 }
nuclear@1 344 return pentry;
nuclear@1 345 }
nuclear@1 346
nuclear@1 347 void WaitConditionImpl::QueueFindAndRemove(EventPoolEntry* pentry)
nuclear@1 348 {
nuclear@1 349 // Do an exhaustive search looking for an entry
nuclear@1 350 EventPoolEntry* p = pQueueHead;
nuclear@1 351
nuclear@1 352 while(p)
nuclear@1 353 {
nuclear@1 354 // Entry found? Remove.
nuclear@1 355 if (p == pentry)
nuclear@1 356 {
nuclear@1 357
nuclear@1 358 // Remove the node form the list
nuclear@1 359 // Prev link
nuclear@1 360 if (pentry->pPrev)
nuclear@1 361 pentry->pPrev->pNext = pentry->pNext;
nuclear@1 362 else
nuclear@1 363 pQueueHead = pentry->pNext;
nuclear@1 364 // Next link
nuclear@1 365 if (pentry->pNext)
nuclear@1 366 pentry->pNext->pPrev = pentry->pPrev;
nuclear@1 367 else
nuclear@1 368 pQueueTail = pentry->pPrev;
nuclear@1 369 // Done
nuclear@1 370 return;
nuclear@1 371 }
nuclear@1 372
nuclear@1 373 // Move to next item
nuclear@1 374 p = p->pNext;
nuclear@1 375 }
nuclear@1 376 }
nuclear@1 377
nuclear@1 378
nuclear@1 379 bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
nuclear@1 380 {
nuclear@1 381 bool result = 0;
nuclear@1 382 unsigned i;
nuclear@1 383 unsigned lockCount = pmutex->pImpl->LockCount;
nuclear@1 384 EventPoolEntry* pentry;
nuclear@1 385
nuclear@1 386 // Mutex must have been locked
nuclear@1 387 if (lockCount == 0)
nuclear@1 388 return 0;
nuclear@1 389
nuclear@1 390 // Add an object to the wait queue
nuclear@1 391 WaitQueueLoc.DoLock();
nuclear@1 392 QueuePush(pentry = GetNewEvent());
nuclear@1 393 WaitQueueLoc.Unlock();
nuclear@1 394
nuclear@1 395 // Finally, release a mutex or semaphore
nuclear@1 396 if (pmutex->pImpl->Recursive)
nuclear@1 397 {
nuclear@1 398 // Release the recursive mutex N times
nuclear@1 399 pmutex->pImpl->LockCount = 0;
nuclear@1 400 for(i=0; i<lockCount; i++)
nuclear@1 401 ::ReleaseMutex(pmutex->pImpl->hMutexOrSemaphore);
nuclear@1 402 }
nuclear@1 403 else
nuclear@1 404 {
nuclear@1 405 pmutex->pImpl->LockCount = 0;
nuclear@1 406 ::ReleaseSemaphore(pmutex->pImpl->hMutexOrSemaphore, 1, NULL);
nuclear@1 407 }
nuclear@1 408
nuclear@1 409 // Note that there is a gap here between mutex.Unlock() and Wait(). However,
nuclear@1 410 // if notify() comes in at this point in the other thread it will set our
nuclear@1 411 // corresponding event so wait will just fall through, as expected.
nuclear@1 412
nuclear@1 413 // Block and wait on the event
nuclear@1 414 DWORD waitResult = ::WaitForSingleObject(pentry->hEvent,
nuclear@1 415 (delay == OVR_WAIT_INFINITE) ? INFINITE : delay);
nuclear@1 416 /*
nuclear@1 417 repeat_wait:
nuclear@1 418 DWORD waitResult =
nuclear@1 419
nuclear@1 420 ::MsgWaitForMultipleObjects(1, &pentry->hEvent, FALSE,
nuclear@1 421 (delay == OVR_WAIT_INFINITE) ? INFINITE : delay,
nuclear@1 422 QS_ALLINPUT);
nuclear@1 423 */
nuclear@1 424
nuclear@1 425 WaitQueueLoc.DoLock();
nuclear@1 426 switch(waitResult)
nuclear@1 427 {
nuclear@1 428 case WAIT_ABANDONED:
nuclear@1 429 case WAIT_OBJECT_0:
nuclear@1 430 result = 1;
nuclear@1 431 // Wait was successful, therefore the event entry should already be removed
nuclear@1 432 // So just add entry back to a free list
nuclear@1 433 ReleaseEvent(pentry);
nuclear@1 434 break;
nuclear@1 435 /*
nuclear@1 436 case WAIT_OBJECT_0 + 1:
nuclear@1 437 // Messages in WINDOWS queue
nuclear@1 438 {
nuclear@1 439 MSG msg;
nuclear@1 440 PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
nuclear@1 441 WaitQueueLoc.Unlock();
nuclear@1 442 goto repeat_wait;
nuclear@1 443 }
nuclear@1 444 break; */
nuclear@1 445 default:
nuclear@1 446 // Timeout, our entry should still be in a queue
nuclear@1 447 QueueFindAndRemove(pentry);
nuclear@1 448 ReleaseEvent(pentry);
nuclear@1 449 }
nuclear@1 450 WaitQueueLoc.Unlock();
nuclear@1 451
nuclear@1 452 // Re-aquire the mutex
nuclear@1 453 for(i=0; i<lockCount; i++)
nuclear@1 454 pmutex->DoLock();
nuclear@1 455
nuclear@1 456 // Return the result
nuclear@1 457 return result;
nuclear@1 458 }
nuclear@1 459
nuclear@1 460 // Notify a condition, releasing the least object in a queue
nuclear@1 461 void WaitConditionImpl::Notify()
nuclear@1 462 {
nuclear@1 463 Lock::Locker lock(&WaitQueueLoc);
nuclear@1 464
nuclear@1 465 // Pop last entry & signal it
nuclear@1 466 EventPoolEntry* pentry = QueuePop();
nuclear@1 467 if (pentry)
nuclear@1 468 ::SetEvent(pentry->hEvent);
nuclear@1 469 }
nuclear@1 470
nuclear@1 471 // Notify a condition, releasing all objects waiting
nuclear@1 472 void WaitConditionImpl::NotifyAll()
nuclear@1 473 {
nuclear@1 474 Lock::Locker lock(&WaitQueueLoc);
nuclear@1 475
nuclear@1 476 // Pop and signal all events
nuclear@1 477 // NOTE : There is no need to release the events, it's the waiters job to do so
nuclear@1 478 EventPoolEntry* pentry = QueuePop();
nuclear@1 479 while (pentry)
nuclear@1 480 {
nuclear@1 481 ::SetEvent(pentry->hEvent);
nuclear@1 482 pentry = QueuePop();
nuclear@1 483 }
nuclear@1 484 }
nuclear@1 485
nuclear@1 486
nuclear@1 487
nuclear@1 488 // *** Actual implementation of WaitCondition
nuclear@1 489
nuclear@1 490 WaitCondition::WaitCondition()
nuclear@1 491 {
nuclear@1 492 pImpl = new WaitConditionImpl;
nuclear@1 493 }
nuclear@1 494 WaitCondition::~WaitCondition()
nuclear@1 495 {
nuclear@1 496 delete pImpl;
nuclear@1 497 }
nuclear@1 498
nuclear@1 499 // Wait without a mutex
nuclear@1 500 bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
nuclear@1 501 {
nuclear@1 502 return pImpl->Wait(pmutex, delay);
nuclear@1 503 }
nuclear@1 504 // Notification
nuclear@1 505 void WaitCondition::Notify()
nuclear@1 506 {
nuclear@1 507 pImpl->Notify();
nuclear@1 508 }
nuclear@1 509 void WaitCondition::NotifyAll()
nuclear@1 510 {
nuclear@1 511 pImpl->NotifyAll();
nuclear@1 512 }
nuclear@1 513
nuclear@1 514
nuclear@1 515
nuclear@1 516 //-----------------------------------------------------------------------------------
nuclear@1 517 // ***** Thread Class
nuclear@1 518
nuclear@1 519 // Per-thread variable
nuclear@1 520 // MA: Don't use TLS for now - portability issues with DLLs, etc.
nuclear@1 521 /*
nuclear@1 522 #if !defined(OVR_CC_MSVC) || (OVR_CC_MSVC < 1300)
nuclear@1 523 __declspec(thread) Thread* pCurrentThread = 0;
nuclear@1 524 #else
nuclear@1 525 #pragma data_seg(".tls$")
nuclear@1 526 __declspec(thread) Thread* pCurrentThread = 0;
nuclear@1 527 #pragma data_seg(".rwdata")
nuclear@1 528 #endif
nuclear@1 529 */
nuclear@1 530
nuclear@1 531 // *** Thread constructors.
nuclear@1 532
nuclear@1 533 Thread::Thread(UPInt stackSize, int processor)
nuclear@1 534 {
nuclear@1 535 CreateParams params;
nuclear@1 536 params.stackSize = stackSize;
nuclear@1 537 params.processor = processor;
nuclear@1 538 Init(params);
nuclear@1 539 }
nuclear@1 540
nuclear@1 541 Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
nuclear@1 542 int processor, Thread::ThreadState initialState)
nuclear@1 543 {
nuclear@1 544 CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
nuclear@1 545 Init(params);
nuclear@1 546 }
nuclear@1 547
nuclear@1 548 Thread::Thread(const CreateParams& params)
nuclear@1 549 {
nuclear@1 550 Init(params);
nuclear@1 551 }
nuclear@1 552 void Thread::Init(const CreateParams& params)
nuclear@1 553 {
nuclear@1 554 // Clear the variables
nuclear@1 555 ThreadFlags = 0;
nuclear@1 556 ThreadHandle = 0;
nuclear@1 557 IdValue = 0;
nuclear@1 558 ExitCode = 0;
nuclear@1 559 SuspendCount = 0;
nuclear@1 560 StackSize = params.stackSize;
nuclear@1 561 Processor = params.processor;
nuclear@1 562 Priority = params.priority;
nuclear@1 563
nuclear@1 564 // Clear Function pointers
nuclear@1 565 ThreadFunction = params.threadFunction;
nuclear@1 566 UserHandle = params.userHandle;
nuclear@1 567 if (params.initialState != NotRunning)
nuclear@1 568 Start(params.initialState);
nuclear@1 569
nuclear@1 570 }
nuclear@1 571
nuclear@1 572 Thread::~Thread()
nuclear@1 573 {
nuclear@1 574 // Thread should not running while object is being destroyed,
nuclear@1 575 // this would indicate ref-counting issue.
nuclear@1 576 //OVR_ASSERT(IsRunning() == 0);
nuclear@1 577
nuclear@1 578 // Clean up thread.
nuclear@1 579 CleanupSystemThread();
nuclear@1 580 ThreadHandle = 0;
nuclear@1 581 }
nuclear@1 582
nuclear@1 583
nuclear@1 584 // *** Overridable User functions.
nuclear@1 585
nuclear@1 586 // Default Run implementation
nuclear@1 587 int Thread::Run()
nuclear@1 588 {
nuclear@1 589 // Call pointer to function, if available.
nuclear@1 590 return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
nuclear@1 591 }
nuclear@1 592 void Thread::OnExit()
nuclear@1 593 {
nuclear@1 594 }
nuclear@1 595
nuclear@1 596 // Finishes the thread and releases internal reference to it.
nuclear@1 597 void Thread::FinishAndRelease()
nuclear@1 598 {
nuclear@1 599 // Note: thread must be US.
nuclear@1 600 ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
nuclear@1 601 ThreadFlags |= OVR_THREAD_FINISHED;
nuclear@1 602
nuclear@1 603 // Release our reference; this is equivalent to 'delete this'
nuclear@1 604 // from the point of view of our thread.
nuclear@1 605 Release();
nuclear@1 606 }
nuclear@1 607
nuclear@1 608
nuclear@1 609 // *** ThreadList - used to tack all created threads
nuclear@1 610
nuclear@1 611 class ThreadList : public NewOverrideBase
nuclear@1 612 {
nuclear@1 613 //------------------------------------------------------------------------
nuclear@1 614 struct ThreadHashOp
nuclear@1 615 {
nuclear@1 616 UPInt operator()(const Thread* ptr)
nuclear@1 617 {
nuclear@1 618 return (((UPInt)ptr) >> 6) ^ (UPInt)ptr;
nuclear@1 619 }
nuclear@1 620 };
nuclear@1 621
nuclear@1 622 HashSet<Thread*, ThreadHashOp> ThreadSet;
nuclear@1 623 Mutex ThreadMutex;
nuclear@1 624 WaitCondition ThreadsEmpty;
nuclear@1 625 // Track the root thread that created us.
nuclear@1 626 ThreadId RootThreadId;
nuclear@1 627
nuclear@1 628 static ThreadList* volatile pRunningThreads;
nuclear@1 629
nuclear@1 630 void addThread(Thread *pthread)
nuclear@1 631 {
nuclear@1 632 Mutex::Locker lock(&ThreadMutex);
nuclear@1 633 ThreadSet.Add(pthread);
nuclear@1 634 }
nuclear@1 635
nuclear@1 636 void removeThread(Thread *pthread)
nuclear@1 637 {
nuclear@1 638 Mutex::Locker lock(&ThreadMutex);
nuclear@1 639 ThreadSet.Remove(pthread);
nuclear@1 640 if (ThreadSet.GetSize() == 0)
nuclear@1 641 ThreadsEmpty.Notify();
nuclear@1 642 }
nuclear@1 643
nuclear@1 644 void finishAllThreads()
nuclear@1 645 {
nuclear@1 646 // Only original root thread can call this.
nuclear@1 647 OVR_ASSERT(GetCurrentThreadId() == RootThreadId);
nuclear@1 648
nuclear@1 649 Mutex::Locker lock(&ThreadMutex);
nuclear@1 650 while (ThreadSet.GetSize() != 0)
nuclear@1 651 ThreadsEmpty.Wait(&ThreadMutex);
nuclear@1 652 }
nuclear@1 653
nuclear@1 654 public:
nuclear@1 655
nuclear@1 656 ThreadList()
nuclear@1 657 {
nuclear@1 658 RootThreadId = GetCurrentThreadId();
nuclear@1 659 }
nuclear@1 660 ~ThreadList() { }
nuclear@1 661
nuclear@1 662
nuclear@1 663 static void AddRunningThread(Thread *pthread)
nuclear@1 664 {
nuclear@1 665 // Non-atomic creation ok since only the root thread
nuclear@1 666 if (!pRunningThreads)
nuclear@1 667 {
nuclear@1 668 pRunningThreads = new ThreadList;
nuclear@1 669 OVR_ASSERT(pRunningThreads);
nuclear@1 670 }
nuclear@1 671 pRunningThreads->addThread(pthread);
nuclear@1 672 }
nuclear@1 673
nuclear@1 674 // NOTE: 'pthread' might be a dead pointer when this is
nuclear@1 675 // called so it should not be accessed; it is only used
nuclear@1 676 // for removal.
nuclear@1 677 static void RemoveRunningThread(Thread *pthread)
nuclear@1 678 {
nuclear@1 679 OVR_ASSERT(pRunningThreads);
nuclear@1 680 pRunningThreads->removeThread(pthread);
nuclear@1 681 }
nuclear@1 682
nuclear@1 683 static void FinishAllThreads()
nuclear@1 684 {
nuclear@1 685 // This is ok because only root thread can wait for other thread finish.
nuclear@1 686 if (pRunningThreads)
nuclear@1 687 {
nuclear@1 688 pRunningThreads->finishAllThreads();
nuclear@1 689 delete pRunningThreads;
nuclear@1 690 pRunningThreads = 0;
nuclear@1 691 }
nuclear@1 692 }
nuclear@1 693 };
nuclear@1 694
nuclear@1 695 // By default, we have no thread list.
nuclear@1 696 ThreadList* volatile ThreadList::pRunningThreads = 0;
nuclear@1 697
nuclear@1 698
nuclear@1 699 // FinishAllThreads - exposed publicly in Thread.
nuclear@1 700 void Thread::FinishAllThreads()
nuclear@1 701 {
nuclear@1 702 ThreadList::FinishAllThreads();
nuclear@1 703 }
nuclear@1 704
nuclear@1 705
nuclear@1 706 // *** Run override
nuclear@1 707
nuclear@1 708 int Thread::PRun()
nuclear@1 709 {
nuclear@1 710 // Suspend us on start, if requested
nuclear@1 711 if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
nuclear@1 712 {
nuclear@1 713 Suspend();
nuclear@1 714 ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
nuclear@1 715 }
nuclear@1 716
nuclear@1 717 // Call the virtual run function
nuclear@1 718 ExitCode = Run();
nuclear@1 719 return ExitCode;
nuclear@1 720 }
nuclear@1 721
nuclear@1 722
nuclear@1 723
nuclear@1 724 /* MA: Don't use TLS for now.
nuclear@1 725
nuclear@1 726 // Static function to return a pointer to the current thread
nuclear@1 727 void Thread::InitCurrentThread(Thread *pthread)
nuclear@1 728 {
nuclear@1 729 pCurrentThread = pthread;
nuclear@1 730 }
nuclear@1 731
nuclear@1 732 // Static function to return a pointer to the current thread
nuclear@1 733 Thread* Thread::GetThread()
nuclear@1 734 {
nuclear@1 735 return pCurrentThread;
nuclear@1 736 }
nuclear@1 737 */
nuclear@1 738
nuclear@1 739
nuclear@1 740 // *** User overridables
nuclear@1 741
nuclear@1 742 bool Thread::GetExitFlag() const
nuclear@1 743 {
nuclear@1 744 return (ThreadFlags & OVR_THREAD_EXIT) != 0;
nuclear@1 745 }
nuclear@1 746
nuclear@1 747 void Thread::SetExitFlag(bool exitFlag)
nuclear@1 748 {
nuclear@1 749 // The below is atomic since ThreadFlags is AtomicInt.
nuclear@1 750 if (exitFlag)
nuclear@1 751 ThreadFlags |= OVR_THREAD_EXIT;
nuclear@1 752 else
nuclear@1 753 ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
nuclear@1 754 }
nuclear@1 755
nuclear@1 756
nuclear@1 757 // Determines whether the thread was running and is now finished
nuclear@1 758 bool Thread::IsFinished() const
nuclear@1 759 {
nuclear@1 760 return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
nuclear@1 761 }
nuclear@1 762 // Determines whether the thread is suspended
nuclear@1 763 bool Thread::IsSuspended() const
nuclear@1 764 {
nuclear@1 765 return SuspendCount > 0;
nuclear@1 766 }
nuclear@1 767 // Returns current thread state
nuclear@1 768 Thread::ThreadState Thread::GetThreadState() const
nuclear@1 769 {
nuclear@1 770 if (IsSuspended())
nuclear@1 771 return Suspended;
nuclear@1 772 if (ThreadFlags & OVR_THREAD_STARTED)
nuclear@1 773 return Running;
nuclear@1 774 return NotRunning;
nuclear@1 775 }
nuclear@1 776
nuclear@1 777
nuclear@1 778
nuclear@1 779 // ***** Thread management
nuclear@1 780 /* static */
nuclear@1 781 int Thread::GetOSPriority(ThreadPriority p)
nuclear@1 782 {
nuclear@1 783 switch(p)
nuclear@1 784 {
nuclear@1 785 case Thread::CriticalPriority: return THREAD_PRIORITY_TIME_CRITICAL;
nuclear@1 786 case Thread::HighestPriority: return THREAD_PRIORITY_HIGHEST;
nuclear@1 787 case Thread::AboveNormalPriority: return THREAD_PRIORITY_ABOVE_NORMAL;
nuclear@1 788 case Thread::NormalPriority: return THREAD_PRIORITY_NORMAL;
nuclear@1 789 case Thread::BelowNormalPriority: return THREAD_PRIORITY_BELOW_NORMAL;
nuclear@1 790 case Thread::LowestPriority: return THREAD_PRIORITY_LOWEST;
nuclear@1 791 case Thread::IdlePriority: return THREAD_PRIORITY_IDLE;
nuclear@1 792 }
nuclear@1 793 return THREAD_PRIORITY_NORMAL;
nuclear@1 794 }
nuclear@1 795
nuclear@1 796 // The actual first function called on thread start
nuclear@1 797 unsigned WINAPI Thread_Win32StartFn(void * phandle)
nuclear@1 798 {
nuclear@1 799 Thread * pthread = (Thread*)phandle;
nuclear@1 800 if (pthread->Processor != -1)
nuclear@1 801 {
nuclear@1 802 DWORD_PTR ret = SetThreadAffinityMask(GetCurrentThread(), (DWORD)pthread->Processor);
nuclear@1 803 if (ret == 0)
nuclear@1 804 OVR_DEBUG_LOG(("Could not set hardware processor for the thread"));
nuclear@1 805 }
nuclear@1 806 BOOL ret = ::SetThreadPriority(GetCurrentThread(), Thread::GetOSPriority(pthread->Priority));
nuclear@1 807 if (ret == 0)
nuclear@1 808 OVR_DEBUG_LOG(("Could not set thread priority"));
nuclear@1 809 OVR_UNUSED(ret);
nuclear@1 810
nuclear@1 811 // Ensure that ThreadId is assigned once thread is running, in case
nuclear@1 812 // beginthread hasn't filled it in yet.
nuclear@1 813 pthread->IdValue = (ThreadId)::GetCurrentThreadId();
nuclear@1 814
nuclear@1 815 DWORD result = pthread->PRun();
nuclear@1 816 // Signal the thread as done and release it atomically.
nuclear@1 817 pthread->FinishAndRelease();
nuclear@1 818 // At this point Thread object might be dead; however we can still pass
nuclear@1 819 // it to RemoveRunningThread since it is only used as a key there.
nuclear@1 820 ThreadList::RemoveRunningThread(pthread);
nuclear@1 821 return (unsigned) result;
nuclear@1 822 }
nuclear@1 823
nuclear@1 824 bool Thread::Start(ThreadState initialState)
nuclear@1 825 {
nuclear@1 826 if (initialState == NotRunning)
nuclear@1 827 return 0;
nuclear@1 828 if (GetThreadState() != NotRunning)
nuclear@1 829 {
nuclear@1 830 OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
nuclear@1 831 return 0;
nuclear@1 832 }
nuclear@1 833
nuclear@1 834 // Free old thread handle before creating the new one
nuclear@1 835 CleanupSystemThread();
nuclear@1 836
nuclear@1 837 // AddRef to us until the thread is finished.
nuclear@1 838 AddRef();
nuclear@1 839 ThreadList::AddRunningThread(this);
nuclear@1 840
nuclear@1 841 ExitCode = 0;
nuclear@1 842 SuspendCount = 0;
nuclear@1 843 ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
nuclear@1 844 ThreadHandle = (HANDLE) _beginthreadex(0, (unsigned)StackSize,
nuclear@1 845 Thread_Win32StartFn, this, 0, (unsigned*)&IdValue);
nuclear@1 846
nuclear@1 847 // Failed? Fail the function
nuclear@1 848 if (ThreadHandle == 0)
nuclear@1 849 {
nuclear@1 850 ThreadFlags = 0;
nuclear@1 851 Release();
nuclear@1 852 ThreadList::RemoveRunningThread(this);
nuclear@1 853 return 0;
nuclear@1 854 }
nuclear@1 855 return 1;
nuclear@1 856 }
nuclear@1 857
nuclear@1 858
nuclear@1 859 // Suspend the thread until resumed
nuclear@1 860 bool Thread::Suspend()
nuclear@1 861 {
nuclear@1 862 // Can't suspend a thread that wasn't started
nuclear@1 863 if (!(ThreadFlags & OVR_THREAD_STARTED))
nuclear@1 864 return 0;
nuclear@1 865
nuclear@1 866 if (::SuspendThread(ThreadHandle) != 0xFFFFFFFF)
nuclear@1 867 {
nuclear@1 868 SuspendCount++;
nuclear@1 869 return 1;
nuclear@1 870 }
nuclear@1 871 return 0;
nuclear@1 872 }
nuclear@1 873
nuclear@1 874 // Resumes currently suspended thread
nuclear@1 875 bool Thread::Resume()
nuclear@1 876 {
nuclear@1 877 // Can't suspend a thread that wasn't started
nuclear@1 878 if (!(ThreadFlags & OVR_THREAD_STARTED))
nuclear@1 879 return 0;
nuclear@1 880
nuclear@1 881 // Decrement count, and resume thread if it is 0
nuclear@1 882 SInt32 oldCount = SuspendCount.ExchangeAdd_Acquire(-1);
nuclear@1 883 if (oldCount >= 1)
nuclear@1 884 {
nuclear@1 885 if (oldCount == 1)
nuclear@1 886 {
nuclear@1 887 if (::ResumeThread(ThreadHandle) != 0xFFFFFFFF)
nuclear@1 888 return 1;
nuclear@1 889 }
nuclear@1 890 else
nuclear@1 891 {
nuclear@1 892 return 1;
nuclear@1 893 }
nuclear@1 894 }
nuclear@1 895 return 0;
nuclear@1 896 }
nuclear@1 897
nuclear@1 898
nuclear@1 899 // Quits with an exit code
nuclear@1 900 void Thread::Exit(int exitCode)
nuclear@1 901 {
nuclear@1 902 // Can only exist the current thread.
nuclear@1 903 // MA: Don't use TLS for now.
nuclear@1 904 //if (GetThread() != this)
nuclear@1 905 // return;
nuclear@1 906
nuclear@1 907 // Call the virtual OnExit function.
nuclear@1 908 OnExit();
nuclear@1 909
nuclear@1 910 // Signal this thread object as done and release it's references.
nuclear@1 911 FinishAndRelease();
nuclear@1 912 ThreadList::RemoveRunningThread(this);
nuclear@1 913
nuclear@1 914 // Call the exit function.
nuclear@1 915 _endthreadex((unsigned)exitCode);
nuclear@1 916 }
nuclear@1 917
nuclear@1 918
nuclear@1 919 void Thread::CleanupSystemThread()
nuclear@1 920 {
nuclear@1 921 if (ThreadHandle != 0)
nuclear@1 922 {
nuclear@1 923 ::CloseHandle(ThreadHandle);
nuclear@1 924 ThreadHandle = 0;
nuclear@1 925 }
nuclear@1 926 }
nuclear@1 927
nuclear@1 928 // *** Sleep functions
nuclear@1 929 // static
nuclear@1 930 bool Thread::Sleep(unsigned secs)
nuclear@1 931 {
nuclear@1 932 ::Sleep(secs*1000);
nuclear@1 933 return 1;
nuclear@1 934 }
nuclear@1 935
nuclear@1 936 // static
nuclear@1 937 bool Thread::MSleep(unsigned msecs)
nuclear@1 938 {
nuclear@1 939 ::Sleep(msecs);
nuclear@1 940 return 1;
nuclear@1 941 }
nuclear@1 942
nuclear@1 943 void Thread::SetThreadName( const char* name )
nuclear@1 944 {
nuclear@1 945 #if !defined(OVR_BUILD_SHIPPING) || defined(OVR_BUILD_PROFILING)
nuclear@1 946 // Looks ugly, but it is the recommended way to name a thread.
nuclear@1 947 typedef struct tagTHREADNAME_INFO {
nuclear@1 948 DWORD dwType; // Must be 0x1000
nuclear@1 949 LPCSTR szName; // Pointer to name (in user address space)
nuclear@1 950 DWORD dwThreadID; // Thread ID (-1 for caller thread)
nuclear@1 951 DWORD dwFlags; // Reserved for future use; must be zero
nuclear@1 952 } THREADNAME_INFO;
nuclear@1 953
nuclear@1 954 THREADNAME_INFO info;
nuclear@1 955
nuclear@1 956 info.dwType = 0x1000;
nuclear@1 957 info.szName = name;
nuclear@1 958 info.dwThreadID = reinterpret_cast<DWORD>(GetThreadId());
nuclear@1 959 info.dwFlags = 0;
nuclear@1 960
nuclear@1 961 __try
nuclear@1 962 {
nuclear@1 963 #ifdef _WIN64
nuclear@1 964 RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR *)&info );
nuclear@1 965 #else
nuclear@1 966 RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info );
nuclear@1 967 #endif
nuclear@1 968 }
nuclear@1 969 __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER )
nuclear@1 970 {
nuclear@1 971 }
nuclear@1 972 #endif // OVR_BUILD_SHIPPING
nuclear@1 973 }
nuclear@1 974
nuclear@1 975 // static
nuclear@1 976 int Thread::GetCPUCount()
nuclear@1 977 {
nuclear@1 978 SYSTEM_INFO sysInfo;
nuclear@1 979 GetSystemInfo(&sysInfo);
nuclear@1 980 return (int) sysInfo.dwNumberOfProcessors;
nuclear@1 981 }
nuclear@1 982
nuclear@1 983 // Returns the unique Id of a thread it is called on, intended for
nuclear@1 984 // comparison purposes.
nuclear@1 985 ThreadId GetCurrentThreadId()
nuclear@1 986 {
nuclear@1 987 return (ThreadId)::GetCurrentThreadId();
nuclear@1 988 }
nuclear@1 989
nuclear@1 990 } // OVR
nuclear@1 991
nuclear@1 992 #endif
nuclear@1 993
nuclear@1 994