eathread_mutex_pc.cpp 5.9 KB

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