eathread_condition.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <EABase/eabase.h>
  5. #include <eathread/internal/config.h>
  6. EA_DISABLE_VC_WARNING(4574)
  7. #include <new>
  8. EA_RESTORE_VC_WARNING()
  9. #if defined(EA_PLATFORM_SONY)
  10. // Posix already defines a Condition (via condition variables).
  11. #include "kettle/eathread_condition_kettle.cpp"
  12. #elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE
  13. // Posix already defines a Condition (via condition variables).
  14. #include "unix/eathread_condition_unix.cpp"
  15. #else // All other platforms
  16. #include <eathread/eathread_condition.h>
  17. #include <string.h>
  18. #ifdef _MSC_VER
  19. #pragma warning(disable: 4996) // This function or variable may be unsafe / deprecated.
  20. #endif
  21. EAConditionData::EAConditionData()
  22. : mnWaitersBlocked(0), mnWaitersToUnblock(0), mnWaitersDone(0),
  23. mSemaphoreBlockQueue(NULL, false), // We will be initializing these ourselves specifically below.
  24. mSemaphoreBlockLock(NULL, false),
  25. mUnblockLock(NULL, false)
  26. {
  27. // Empty
  28. }
  29. EA::Thread::ConditionParameters::ConditionParameters(bool bIntraProcess, const char* pName)
  30. : mbIntraProcess(bIntraProcess)
  31. {
  32. if(pName)
  33. {
  34. strncpy(mName, pName, sizeof(mName)-1);
  35. mName[sizeof(mName)-1] = 0;
  36. }
  37. else
  38. mName[0] = 0;
  39. }
  40. EA::Thread::Condition::Condition(const ConditionParameters* pConditionParameters, bool bDefaultParameters)
  41. {
  42. if(!pConditionParameters && bDefaultParameters)
  43. {
  44. ConditionParameters parameters;
  45. Init(&parameters);
  46. }
  47. else
  48. Init(pConditionParameters);
  49. }
  50. EA::Thread::Condition::~Condition()
  51. {
  52. // Empty
  53. }
  54. bool EA::Thread::Condition::Init(const ConditionParameters* pConditionParameters)
  55. {
  56. if(pConditionParameters)
  57. {
  58. // We have a problem with naming here. We implement our Condition variable with two semaphores and a mutex.
  59. // It's not possible to have them all have the same name, since the OS will think you want them to be
  60. // shared instances. What we really need is an explicit debug name that is separate from the OS name.
  61. // And the ConditionParameters::mName should be that debug name only and not be applied to the child primitives.
  62. const SemaphoreParameters sp1(0, pConditionParameters->mbIntraProcess, NULL); // Set the name to NULL, regardless of what pConditionParameters->mName is.
  63. const SemaphoreParameters sp2(1, pConditionParameters->mbIntraProcess, NULL);
  64. const MutexParameters mp(pConditionParameters->mbIntraProcess, NULL);
  65. if(mConditionData.mSemaphoreBlockQueue.Init(&sp1) &&
  66. mConditionData.mSemaphoreBlockLock .Init(&sp2) &&
  67. mConditionData.mUnblockLock.Init(&mp))
  68. {
  69. return true;
  70. }
  71. }
  72. return false;
  73. }
  74. EA::Thread::Condition::Result EA::Thread::Condition::Wait(Mutex* pMutex, const ThreadTime& timeoutAbsolute)
  75. {
  76. int lockResult, result;
  77. EAT_ASSERT(pMutex); // The user is required to pass a valid Mutex pointer.
  78. ++mConditionData.mnWaitersBlocked; // Note that this is an atomic operation.
  79. EAT_ASSERT(pMutex->GetLockCount() == 1);
  80. lockResult = pMutex->Unlock();
  81. if(lockResult < 0)
  82. return (Result)lockResult;
  83. result = mConditionData.mSemaphoreBlockQueue.Wait(timeoutAbsolute);
  84. EAT_ASSERT(result != EA::Thread::Semaphore::kResultError);
  85. // Regardless of the result of the above error, we must press on with the code below.
  86. mConditionData.mUnblockLock.Lock();
  87. const int nWaitersToUnblock = mConditionData.mnWaitersToUnblock;
  88. if(nWaitersToUnblock != 0)
  89. --mConditionData.mnWaitersToUnblock;
  90. else if(++mConditionData.mnWaitersDone == (INT_MAX / 2)) // This is not an atomic operation. We are within a mutex lock.
  91. {
  92. // Normally this doesn't happen, but can happen under very
  93. // unusual circumstances, such as spurious semaphore signals
  94. // or cases whereby many many threads are timing out.
  95. EAT_ASSERT(false);
  96. mConditionData.mSemaphoreBlockLock.Wait();
  97. mConditionData.mnWaitersBlocked -= mConditionData.mnWaitersDone;
  98. mConditionData.mSemaphoreBlockLock.Post();
  99. mConditionData.mnWaitersDone = 0;
  100. }
  101. mConditionData.mUnblockLock.Unlock();
  102. if(nWaitersToUnblock == 1) // If we were the last...
  103. mConditionData.mSemaphoreBlockLock.Post();
  104. // We cannot apply a timeout here. The caller always expects to have the
  105. // lock upon return, even in the case of a wait timeout. Similarly, we
  106. // may or may not want the result of the lock attempt to be propogated
  107. // back to the caller. In this case, we do if it is an error.
  108. lockResult = pMutex->Lock();
  109. if(lockResult == Mutex::kResultError)
  110. return kResultError;
  111. else if(result >= 0)
  112. return kResultOK;
  113. return (Result)result; // This is the result of the wait call above.
  114. }
  115. bool EA::Thread::Condition::Signal(bool bBroadcast)
  116. {
  117. int result;
  118. int nSignalsToIssue;
  119. result = mConditionData.mUnblockLock.Lock();
  120. if(result < 0)
  121. return false;
  122. if(mConditionData.mnWaitersToUnblock)
  123. {
  124. if(mConditionData.mnWaitersBlocked == 0)
  125. {
  126. mConditionData.mUnblockLock.Unlock();
  127. return true;
  128. }
  129. if(bBroadcast)
  130. {
  131. nSignalsToIssue = (int)mConditionData.mnWaitersBlocked.SetValue(0);
  132. mConditionData.mnWaitersToUnblock += nSignalsToIssue;
  133. }
  134. else
  135. {
  136. nSignalsToIssue = 1;
  137. mConditionData.mnWaitersToUnblock++;
  138. mConditionData.mnWaitersBlocked--;
  139. }
  140. }
  141. else if(mConditionData.mnWaitersBlocked > mConditionData.mnWaitersDone)
  142. {
  143. if(mConditionData.mSemaphoreBlockLock.Wait() == EA::Thread::Semaphore::kResultError)
  144. {
  145. mConditionData.mUnblockLock.Unlock();
  146. return false;
  147. }
  148. if(mConditionData.mnWaitersDone != 0)
  149. {
  150. mConditionData.mnWaitersBlocked -= mConditionData.mnWaitersDone;
  151. mConditionData.mnWaitersDone = 0;
  152. }
  153. if(bBroadcast)
  154. {
  155. nSignalsToIssue = mConditionData.mnWaitersToUnblock = (int)mConditionData.mnWaitersBlocked.SetValue(0);
  156. }
  157. else
  158. {
  159. nSignalsToIssue = mConditionData.mnWaitersToUnblock = 1;
  160. mConditionData.mnWaitersBlocked--;
  161. }
  162. }
  163. else
  164. {
  165. mConditionData.mUnblockLock.Unlock();
  166. return true;
  167. }
  168. mConditionData.mUnblockLock.Unlock();
  169. mConditionData.mSemaphoreBlockQueue.Post(nSignalsToIssue);
  170. return true;
  171. }
  172. #endif // EA_PLATFORM_XXX
  173. EA::Thread::Condition* EA::Thread::ConditionFactory::CreateCondition()
  174. {
  175. Allocator* pAllocator = GetAllocator();
  176. if(pAllocator)
  177. return new(pAllocator->Alloc(sizeof(EA::Thread::Condition))) EA::Thread::Condition;
  178. else
  179. return new EA::Thread::Condition;
  180. }
  181. void EA::Thread::ConditionFactory::DestroyCondition(EA::Thread::Condition* pCondition)
  182. {
  183. Allocator* pAllocator = GetAllocator();
  184. if(pAllocator)
  185. {
  186. pCondition->~Condition();
  187. pAllocator->Free(pCondition);
  188. }
  189. else
  190. delete pCondition;
  191. }
  192. size_t EA::Thread::ConditionFactory::GetConditionSize()
  193. {
  194. return sizeof(EA::Thread::Condition);
  195. }
  196. EA::Thread::Condition* EA::Thread::ConditionFactory::ConstructCondition(void* pMemory)
  197. {
  198. return new(pMemory) EA::Thread::Condition;
  199. }
  200. void EA::Thread::ConditionFactory::DestructCondition(EA::Thread::Condition* pCondition)
  201. {
  202. pCondition->~Condition();
  203. }