eathread_barrier.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. #include "kettle/eathread_barrier_kettle.cpp"
  11. #elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE
  12. // Posix already defines a barrier (via condition variables or directly with pthread_barrier).
  13. #include "unix/eathread_barrier_unix.cpp"
  14. #else // All other platforms
  15. #include <eathread/eathread_barrier.h>
  16. #include <string.h>
  17. EABarrierData::EABarrierData()
  18. : mnCurrent(0), mnHeight(0), mnIndex(0), mSemaphore0(NULL, false), mSemaphore1(NULL, false)
  19. {
  20. // Leave mSemaphores alone for now. We leave them constructed but not initialized.
  21. }
  22. EA::Thread::BarrierParameters::BarrierParameters(int height, bool bIntraProcess, const char* pName)
  23. : mHeight(height), mbIntraProcess(bIntraProcess)
  24. {
  25. if(pName)
  26. {
  27. EA_DISABLE_VC_WARNING(4996); // This function or variable may be unsafe / deprecated.
  28. strncpy(mName, pName, sizeof(mName)-1);
  29. EA_RESTORE_VC_WARNING();
  30. mName[sizeof(mName)-1] = 0;
  31. }
  32. else
  33. mName[0] = 0;
  34. }
  35. EA::Thread::Barrier::Barrier(const BarrierParameters* pBarrierParameters, bool bDefaultParameters)
  36. {
  37. if(!pBarrierParameters && bDefaultParameters)
  38. {
  39. BarrierParameters parameters;
  40. Init(&parameters);
  41. }
  42. else
  43. Init(pBarrierParameters);
  44. }
  45. EA::Thread::Barrier::Barrier(int height)
  46. {
  47. BarrierParameters parameters(height);
  48. Init(&parameters);
  49. }
  50. EA::Thread::Barrier::~Barrier()
  51. {
  52. // Nothing to do.
  53. }
  54. bool EA::Thread::Barrier::Init(const BarrierParameters* pBarrierParameters)
  55. {
  56. // You cannot set the height after it's already been set.
  57. EAT_ASSERT((mBarrierData.mnHeight == 0) && (mBarrierData.mnCurrent == 0));
  58. if(pBarrierParameters && (mBarrierData.mnHeight == 0))
  59. {
  60. mBarrierData.mnHeight = pBarrierParameters->mHeight; // We don't put mutex lock around this as it is only to be ever set once, before use.
  61. mBarrierData.mnCurrent = pBarrierParameters->mHeight;
  62. SemaphoreParameters sp(0, pBarrierParameters->mbIntraProcess);
  63. mBarrierData.mSemaphore0.Init(&sp);
  64. mBarrierData.mSemaphore1.Init(&sp);
  65. return true;
  66. }
  67. return false;
  68. }
  69. EA::Thread::Barrier::Result EA::Thread::Barrier::Wait(const ThreadTime& timeoutAbsolute)
  70. {
  71. int result;
  72. const int nCurrentIndex = (int)mBarrierData.mnIndex;
  73. // Question: What do we do if a fifth thread calls Wait on a barrier with height
  74. // of four after the fourth thread has decremented the current count below?
  75. EAT_ASSERT(mBarrierData.mnCurrent > 0); // If this assert fails then it means that more threads are waiting on the barrier than the barrier height.
  76. const int32_t nCurrent = mBarrierData.mnCurrent.Decrement(); // atomic integer operation.
  77. if(nCurrent == 0) // If the barrier has been breached...
  78. {
  79. mBarrierData.mnCurrent = mBarrierData.mnHeight;
  80. if(mBarrierData.mnHeight > 1) // If there are threads other than us...
  81. {
  82. // We don't have a potential race condition here because we use alternating
  83. // semaphores and since we are here, all other threads are waiting on the
  84. // current semaphore below. And if they haven't started waiting on the
  85. // semaphore yet, they'll succeed anyway because we Post all directly below.
  86. Semaphore* const pSemaphore = (nCurrentIndex == 0 ? &mBarrierData.mSemaphore0 : &mBarrierData.mSemaphore1);
  87. result = pSemaphore->Post(mBarrierData.mnHeight - 1); // Upon success, the return value will in practice be >= 1, but semaphore defines success as >= 0.
  88. }
  89. else // Else we are the only thead.
  90. result = 0;
  91. }
  92. else
  93. {
  94. Semaphore* const pSemaphore = (nCurrentIndex == 0 ? &mBarrierData.mSemaphore0 : &mBarrierData.mSemaphore1);
  95. result = pSemaphore->Wait(timeoutAbsolute);
  96. if(result == Semaphore::kResultTimeout)
  97. return kResultTimeout;
  98. }
  99. if(result >= 0) // If the result wasn't an error such as Semaphore::kResultError or Semaphore::kResultTimeout.
  100. {
  101. // Use an atomic operation to change the index, which conveniently gives us a thread to designate as primary.
  102. EAT_ASSERT((unsigned)nCurrentIndex <= 1);
  103. if(mBarrierData.mnIndex.SetValueConditional(1 - nCurrentIndex, nCurrentIndex)) // Toggle value between 0 and 1.
  104. return kResultPrimary;
  105. return kResultSecondary;
  106. }
  107. return kResultError;
  108. }
  109. EA::Thread::Barrier* EA::Thread::BarrierFactory::CreateBarrier()
  110. {
  111. EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator();
  112. if(pAllocator)
  113. return new(pAllocator->Alloc(sizeof(EA::Thread::Barrier))) EA::Thread::Barrier;
  114. else
  115. return new EA::Thread::Barrier;
  116. }
  117. void EA::Thread::BarrierFactory::DestroyBarrier(EA::Thread::Barrier* pBarrier)
  118. {
  119. EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator();
  120. if(pAllocator)
  121. {
  122. pBarrier->~Barrier();
  123. pAllocator->Free(pBarrier);
  124. }
  125. else
  126. delete pBarrier;
  127. }
  128. size_t EA::Thread::BarrierFactory::GetBarrierSize()
  129. {
  130. return sizeof(EA::Thread::Barrier);
  131. }
  132. EA::Thread::Barrier* EA::Thread::BarrierFactory::ConstructBarrier(void* pMemory)
  133. {
  134. return new(pMemory) EA::Thread::Barrier;
  135. }
  136. void EA::Thread::BarrierFactory::DestructBarrier(EA::Thread::Barrier* pBarrier)
  137. {
  138. pBarrier->~Barrier();
  139. }
  140. #endif // EA_PLATFORM_XXX