| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include <EABase/eabase.h>
- #include <eathread/eathread_barrier.h>
- #include <eathread/eathread.h>
- #if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
- #include <pthread.h>
- #include <time.h>
- #include <errno.h>
- #include <string.h>
- #include <new>
- #ifdef EA_PLATFORM_WINDOWS
- EA_DISABLE_ALL_VC_WARNINGS()
- #include <Windows.h> // Presumably we are using pthreads-win32.
- EA_RESTORE_ALL_VC_WARNINGS()
- #endif
- EABarrierData::EABarrierData()
- : mCV(), mMutex(), mnHeight(0), mnCurrent(0), mnCycle(0), mbValid(false)
- {}
- EA::Thread::BarrierParameters::BarrierParameters(int height, bool bIntraProcess, const char* pName)
- : mHeight(height), mbIntraProcess(bIntraProcess)
- {
- if(pName)
- strncpy(mName, pName, sizeof(mName)-1);
- 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()
- {
- if(mBarrierData.mbValid){
- EAT_ASSERT(mBarrierData.mnCurrent == mBarrierData.mnHeight);
- int result = pthread_mutex_destroy(&mBarrierData.mMutex);
- EA_UNUSED(result);
- EAT_ASSERT(result == 0);
- result = pthread_cond_destroy(&mBarrierData.mCV);
- EAT_ASSERT(result == 0);
- EA_UNUSED( result ); //if compiling without asserts
- }
- }
- bool EA::Thread::Barrier::Init(const BarrierParameters* pBarrierParameters)
- {
- if(pBarrierParameters && !mBarrierData.mbValid){
- mBarrierData.mbValid = false;
- mBarrierData.mnHeight = pBarrierParameters->mHeight;
- mBarrierData.mnCurrent = pBarrierParameters->mHeight;
- mBarrierData.mnCycle = 0;
- int result = pthread_mutex_init(&mBarrierData.mMutex, NULL);
- if(result == 0){
- result = pthread_cond_init(&mBarrierData.mCV, NULL);
- if(result == 0)
- mBarrierData.mbValid = true;
- else
- pthread_mutex_destroy(&mBarrierData.mMutex);
- }
- return mBarrierData.mbValid;
- }
- return false;
- }
- EA::Thread::Barrier::Result EA::Thread::Barrier::Wait(const ThreadTime& timeoutAbsolute)
- {
- if(!mBarrierData.mbValid){
- EAT_ASSERT(false);
- return kResultError;
- }
- int result = pthread_mutex_lock(&mBarrierData.mMutex);
- if(result != 0){
- EAT_ASSERT(false);
- return kResultError;
- }
- const unsigned long nCurrentCycle = (unsigned)mBarrierData.mnCycle;
- bool bPrimary = false;
- if(--mBarrierData.mnCurrent == 0){ // This is not an atomic operation. We are within a mutex lock.
- // The last barrier can never time out, as its action is always immediate.
- mBarrierData.mnCycle++;
- mBarrierData.mnCurrent = mBarrierData.mnHeight;
- result = pthread_cond_broadcast(&mBarrierData.mCV);
- // The last thread into the barrier will return a result of
- // kResultPrimary rather than kResultSecondary.
- if(result == 0)
- bPrimary = true;
- //else leave result as an error value.
- }
- else{
- // timeoutMilliseconds
- // Wait with cancellation disabled, because pthreads barrier_wait
- // should not be a cancellation point.
- #if defined(PTHREAD_CANCEL_DISABLE)
- int cancel;
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel);
- #endif
- // Wait until the barrier's cycle changes, which means that
- // it has been broadcast, and we don't want to wait anymore.
- while(nCurrentCycle == mBarrierData.mnCycle){
- do{
- // Under SMP systems, pthread_cond_wait can return the success value 'spuriously'.
- // This is by design and we must retest the predicate condition and if it has
- // not true, we must go back to waiting.
- result = pthread_cond_timedwait(&mBarrierData.mCV, &mBarrierData.mMutex, &timeoutAbsolute);
- } while((result == 0) && (nCurrentCycle == mBarrierData.mnCycle));
- if(result != 0)
- break;
- }
- #if defined(PTHREAD_CANCEL_DISABLE)
- int cancelTemp;
- pthread_setcancelstate(cancel, &cancelTemp);
- #endif
- }
- // We declare a new result2 value because the old one
- // might have a special value from above in it.
- const int result2 = pthread_mutex_unlock(&mBarrierData.mMutex); (void)result2;
- EAT_ASSERT(result2 == 0);
- if(result == 0)
- return bPrimary ? kResultPrimary : kResultSecondary;
- else if(result == ETIMEDOUT)
- return kResultTimeout;
- 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;
- }
- #endif // EA_PLATFORM_XXX
|