| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include <EABase/eabase.h>
- #include <eathread/internal/config.h>
- EA_DISABLE_VC_WARNING(4574)
- #include <new>
- EA_RESTORE_VC_WARNING()
- #if defined(EA_PLATFORM_SONY)
- #include "kettle/eathread_barrier_kettle.cpp"
- #elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE
- // Posix already defines a barrier (via condition variables or directly with pthread_barrier).
- #include "unix/eathread_barrier_unix.cpp"
- #else // All other platforms
- #include <eathread/eathread_barrier.h>
- #include <string.h>
- EABarrierData::EABarrierData()
- : mnCurrent(0), mnHeight(0), mnIndex(0), mSemaphore0(NULL, false), mSemaphore1(NULL, false)
- {
- // Leave mSemaphores alone for now. We leave them constructed but not initialized.
- }
- EA::Thread::BarrierParameters::BarrierParameters(int height, bool bIntraProcess, const char* pName)
- : mHeight(height), mbIntraProcess(bIntraProcess)
- {
- if(pName)
- {
- EA_DISABLE_VC_WARNING(4996); // This function or variable may be unsafe / deprecated.
- strncpy(mName, pName, sizeof(mName)-1);
- EA_RESTORE_VC_WARNING();
- mName[sizeof(mName)-1] = 0;
- }
- else
- mName[0] = 0;
- }
- EA::Thread::Barrier::Barrier(const BarrierParameters* pBarrierParameters, bool bDefaultParameters)
- {
- if(!pBarrierParameters && bDefaultParameters)
- {
- BarrierParameters parameters;
- Init(¶meters);
- }
- else
- Init(pBarrierParameters);
- }
- EA::Thread::Barrier::Barrier(int height)
- {
- BarrierParameters parameters(height);
- Init(¶meters);
- }
- EA::Thread::Barrier::~Barrier()
- {
- // Nothing to do.
- }
- bool EA::Thread::Barrier::Init(const BarrierParameters* pBarrierParameters)
- {
- // You cannot set the height after it's already been set.
- EAT_ASSERT((mBarrierData.mnHeight == 0) && (mBarrierData.mnCurrent == 0));
- if(pBarrierParameters && (mBarrierData.mnHeight == 0))
- {
- mBarrierData.mnHeight = pBarrierParameters->mHeight; // We don't put mutex lock around this as it is only to be ever set once, before use.
- mBarrierData.mnCurrent = pBarrierParameters->mHeight;
- SemaphoreParameters sp(0, pBarrierParameters->mbIntraProcess);
- mBarrierData.mSemaphore0.Init(&sp);
- mBarrierData.mSemaphore1.Init(&sp);
- return true;
- }
- return false;
- }
- EA::Thread::Barrier::Result EA::Thread::Barrier::Wait(const ThreadTime& timeoutAbsolute)
- {
- int result;
- const int nCurrentIndex = (int)mBarrierData.mnIndex;
- // Question: What do we do if a fifth thread calls Wait on a barrier with height
- // of four after the fourth thread has decremented the current count below?
- EAT_ASSERT(mBarrierData.mnCurrent > 0); // If this assert fails then it means that more threads are waiting on the barrier than the barrier height.
- const int32_t nCurrent = mBarrierData.mnCurrent.Decrement(); // atomic integer operation.
- if(nCurrent == 0) // If the barrier has been breached...
- {
- mBarrierData.mnCurrent = mBarrierData.mnHeight;
- if(mBarrierData.mnHeight > 1) // If there are threads other than us...
- {
- // We don't have a potential race condition here because we use alternating
- // semaphores and since we are here, all other threads are waiting on the
- // current semaphore below. And if they haven't started waiting on the
- // semaphore yet, they'll succeed anyway because we Post all directly below.
- Semaphore* const pSemaphore = (nCurrentIndex == 0 ? &mBarrierData.mSemaphore0 : &mBarrierData.mSemaphore1);
- result = pSemaphore->Post(mBarrierData.mnHeight - 1); // Upon success, the return value will in practice be >= 1, but semaphore defines success as >= 0.
- }
- else // Else we are the only thead.
- result = 0;
- }
- else
- {
- Semaphore* const pSemaphore = (nCurrentIndex == 0 ? &mBarrierData.mSemaphore0 : &mBarrierData.mSemaphore1);
- result = pSemaphore->Wait(timeoutAbsolute);
- if(result == Semaphore::kResultTimeout)
- return kResultTimeout;
- }
- if(result >= 0) // If the result wasn't an error such as Semaphore::kResultError or Semaphore::kResultTimeout.
- {
- // Use an atomic operation to change the index, which conveniently gives us a thread to designate as primary.
- EAT_ASSERT((unsigned)nCurrentIndex <= 1);
- if(mBarrierData.mnIndex.SetValueConditional(1 - nCurrentIndex, nCurrentIndex)) // Toggle value between 0 and 1.
- return kResultPrimary;
- return kResultSecondary;
- }
- return kResultError;
- }
- EA::Thread::Barrier* EA::Thread::BarrierFactory::CreateBarrier()
- {
- EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator();
- if(pAllocator)
- return new(pAllocator->Alloc(sizeof(EA::Thread::Barrier))) EA::Thread::Barrier;
- else
- return new EA::Thread::Barrier;
- }
- void EA::Thread::BarrierFactory::DestroyBarrier(EA::Thread::Barrier* pBarrier)
- {
- EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator();
- if(pAllocator)
- {
- pBarrier->~Barrier();
- pAllocator->Free(pBarrier);
- }
- else
- delete pBarrier;
- }
- size_t EA::Thread::BarrierFactory::GetBarrierSize()
- {
- return sizeof(EA::Thread::Barrier);
- }
- EA::Thread::Barrier* EA::Thread::BarrierFactory::ConstructBarrier(void* pMemory)
- {
- return new(pMemory) EA::Thread::Barrier;
- }
- void EA::Thread::BarrierFactory::DestructBarrier(EA::Thread::Barrier* pBarrier)
- {
- pBarrier->~Barrier();
- }
- #endif // EA_PLATFORM_XXX
|