oculus1

annotate libovr/Src/win32/OVR_ThreadsWinAPI.cpp @ 24:8419d8a13cee

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