eathread_barrier_unix.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <EABase/eabase.h>
  5. #include <eathread/eathread_barrier.h>
  6. #include <eathread/eathread.h>
  7. #if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
  8. #include <pthread.h>
  9. #include <time.h>
  10. #include <errno.h>
  11. #include <string.h>
  12. #include <new>
  13. #ifdef EA_PLATFORM_WINDOWS
  14. EA_DISABLE_ALL_VC_WARNINGS()
  15. #include <Windows.h> // Presumably we are using pthreads-win32.
  16. EA_RESTORE_ALL_VC_WARNINGS()
  17. #endif
  18. EABarrierData::EABarrierData()
  19. : mCV(), mMutex(), mnHeight(0), mnCurrent(0), mnCycle(0), mbValid(false)
  20. {}
  21. EA::Thread::BarrierParameters::BarrierParameters(int height, bool bIntraProcess, const char* pName)
  22. : mHeight(height), mbIntraProcess(bIntraProcess)
  23. {
  24. if(pName)
  25. strncpy(mName, pName, sizeof(mName)-1);
  26. else
  27. mName[0] = 0;
  28. }
  29. EA::Thread::Barrier::Barrier(const BarrierParameters* pBarrierParameters, bool bDefaultParameters)
  30. {
  31. if(!pBarrierParameters && bDefaultParameters)
  32. {
  33. BarrierParameters parameters;
  34. Init(&parameters);
  35. }
  36. else
  37. Init(pBarrierParameters);
  38. }
  39. EA::Thread::Barrier::Barrier(int height)
  40. {
  41. BarrierParameters parameters(height);
  42. Init(&parameters);
  43. }
  44. EA::Thread::Barrier::~Barrier()
  45. {
  46. if(mBarrierData.mbValid){
  47. EAT_ASSERT(mBarrierData.mnCurrent == mBarrierData.mnHeight);
  48. int result = pthread_mutex_destroy(&mBarrierData.mMutex);
  49. EA_UNUSED(result);
  50. EAT_ASSERT(result == 0);
  51. result = pthread_cond_destroy(&mBarrierData.mCV);
  52. EAT_ASSERT(result == 0);
  53. EA_UNUSED( result ); //if compiling without asserts
  54. }
  55. }
  56. bool EA::Thread::Barrier::Init(const BarrierParameters* pBarrierParameters)
  57. {
  58. if(pBarrierParameters && !mBarrierData.mbValid){
  59. mBarrierData.mbValid = false;
  60. mBarrierData.mnHeight = pBarrierParameters->mHeight;
  61. mBarrierData.mnCurrent = pBarrierParameters->mHeight;
  62. mBarrierData.mnCycle = 0;
  63. int result = pthread_mutex_init(&mBarrierData.mMutex, NULL);
  64. if(result == 0){
  65. result = pthread_cond_init(&mBarrierData.mCV, NULL);
  66. if(result == 0)
  67. mBarrierData.mbValid = true;
  68. else
  69. pthread_mutex_destroy(&mBarrierData.mMutex);
  70. }
  71. return mBarrierData.mbValid;
  72. }
  73. return false;
  74. }
  75. EA::Thread::Barrier::Result EA::Thread::Barrier::Wait(const ThreadTime& timeoutAbsolute)
  76. {
  77. if(!mBarrierData.mbValid){
  78. EAT_ASSERT(false);
  79. return kResultError;
  80. }
  81. int result = pthread_mutex_lock(&mBarrierData.mMutex);
  82. if(result != 0){
  83. EAT_ASSERT(false);
  84. return kResultError;
  85. }
  86. const unsigned long nCurrentCycle = (unsigned)mBarrierData.mnCycle;
  87. bool bPrimary = false;
  88. if(--mBarrierData.mnCurrent == 0){ // This is not an atomic operation. We are within a mutex lock.
  89. // The last barrier can never time out, as its action is always immediate.
  90. mBarrierData.mnCycle++;
  91. mBarrierData.mnCurrent = mBarrierData.mnHeight;
  92. result = pthread_cond_broadcast(&mBarrierData.mCV);
  93. // The last thread into the barrier will return a result of
  94. // kResultPrimary rather than kResultSecondary.
  95. if(result == 0)
  96. bPrimary = true;
  97. //else leave result as an error value.
  98. }
  99. else{
  100. // timeoutMilliseconds
  101. // Wait with cancellation disabled, because pthreads barrier_wait
  102. // should not be a cancellation point.
  103. #if defined(PTHREAD_CANCEL_DISABLE)
  104. int cancel;
  105. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel);
  106. #endif
  107. // Wait until the barrier's cycle changes, which means that
  108. // it has been broadcast, and we don't want to wait anymore.
  109. while(nCurrentCycle == mBarrierData.mnCycle){
  110. do{
  111. // Under SMP systems, pthread_cond_wait can return the success value 'spuriously'.
  112. // This is by design and we must retest the predicate condition and if it has
  113. // not true, we must go back to waiting.
  114. result = pthread_cond_timedwait(&mBarrierData.mCV, &mBarrierData.mMutex, &timeoutAbsolute);
  115. } while((result == 0) && (nCurrentCycle == mBarrierData.mnCycle));
  116. if(result != 0)
  117. break;
  118. }
  119. #if defined(PTHREAD_CANCEL_DISABLE)
  120. int cancelTemp;
  121. pthread_setcancelstate(cancel, &cancelTemp);
  122. #endif
  123. }
  124. // We declare a new result2 value because the old one
  125. // might have a special value from above in it.
  126. const int result2 = pthread_mutex_unlock(&mBarrierData.mMutex); (void)result2;
  127. EAT_ASSERT(result2 == 0);
  128. if(result == 0)
  129. return bPrimary ? kResultPrimary : kResultSecondary;
  130. else if(result == ETIMEDOUT)
  131. return kResultTimeout;
  132. return kResultError;
  133. }
  134. EA::Thread::Barrier* EA::Thread::BarrierFactory::CreateBarrier()
  135. {
  136. EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator();
  137. if(pAllocator)
  138. return new(pAllocator->Alloc(sizeof(EA::Thread::Barrier))) EA::Thread::Barrier;
  139. else
  140. return new EA::Thread::Barrier;
  141. }
  142. void EA::Thread::BarrierFactory::DestroyBarrier(EA::Thread::Barrier* pBarrier)
  143. {
  144. EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator();
  145. if(pAllocator)
  146. {
  147. pBarrier->~Barrier();
  148. pAllocator->Free(pBarrier);
  149. }
  150. else
  151. delete pBarrier;
  152. }
  153. #endif // EA_PLATFORM_XXX