eathread_semaphore_android.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #ifndef INCLUDED_eabase_H
  5. #include <EABase/eabase.h>
  6. #endif
  7. #ifndef EATHREAD_EATHREAD_SEMAPHORE_H
  8. #include <eathread/eathread_semaphore.h>
  9. #endif
  10. #if defined(EA_PLATFORM_ANDROID)
  11. #include <time.h>
  12. #include <string.h>
  13. #include <limits.h>
  14. #include <stdio.h>
  15. #include <sys/errno.h>
  16. EASemaphoreData::EASemaphoreData()
  17. : mnCount(0), mnMaxCount(INT_MAX)
  18. {
  19. memset(&mSemaphore, 0, sizeof(mSemaphore));
  20. }
  21. EA::Thread::SemaphoreParameters::SemaphoreParameters(int initialCount, bool bIntraProcess, const char* /*pName*/)
  22. : mInitialCount(initialCount), mMaxCount(INT_MAX), mbIntraProcess(bIntraProcess)
  23. {
  24. }
  25. EA::Thread::Semaphore::Semaphore(const SemaphoreParameters* pSemaphoreParameters, bool bDefaultParameters)
  26. {
  27. if(!pSemaphoreParameters && bDefaultParameters)
  28. {
  29. SemaphoreParameters parameters;
  30. Init(&parameters);
  31. }
  32. else
  33. Init(pSemaphoreParameters);
  34. }
  35. EA::Thread::Semaphore::Semaphore(int initialCount)
  36. {
  37. SemaphoreParameters parameters(initialCount);
  38. Init(&parameters);
  39. }
  40. EA::Thread::Semaphore::~Semaphore()
  41. {
  42. sem_destroy(&mSemaphoreData.mSemaphore);
  43. // Can't use the following because Android's sem_destroy is broken. http://code.google.com/p/android/issues/detail?id=3106
  44. //
  45. // int result = -1;
  46. //
  47. // for(;;)
  48. // {
  49. // result = sem_destroy(&mSemaphoreData.mSemaphore);
  50. //
  51. // if((result == -1) && (errno == EBUSY)) // If another thread or process is blocked on this semaphore...
  52. // ThreadSleep(kTimeoutYield); // Yield. If we don't yield, it's possible we could block other other threads or processes from running, on some systems.
  53. // else
  54. // break;
  55. // }
  56. //
  57. // EAT_ASSERT(result != -1);
  58. }
  59. bool EA::Thread::Semaphore::Init(const SemaphoreParameters* pSemaphoreParameters)
  60. {
  61. if(pSemaphoreParameters)
  62. {
  63. mSemaphoreData.mnCount = pSemaphoreParameters->mInitialCount;
  64. mSemaphoreData.mnMaxCount = pSemaphoreParameters->mMaxCount;
  65. if(mSemaphoreData.mnCount < 0)
  66. mSemaphoreData.mnCount = 0;
  67. // TODO intraprocess not supported on Android. Assert? Fail?
  68. mSemaphoreData.mbIntraProcess = false;
  69. int result = sem_init(&mSemaphoreData.mSemaphore, 0, (unsigned int)mSemaphoreData.mnCount);
  70. if(result != 0)
  71. {
  72. EAT_ASSERT(false);
  73. memset(&mSemaphoreData.mSemaphore, 0, sizeof(mSemaphoreData.mSemaphore));
  74. }
  75. return (result != -1);
  76. }
  77. return false;
  78. }
  79. int EA::Thread::Semaphore::Wait(const ThreadTime& timeoutAbsolute)
  80. {
  81. int result;
  82. if(timeoutAbsolute == kTimeoutNone)
  83. {
  84. // We retry waits that were interrupted by signals. Should we instead require
  85. // the user to deal with this and return an error value? Or should we require
  86. // the user to disable the appropriate signal interruptions?
  87. while(((result = sem_wait(&mSemaphoreData.mSemaphore)) != 0) && (errno == EINTR))
  88. {
  89. continue;
  90. }
  91. int val;
  92. sem_getvalue(&mSemaphoreData.mSemaphore, &val);
  93. if(result != 0)
  94. {
  95. EAT_ASSERT(false); // This is an error condition.
  96. return kResultError;
  97. }
  98. }
  99. else if(timeoutAbsolute == kTimeoutImmediate)
  100. {
  101. // The sem_trywait() and sem_wait() functions shall return zero if the calling process successfully
  102. // performed the semaphore lock operation on the semaphore designated by sem. If the call was
  103. // unsuccessful, the state of the semaphore shall be unchanged, and the function shall return a
  104. // value of -1 and set errno to indicate the error.
  105. int trywaitResult = sem_trywait(&mSemaphoreData.mSemaphore);
  106. if(trywaitResult == -1)
  107. {
  108. if(errno == EAGAIN) // The sem_* family of functions are different from pthreads because they set errno instead of returning an error value.
  109. return kResultTimeout;
  110. return kResultError;
  111. }
  112. // Android sem_trywait is broken and in earlier versions returns EAGAIN instead of setting
  113. // errno to EAGAIN. http://source-android.frandroid.com/bionic/libc/docs/CHANGES.TXT
  114. #if defined(EA_PLATFORM_ANDROID)
  115. if(trywaitResult == EAGAIN)
  116. return kResultTimeout;
  117. #endif
  118. }
  119. else
  120. {
  121. // Some systems don't have a sem_timedwait. In these cases we
  122. // fall back to a polling mechanism. However, polling really
  123. // isn't proper because the polling thread might be at a greater
  124. // priority level than the lock-owning thread and thus this code
  125. // might not work as well as desired.
  126. // We retry waits that were interrupted by signals. Should we instead require
  127. // the user to deal with this and return an error value? Or should we require
  128. // the user to disable the appropriate signal interruptions?
  129. while(((result = sem_timedwait(&mSemaphoreData.mSemaphore, &timeoutAbsolute)) != 0) && (errno == EINTR))
  130. {
  131. continue;
  132. }
  133. if(result != 0)
  134. {
  135. if(errno == ETIMEDOUT)
  136. return kResultTimeout;
  137. return kResultError;
  138. }
  139. }
  140. EAT_ASSERT(mSemaphoreData.mnCount > 0);
  141. return (int)mSemaphoreData.mnCount.Decrement(); // AtomicInt32 operation. Note that the value of the semaphore count could change from the returned value by the time the caller reads it. This is fine but the user should understand this.
  142. }
  143. int EA::Thread::Semaphore::Post(int count)
  144. {
  145. // Some systems have a sem_post_multiple which we could take advantage
  146. // of here to atomically post multiple times.
  147. EAT_ASSERT(mSemaphoreData.mnCount >= 0);
  148. // It's hard to correctly implement mnMaxCount here, given that it
  149. // may be modified by multiple threads during this execution. So if you want
  150. // to use max-count with an IntraProcess semaphore safely then you need to
  151. // post only from a single thread, or at least a single thread at a time.
  152. int currentCount = mSemaphoreData.mnCount;
  153. // If count would cause an overflow exit early
  154. if ((mSemaphoreData.mnMaxCount - count) < currentCount)
  155. return kResultError;
  156. currentCount += count;
  157. while(count-- > 0)
  158. {
  159. ++mSemaphoreData.mnCount; // AtomicInt32 operation.
  160. if(sem_post(&mSemaphoreData.mSemaphore) != 0)
  161. {
  162. --mSemaphoreData.mnCount; // AtomicInt32 operation.
  163. EAT_ASSERT(false);
  164. return kResultError;
  165. }
  166. }
  167. // If all count posts occurred...
  168. return currentCount; // It's possible that another thread may have modified this value since we changed it, but that's not important.
  169. }
  170. int EA::Thread::Semaphore::GetCount() const
  171. {
  172. return (int)mSemaphoreData.mnCount;
  173. }
  174. #endif // EA_PLATFORM_XXX