mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
266 lines
7.0 KiB
C++
266 lines
7.0 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// 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)
|
|
// Posix already defines a Condition (via condition variables).
|
|
#include "sony/eathread_condition_sony.cpp"
|
|
#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE
|
|
// Posix already defines a Condition (via condition variables).
|
|
#include "unix/eathread_condition_unix.cpp"
|
|
|
|
#else // All other platforms
|
|
|
|
#include <eathread/eathread_condition.h>
|
|
#include <string.h>
|
|
|
|
EAConditionData::EAConditionData()
|
|
: mnWaitersBlocked(0), mnWaitersToUnblock(0), mnWaitersDone(0),
|
|
mSemaphoreBlockQueue(NULL, false), // We will be initializing these ourselves specifically below.
|
|
mSemaphoreBlockLock(NULL, false),
|
|
mUnblockLock(NULL, false)
|
|
{
|
|
// Empty
|
|
}
|
|
|
|
|
|
EA::Thread::ConditionParameters::ConditionParameters(bool bIntraProcess, const char* pName)
|
|
: mbIntraProcess(bIntraProcess)
|
|
{
|
|
if(pName)
|
|
{
|
|
strncpy(mName, pName, sizeof(mName)-1);
|
|
mName[sizeof(mName)-1] = 0;
|
|
}
|
|
else
|
|
mName[0] = 0;
|
|
}
|
|
|
|
|
|
EA::Thread::Condition::Condition(const ConditionParameters* pConditionParameters, bool bDefaultParameters)
|
|
{
|
|
if(!pConditionParameters && bDefaultParameters)
|
|
{
|
|
ConditionParameters parameters;
|
|
Init(¶meters);
|
|
}
|
|
else
|
|
Init(pConditionParameters);
|
|
}
|
|
|
|
|
|
EA::Thread::Condition::~Condition()
|
|
{
|
|
// Empty
|
|
}
|
|
|
|
|
|
bool EA::Thread::Condition::Init(const ConditionParameters* pConditionParameters)
|
|
{
|
|
if(pConditionParameters)
|
|
{
|
|
// We have a problem with naming here. We implement our Condition variable with two semaphores and a mutex.
|
|
// It's not possible to have them all have the same name, since the OS will think you want them to be
|
|
// shared instances. What we really need is an explicit debug name that is separate from the OS name.
|
|
// And the ConditionParameters::mName should be that debug name only and not be applied to the child primitives.
|
|
|
|
const SemaphoreParameters sp1(0, pConditionParameters->mbIntraProcess, NULL); // Set the name to NULL, regardless of what pConditionParameters->mName is.
|
|
const SemaphoreParameters sp2(1, pConditionParameters->mbIntraProcess, NULL);
|
|
const MutexParameters mp(pConditionParameters->mbIntraProcess, NULL);
|
|
|
|
if(mConditionData.mSemaphoreBlockQueue.Init(&sp1) &&
|
|
mConditionData.mSemaphoreBlockLock .Init(&sp2) &&
|
|
mConditionData.mUnblockLock.Init(&mp))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
EA::Thread::Condition::Result EA::Thread::Condition::Wait(Mutex* pMutex, const ThreadTime& timeoutAbsolute)
|
|
{
|
|
int lockResult, result;
|
|
|
|
EAT_ASSERT(pMutex); // The user is required to pass a valid Mutex pointer.
|
|
|
|
++mConditionData.mnWaitersBlocked; // Note that this is an atomic operation.
|
|
|
|
EAT_ASSERT(pMutex->GetLockCount() == 1);
|
|
lockResult = pMutex->Unlock();
|
|
if(lockResult < 0)
|
|
return (Result)lockResult;
|
|
|
|
result = mConditionData.mSemaphoreBlockQueue.Wait(timeoutAbsolute);
|
|
EAT_ASSERT(result != EA::Thread::Semaphore::kResultError);
|
|
// Regardless of the result of the above error, we must press on with the code below.
|
|
|
|
mConditionData.mUnblockLock.Lock();
|
|
|
|
const int nWaitersToUnblock = mConditionData.mnWaitersToUnblock;
|
|
|
|
if(nWaitersToUnblock != 0)
|
|
--mConditionData.mnWaitersToUnblock;
|
|
else if(++mConditionData.mnWaitersDone == (INT_MAX / 2)) // This is not an atomic operation. We are within a mutex lock.
|
|
{
|
|
// Normally this doesn't happen, but can happen under very
|
|
// unusual circumstances, such as spurious semaphore signals
|
|
// or cases whereby many many threads are timing out.
|
|
EAT_ASSERT(false);
|
|
mConditionData.mSemaphoreBlockLock.Wait();
|
|
mConditionData.mnWaitersBlocked -= mConditionData.mnWaitersDone;
|
|
mConditionData.mSemaphoreBlockLock.Post();
|
|
mConditionData.mnWaitersDone = 0;
|
|
}
|
|
|
|
mConditionData.mUnblockLock.Unlock();
|
|
|
|
if(nWaitersToUnblock == 1) // If we were the last...
|
|
mConditionData.mSemaphoreBlockLock.Post();
|
|
|
|
// We cannot apply a timeout here. The caller always expects to have the
|
|
// lock upon return, even in the case of a wait timeout. Similarly, we
|
|
// may or may not want the result of the lock attempt to be propogated
|
|
// back to the caller. In this case, we do if it is an error.
|
|
lockResult = pMutex->Lock();
|
|
|
|
if(lockResult == Mutex::kResultError)
|
|
return kResultError;
|
|
else if(result >= 0)
|
|
return kResultOK;
|
|
|
|
return (Result)result; // This is the result of the wait call above.
|
|
}
|
|
|
|
|
|
bool EA::Thread::Condition::Signal(bool bBroadcast)
|
|
{
|
|
int result;
|
|
int nSignalsToIssue;
|
|
|
|
result = mConditionData.mUnblockLock.Lock();
|
|
|
|
if(result < 0)
|
|
return false;
|
|
|
|
if(mConditionData.mnWaitersToUnblock)
|
|
{
|
|
if(mConditionData.mnWaitersBlocked == 0)
|
|
{
|
|
mConditionData.mUnblockLock.Unlock();
|
|
return true;
|
|
}
|
|
|
|
if(bBroadcast)
|
|
{
|
|
nSignalsToIssue = (int)mConditionData.mnWaitersBlocked.SetValue(0);
|
|
mConditionData.mnWaitersToUnblock += nSignalsToIssue;
|
|
}
|
|
else
|
|
{
|
|
nSignalsToIssue = 1;
|
|
mConditionData.mnWaitersToUnblock++;
|
|
mConditionData.mnWaitersBlocked--;
|
|
}
|
|
}
|
|
else if(mConditionData.mnWaitersBlocked > mConditionData.mnWaitersDone)
|
|
{
|
|
if(mConditionData.mSemaphoreBlockLock.Wait() == EA::Thread::Semaphore::kResultError)
|
|
{
|
|
mConditionData.mUnblockLock.Unlock();
|
|
return false;
|
|
}
|
|
|
|
if(mConditionData.mnWaitersDone != 0)
|
|
{
|
|
mConditionData.mnWaitersBlocked -= mConditionData.mnWaitersDone;
|
|
mConditionData.mnWaitersDone = 0;
|
|
}
|
|
|
|
if(bBroadcast)
|
|
{
|
|
nSignalsToIssue = mConditionData.mnWaitersToUnblock = (int)mConditionData.mnWaitersBlocked.SetValue(0);
|
|
}
|
|
else
|
|
{
|
|
nSignalsToIssue = mConditionData.mnWaitersToUnblock = 1;
|
|
mConditionData.mnWaitersBlocked--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mConditionData.mUnblockLock.Unlock();
|
|
return true;
|
|
}
|
|
|
|
mConditionData.mUnblockLock.Unlock();
|
|
mConditionData.mSemaphoreBlockQueue.Post(nSignalsToIssue);
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // EA_PLATFORM_XXX
|
|
|
|
|
|
|
|
|
|
EA::Thread::Condition* EA::Thread::ConditionFactory::CreateCondition()
|
|
{
|
|
Allocator* pAllocator = GetAllocator();
|
|
|
|
if(pAllocator)
|
|
return new(pAllocator->Alloc(sizeof(EA::Thread::Condition))) EA::Thread::Condition;
|
|
else
|
|
return new EA::Thread::Condition;
|
|
}
|
|
|
|
void EA::Thread::ConditionFactory::DestroyCondition(EA::Thread::Condition* pCondition)
|
|
{
|
|
Allocator* pAllocator = GetAllocator();
|
|
|
|
if(pAllocator)
|
|
{
|
|
pCondition->~Condition();
|
|
pAllocator->Free(pCondition);
|
|
}
|
|
else
|
|
delete pCondition;
|
|
}
|
|
|
|
size_t EA::Thread::ConditionFactory::GetConditionSize()
|
|
{
|
|
return sizeof(EA::Thread::Condition);
|
|
}
|
|
|
|
EA::Thread::Condition* EA::Thread::ConditionFactory::ConstructCondition(void* pMemory)
|
|
{
|
|
return new(pMemory) EA::Thread::Condition;
|
|
}
|
|
|
|
void EA::Thread::ConditionFactory::DestructCondition(EA::Thread::Condition* pCondition)
|
|
{
|
|
pCondition->~Condition();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|