eathread_mutex_pc.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "EABase/eabase.h"
  5. #include "eathread/eathread_mutex.h"
  6. #include "eathread/eathread.h"
  7. #if defined(EA_PLATFORM_MICROSOFT)
  8. EA_DISABLE_ALL_VC_WARNINGS()
  9. #include <Windows.h>
  10. EA_RESTORE_ALL_VC_WARNINGS()
  11. #endif
  12. #ifdef CreateMutex
  13. #undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW.
  14. #endif
  15. #if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE
  16. #if defined(EA_PLATFORM_WINDOWS)
  17. extern "C" WINBASEAPI BOOL WINAPI TryEnterCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);
  18. #endif
  19. EAMutexData::EAMutexData()
  20. : mnLockCount(0), mbIntraProcess(true)
  21. {
  22. #if EAT_ASSERT_ENABLED
  23. mThreadId = EA::Thread::kThreadIdInvalid;
  24. mSysThreadId = EA::Thread::kSysThreadIdInvalid;
  25. #endif
  26. ::memset(&mData, 0, sizeof(mData));
  27. }
  28. EA::Thread::MutexParameters::MutexParameters(bool bIntraProcess, const char* pName)
  29. : mbIntraProcess(bIntraProcess)
  30. {
  31. if(pName)
  32. {
  33. strncpy(mName, pName, sizeof(mName)-1);
  34. mName[sizeof(mName)-1] = 0;
  35. }
  36. else
  37. mName[0] = 0;
  38. }
  39. EA::Thread::Mutex::Mutex(const MutexParameters* pMutexParameters, bool bDefaultParameters)
  40. {
  41. if(!pMutexParameters && bDefaultParameters)
  42. {
  43. MutexParameters parameters;
  44. Init(&parameters);
  45. }
  46. else
  47. Init(pMutexParameters);
  48. }
  49. EA::Thread::Mutex::~Mutex()
  50. {
  51. EAT_ASSERT(mMutexData.mnLockCount == 0);
  52. // Consider doing something to verify the mutex object has been initialized.
  53. #if defined(EA_PLATFORM_WINDOWS)
  54. if(mMutexData.mbIntraProcess)
  55. DeleteCriticalSection((CRITICAL_SECTION*)mMutexData.mData);
  56. else
  57. CloseHandle(*(HANDLE*)mMutexData.mData);
  58. #else
  59. DeleteCriticalSection((CRITICAL_SECTION*)mMutexData.mData);
  60. #endif
  61. }
  62. bool EA::Thread::Mutex::Init(const MutexParameters* pMutexParameters)
  63. {
  64. // Make sure that internal structure is big enough to hold critical section data.
  65. // If this assert fires, please adjust MUTEX_PLATFORM_DATA_SIZE in eathread_mutex.h accordingly.
  66. EAT_COMPILETIME_ASSERT(sizeof(CRITICAL_SECTION) <= (MUTEX_PLATFORM_DATA_SIZE / sizeof(uint64_t) * sizeof(uint64_t)));
  67. EAT_COMPILETIME_ASSERT(sizeof(HANDLE) <= MUTEX_PLATFORM_DATA_SIZE);
  68. if(pMutexParameters)
  69. {
  70. mMutexData.mnLockCount = 0;
  71. #if defined(EA_PLATFORM_WINDOWS)
  72. mMutexData.mbIntraProcess = pMutexParameters->mbIntraProcess;
  73. if(mMutexData.mbIntraProcess)
  74. {
  75. // We use InitializeCriticalSectionAndSpinCount, as that has resulted in improved performance in practice on multiprocessors systems.
  76. int rv = InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION*)mMutexData.mData, 256);
  77. EAT_ASSERT(rv != 0);
  78. EA_UNUSED(rv);
  79. return true;
  80. }
  81. else
  82. {
  83. EAT_COMPILETIME_ASSERT(sizeof(pMutexParameters->mName) <= MAX_PATH);
  84. *(HANDLE*)mMutexData.mData = ::CreateMutexA(NULL, false, pMutexParameters->mName[0] ? pMutexParameters->mName : NULL);
  85. EAT_ASSERT(*(HANDLE*)mMutexData.mData != 0);
  86. return *(HANDLE*)mMutexData.mData != 0;
  87. }
  88. #else
  89. // We use InitializeCriticalSectionAndSpinCount, as that has resulted in improved performance in practice on multiprocessors systems.
  90. InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION*)mMutexData.mData, 256);
  91. return true;
  92. #endif
  93. }
  94. return false;
  95. }
  96. #pragma warning(push)
  97. #pragma warning(disable: 4706) // disable warning about assignment within a conditional expression
  98. int EA::Thread::Mutex::Lock(const ThreadTime& timeoutAbsolute)
  99. {
  100. EAT_ASSERT(mMutexData.mnLockCount < 100000);
  101. #if defined(EA_PLATFORM_WINDOWS) // Non-Windows is always assumed to be intra-process.
  102. if(mMutexData.mbIntraProcess)
  103. {
  104. #endif
  105. if(timeoutAbsolute == kTimeoutNone)
  106. EnterCriticalSection((CRITICAL_SECTION*)mMutexData.mData);
  107. else
  108. {
  109. // To consider: Have a pathway for kTimeoutImmediate which doesn't check the current time.
  110. while(!TryEnterCriticalSection((CRITICAL_SECTION*)mMutexData.mData))
  111. {
  112. if(GetThreadTime() >= timeoutAbsolute)
  113. return kResultTimeout;
  114. Sleep(1);
  115. }
  116. }
  117. #if defined(EA_PLATFORM_WINDOWS)
  118. }
  119. else
  120. {
  121. EAT_ASSERT(*(HANDLE*)mMutexData.mData != 0);
  122. const DWORD dw = ::WaitForSingleObject(*(HANDLE*)mMutexData.mData, RelativeTimeoutFromAbsoluteTimeout(timeoutAbsolute));
  123. if(dw == WAIT_TIMEOUT)
  124. return kResultTimeout;
  125. if(dw != WAIT_OBJECT_0)
  126. {
  127. EAT_ASSERT(false);
  128. return kResultError;
  129. }
  130. }
  131. #endif
  132. EAT_ASSERT((mMutexData.mSysThreadId = EA::Thread::GetSysThreadId()) != kSysThreadIdInvalid);
  133. EAT_ASSERT(mMutexData.mnLockCount >= 0);
  134. return ++mMutexData.mnLockCount; // This is safe to do because we have the lock.
  135. }
  136. #pragma warning(pop)
  137. int EA::Thread::Mutex::Unlock()
  138. {
  139. EAT_ASSERT(mMutexData.mSysThreadId == EA::Thread::GetSysThreadId());
  140. EAT_ASSERT(mMutexData.mnLockCount > 0);
  141. const int nReturnValue(--mMutexData.mnLockCount); // This is safe to do because we have the lock.
  142. #if defined(EA_PLATFORM_WINDOWS)
  143. if(mMutexData.mbIntraProcess)
  144. LeaveCriticalSection((CRITICAL_SECTION*)mMutexData.mData);
  145. else
  146. {
  147. EAT_ASSERT(*(HANDLE*)mMutexData.mData != 0);
  148. ReleaseMutex(*(HANDLE*)mMutexData.mData);
  149. }
  150. #else
  151. LeaveCriticalSection((CRITICAL_SECTION*)mMutexData.mData);
  152. #endif
  153. return nReturnValue;
  154. }
  155. int EA::Thread::Mutex::GetLockCount() const
  156. {
  157. return mMutexData.mnLockCount;
  158. }
  159. bool EA::Thread::Mutex::HasLock() const
  160. {
  161. #if EAT_ASSERT_ENABLED
  162. return (mMutexData.mnLockCount > 0) && (mMutexData.mSysThreadId == EA::Thread::GetSysThreadId());
  163. #else
  164. return (mMutexData.mnLockCount > 0); // This is the best we can do, though it is of limited use, since it doesn't tell you if you are the thread with the lock.
  165. #endif
  166. }
  167. #endif // EA_PLATFORM_XXX