eathread_condition.cpp 7.0 KB

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