eathread_semaphore.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <eathread/internal/config.h>
  5. #include <eathread/eathread_semaphore.h>
  6. EA_DISABLE_VC_WARNING(4574)
  7. #include <string.h>
  8. #include <new>
  9. EA_RESTORE_VC_WARNING()
  10. #if !EA_THREADS_AVAILABLE
  11. #include <eathread/eathread_semaphore.h>
  12. #elif EATHREAD_USE_SYNTHESIZED_SEMAPHORE
  13. // Fall through.
  14. #elif 0 //EA_USE_CPP11_CONCURRENCY
  15. #include "cpp11/eathread_semaphore_cpp11.cpp"
  16. #elif defined(EA_PLATFORM_APPLE)
  17. #include "apple/eathread_semaphore_apple.cpp"
  18. #elif defined(EA_PLATFORM_ANDROID)
  19. #include "android/eathread_semaphore_android.cpp"
  20. #elif defined(EA_PLATFORM_SONY)
  21. #include "kettle/eathread_semaphore_kettle.cpp"
  22. #elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
  23. #include "unix/eathread_semaphore_unix.cpp"
  24. #elif defined(EA_PLATFORM_MICROSOFT)
  25. #include "pc/eathread_semaphore_pc.cpp"
  26. #endif
  27. namespace EA
  28. {
  29. namespace Thread
  30. {
  31. extern Allocator* gpAllocator;
  32. }
  33. }
  34. EA::Thread::Semaphore* EA::Thread::SemaphoreFactory::CreateSemaphore()
  35. {
  36. if(gpAllocator)
  37. return new(gpAllocator->Alloc(sizeof(EA::Thread::Semaphore))) EA::Thread::Semaphore;
  38. else
  39. return new EA::Thread::Semaphore;
  40. }
  41. void EA::Thread::SemaphoreFactory::DestroySemaphore(EA::Thread::Semaphore* pSemaphore)
  42. {
  43. if(gpAllocator)
  44. {
  45. pSemaphore->~Semaphore();
  46. gpAllocator->Free(pSemaphore);
  47. }
  48. else
  49. delete pSemaphore;
  50. }
  51. size_t EA::Thread::SemaphoreFactory::GetSemaphoreSize()
  52. {
  53. return sizeof(EA::Thread::Semaphore);
  54. }
  55. EA::Thread::Semaphore* EA::Thread::SemaphoreFactory::ConstructSemaphore(void* pMemory)
  56. {
  57. return new(pMemory) EA::Thread::Semaphore;
  58. }
  59. void EA::Thread::SemaphoreFactory::DestructSemaphore(EA::Thread::Semaphore* pSemaphore)
  60. {
  61. pSemaphore->~Semaphore();
  62. }
  63. #if EATHREAD_USE_SYNTHESIZED_SEMAPHORE
  64. EASemaphoreData::EASemaphoreData()
  65. : mCV(),
  66. mMutex(),
  67. mnCount(0),
  68. mnMaxCount(INT_MAX),
  69. mbValid(false)
  70. {
  71. // Empty
  72. }
  73. EA::Thread::SemaphoreParameters::SemaphoreParameters(int initialCount, bool bIntraProcess, const char* pName)
  74. : mInitialCount(initialCount),
  75. mMaxCount(INT_MAX),
  76. mbIntraProcess(bIntraProcess)
  77. {
  78. if(pName)
  79. {
  80. strncpy(mName, pName, sizeof(mName)-1);
  81. mName[sizeof(mName)-1] = 0;
  82. }
  83. else
  84. mName[0] = 0;
  85. }
  86. EA::Thread::Semaphore::Semaphore(const SemaphoreParameters* pSemaphoreParameters, bool bDefaultParameters)
  87. {
  88. if(!pSemaphoreParameters && bDefaultParameters)
  89. {
  90. SemaphoreParameters parameters;
  91. Init(&parameters);
  92. }
  93. else
  94. Init(pSemaphoreParameters);
  95. }
  96. EA::Thread::Semaphore::Semaphore(int initialCount)
  97. {
  98. SemaphoreParameters parameters(initialCount);
  99. Init(&parameters);
  100. }
  101. EA::Thread::Semaphore::~Semaphore()
  102. {
  103. EAT_ASSERT(!mSemaphoreData.mMutex.HasLock()); // The mMutex destructor will also assert this, but here it makes it more obvious this mutex is ours.
  104. }
  105. bool EA::Thread::Semaphore::Init(const SemaphoreParameters* pSemaphoreParameters)
  106. {
  107. if(pSemaphoreParameters && (!mSemaphoreData.mbValid))
  108. {
  109. mSemaphoreData.mbValid = true; // It's not really true unless our member mCV and mMutex init OK. To do: Added functions to our classes that verify they are OK.
  110. mSemaphoreData.mnCount = pSemaphoreParameters->mInitialCount;
  111. mSemaphoreData.mnMaxCount = pSemaphoreParameters->mMaxCount;
  112. if(mSemaphoreData.mnCount < 0)
  113. mSemaphoreData.mnCount = 0;
  114. return mSemaphoreData.mbValid;
  115. }
  116. return false;
  117. }
  118. int EA::Thread::Semaphore::Wait(const ThreadTime& timeoutAbsolute)
  119. {
  120. int nReturnValue = kResultError;
  121. int result = mSemaphoreData.mMutex.Lock(); // This mutex is owned by us and will be unlocked immediately in the mCV.Wait call, so we don't apply timeoutAbsolute. To consider: Maybe we should do so, though it's less efficient.
  122. if(result > 0) // If success...
  123. {
  124. if(timeoutAbsolute == kTimeoutImmediate)
  125. {
  126. if(mSemaphoreData.mnCount.GetValue() >= 1)
  127. nReturnValue = mSemaphoreData.mnCount.Decrement();
  128. else
  129. nReturnValue = kResultTimeout;
  130. }
  131. else
  132. {
  133. if(mSemaphoreData.mnCount.GetValue() >= 1) // If we can decrement it immediately...
  134. nReturnValue = mSemaphoreData.mnCount.Decrement();
  135. else // Else we need to wait.
  136. {
  137. Condition::Result cResult;
  138. do{
  139. cResult = mSemaphoreData.mCV.Wait(&mSemaphoreData.mMutex, timeoutAbsolute);
  140. } while((cResult == Condition::kResultOK) && (mSemaphoreData.mnCount.GetValue() < 1)); // Always need to check the condition and retry if not matched. In rare cases two threads could return from Wait.
  141. if(cResult == Condition::kResultOK) // If apparent success...
  142. nReturnValue = mSemaphoreData.mnCount.Decrement();
  143. else if(cResult == Condition::kResultTimeout)
  144. nReturnValue = kResultTimeout;
  145. else
  146. {
  147. // We return immediately here because mCV.Wait has not locked the mutex for
  148. // us and so we don't want to fall through and unlock it below. Also, it would
  149. // be inefficient for us to lock here and fall through only to unlock it below.
  150. return nReturnValue;
  151. }
  152. }
  153. }
  154. result = mSemaphoreData.mMutex.Unlock();
  155. EAT_ASSERT(result >= 0);
  156. if(result < 0)
  157. nReturnValue = kResultError; // This Semaphore is now considered dead and unusable.
  158. }
  159. return nReturnValue;
  160. }
  161. int EA::Thread::Semaphore::Post(int count)
  162. {
  163. EAT_ASSERT(mSemaphoreData.mnCount >= 0);
  164. int newValue = kResultError;
  165. int result = mSemaphoreData.mMutex.Lock();
  166. if(result > 0)
  167. {
  168. // Set the new value to be whatever the current value is.
  169. newValue = mSemaphoreData.mnCount.GetValue();
  170. if((mSemaphoreData.mnMaxCount - count) < newValue) // If count would cause an overflow...
  171. return kResultError; // We do what most OS implementations of max-count do. count = (mSemaphoreData.mnMaxCount - newValue);
  172. newValue = mSemaphoreData.mnCount.Add(count);
  173. bool bResult = mSemaphoreData.mCV.Signal(true); // Signal broadcast (the true arg) because semaphores could have multiple counts and multiple threads waiting for them. There's a potential "thundering herd" problem here.
  174. EAT_ASSERT(bResult);
  175. EA_UNUSED(bResult);
  176. result = mSemaphoreData.mMutex.Unlock(); // Important that we lock after the mCV.Signal.
  177. EAT_ASSERT(result >= 0);
  178. if(result < 0)
  179. newValue = kResultError; // This Semaphore is now considered dead and unusable.
  180. }
  181. return newValue;
  182. }
  183. int EA::Thread::Semaphore::GetCount() const
  184. {
  185. return mSemaphoreData.mnCount.GetValue();
  186. }
  187. #elif !EA_THREADS_AVAILABLE
  188. ///////////////////////////////////////////////////////////////////////////////
  189. // non-threaded implementation
  190. ///////////////////////////////////////////////////////////////////////////////
  191. EASemaphoreData::EASemaphoreData()
  192. : mnCount(0),
  193. mnMaxCount(INT_MAX)
  194. {
  195. // Empty
  196. }
  197. EA::Thread::SemaphoreParameters::SemaphoreParameters(int initialCount, bool bIntraProcess, const char* pName)
  198. : mInitialCount(initialCount),
  199. mMaxCount(INT_MAX),
  200. mbIntraProcess(bIntraProcess)
  201. {
  202. if(pName)
  203. {
  204. strncpy(mName, pName, sizeof(mName)-1);
  205. mName[sizeof(mName)-1] = 0;
  206. }
  207. else
  208. mName[0] = 0;
  209. }
  210. EA::Thread::Semaphore::Semaphore(const SemaphoreParameters* pSemaphoreParameters, bool bDefaultParameters)
  211. {
  212. if(!pSemaphoreParameters && bDefaultParameters)
  213. {
  214. SemaphoreParameters parameters;
  215. Init(&parameters);
  216. }
  217. else
  218. Init(pSemaphoreParameters);
  219. }
  220. EA::Thread::Semaphore::Semaphore(int initialCount)
  221. {
  222. SemaphoreParameters parameters(initialCount);
  223. Init(&parameters);
  224. }
  225. EA::Thread::Semaphore::~Semaphore()
  226. {
  227. }
  228. bool EA::Thread::Semaphore::Init(const SemaphoreParameters* pSemaphoreParameters)
  229. {
  230. if(pSemaphoreParameters)
  231. {
  232. mSemaphoreData.mnCount = pSemaphoreParameters->mInitialCount;
  233. mSemaphoreData.mnMaxCount = pSemaphoreParameters->mMaxCount;
  234. return true;
  235. }
  236. return false;
  237. }
  238. int EA::Thread::Semaphore::Wait(const ThreadTime& timeoutAbsolute)
  239. {
  240. if(timeoutAbsolute == kTimeoutNone)
  241. {
  242. while(mSemaphoreData.mnCount <= 0)
  243. ThreadSleep(1);
  244. --mSemaphoreData.mnCount;
  245. }
  246. else if(timeoutAbsolute == 0)
  247. {
  248. if(mSemaphoreData.mnCount)
  249. --mSemaphoreData.mnCount;
  250. else
  251. return kResultTimeout;
  252. }
  253. else
  254. {
  255. while((mSemaphoreData.mnCount <= 0) && (GetThreadTime() < timeoutAbsolute))
  256. ThreadSleep(1);
  257. if(mSemaphoreData.mnCount <= 0)
  258. return kResultTimeout;
  259. }
  260. return mSemaphoreData.mnCount;
  261. }
  262. int EA::Thread::Semaphore::Post(int count)
  263. {
  264. EAT_ASSERT(mSemaphoreData.mnCount >= 0);
  265. // Ideally, what we would do is account for the number of waiters in
  266. // this overflow calculation. If max-count = 4, count = 6, waiters = 8,
  267. // we would release 6 waiters and leave the semaphore at 2.
  268. // The problem is that some of those 6 waiters might time out while we
  269. // are doing this and leave ourselves with count greater than max-count.
  270. if((mSemaphoreData.mnMaxCount - count) < mSemaphoreData.mnCount) // If count would cause an overflow...
  271. return kResultError; // We do what most OS implementations of max-count do. // count = (mSemaphoreData.mnMaxCount - nLastCount);
  272. return (mSemaphoreData.mnCount += count);
  273. }
  274. int EA::Thread::Semaphore::GetCount() const
  275. {
  276. return mSemaphoreData.mnCount;
  277. }
  278. #endif // !EA_THREADS_AVAILABLE