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.
224 lines
5.3 KiB
C++
224 lines
5.3 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include <EABase/eabase.h>
|
|
#include <eathread/internal/config.h>
|
|
#include <eathread/eathread_mutex.h>
|
|
|
|
|
|
#if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#ifdef EA_PLATFORM_WINDOWS
|
|
EA_DISABLE_ALL_VC_WARNINGS()
|
|
#include <Windows.h> // Presumably we are using pthreads-win32.
|
|
EA_RESTORE_ALL_VC_WARNINGS()
|
|
#ifdef CreateMutex
|
|
#undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW.
|
|
#endif
|
|
#endif
|
|
|
|
|
|
EAMutexData::EAMutexData()
|
|
: mMutex(), mnLockCount(0)
|
|
{
|
|
#if EAT_ASSERT_ENABLED
|
|
mThreadId = EA::Thread::kThreadIdInvalid;
|
|
#endif
|
|
|
|
::memset(&mMutex, 0, sizeof(mMutex));
|
|
}
|
|
|
|
void EAMutexData::SimulateLock(bool bLock)
|
|
{
|
|
if(bLock)
|
|
{
|
|
++mnLockCount;
|
|
|
|
#if EAT_ASSERT_ENABLED
|
|
mThreadId = EA::Thread::GetThreadId();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
--mnLockCount;
|
|
|
|
#if EAT_ASSERT_ENABLED
|
|
mThreadId = EA::Thread::kThreadIdInvalid;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
EA::Thread::MutexParameters::MutexParameters(bool bIntraProcess, const char* /*pName*/)
|
|
: mbIntraProcess(bIntraProcess)
|
|
{
|
|
// Empty
|
|
}
|
|
|
|
|
|
EA::Thread::Mutex::Mutex(const MutexParameters* pMutexParameters, bool bDefaultParameters)
|
|
{
|
|
if(!pMutexParameters && bDefaultParameters)
|
|
{
|
|
MutexParameters parameters;
|
|
Init(¶meters);
|
|
}
|
|
else
|
|
Init(pMutexParameters);
|
|
}
|
|
|
|
|
|
EA::Thread::Mutex::~Mutex()
|
|
{
|
|
EAT_ASSERT(mMutexData.mnLockCount == 0);
|
|
pthread_mutex_destroy(&mMutexData.mMutex);
|
|
}
|
|
|
|
|
|
bool EA::Thread::Mutex::Init(const MutexParameters* pMutexParameters)
|
|
{
|
|
if(pMutexParameters)
|
|
{
|
|
mMutexData.mnLockCount = 0;
|
|
|
|
pthread_mutexattr_t attr;
|
|
pthread_mutexattr_init(&attr);
|
|
|
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
|
|
#if !defined( EA_PLATFORM_NX ) // NX defines PTHREAD_PROCESS_PRIVATE but doesn't implement pthread_mutexattr_setpshared because they are all private.
|
|
#if defined(PTHREAD_PROCESS_PRIVATE) // Some pthread implementations don't recognize this.
|
|
#if defined(PTHREAD_PROCESS_SHARED)
|
|
if (pMutexParameters->mbIntraProcess)
|
|
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
|
|
else
|
|
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
|
#else
|
|
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
const int result = pthread_mutex_init(&mMutexData.mMutex, &attr);
|
|
pthread_mutexattr_destroy(&attr);
|
|
|
|
EAT_ASSERT(result != -1);
|
|
return (result != -1);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int EA::Thread::Mutex::Lock(const ThreadTime& timeoutAbsolute)
|
|
{
|
|
int result;
|
|
|
|
EAT_ASSERT(mMutexData.mnLockCount < 100000);
|
|
|
|
if(timeoutAbsolute == kTimeoutNone)
|
|
{
|
|
result = pthread_mutex_lock(&mMutexData.mMutex);
|
|
|
|
if(result != 0)
|
|
{
|
|
EAT_ASSERT(false);
|
|
return kResultError;
|
|
}
|
|
}
|
|
else if(timeoutAbsolute == kTimeoutImmediate)
|
|
{
|
|
result = pthread_mutex_trylock(&mMutexData.mMutex);
|
|
|
|
if(result != 0)
|
|
{
|
|
if(result == EBUSY)
|
|
return kResultTimeout;
|
|
|
|
EAT_ASSERT(false);
|
|
return kResultError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if (defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_WINDOWS)) && !defined(EA_PLATFORM_CYGWIN) && !defined(EA_PLATFORM_ANDROID)
|
|
const timespec* pTimeSpec = &timeoutAbsolute;
|
|
result = pthread_mutex_timedlock(&mMutexData.mMutex, const_cast<timespec*>(pTimeSpec)); // Some pthread implementations use non-const timespec, so cast for them.
|
|
|
|
if(result != 0)
|
|
{
|
|
if(result == ETIMEDOUT)
|
|
return kResultTimeout;
|
|
EAT_ASSERT(false);
|
|
return kResultError;
|
|
}
|
|
#else // OSX, BSD
|
|
// Some Posix systems don't have pthread_mutex_timedlock. In these
|
|
// cases we fall back to a polling mechanism. However, polling really
|
|
// isn't proper because the polling thread might be at a greater
|
|
// priority level than the lock-owning thread and thus this code
|
|
// might not work as well as desired.
|
|
while(((result = pthread_mutex_trylock(&mMutexData.mMutex)) != 0) && (GetThreadTime() < timeoutAbsolute))
|
|
ThreadSleep(1);
|
|
|
|
if(result != 0)
|
|
{
|
|
if(result == EBUSY)
|
|
return kResultTimeout;
|
|
EAT_ASSERT(false);
|
|
return kResultError;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EAT_ASSERT(mMutexData.mThreadId = EA::Thread::GetThreadId()); // Intentionally '=' here and not '=='.
|
|
EAT_ASSERT(mMutexData.mnLockCount >= 0);
|
|
return ++mMutexData.mnLockCount; // This is safe to do because we have the lock.
|
|
}
|
|
|
|
|
|
int EA::Thread::Mutex::Unlock()
|
|
{
|
|
EAT_ASSERT(mMutexData.mThreadId == EA::Thread::GetThreadId());
|
|
EAT_ASSERT(mMutexData.mnLockCount > 0);
|
|
|
|
const int nReturnValue(--mMutexData.mnLockCount); // This is safe to do because we have the lock.
|
|
|
|
if(pthread_mutex_unlock(&mMutexData.mMutex) != 0)
|
|
{
|
|
EAT_ASSERT(false);
|
|
return nReturnValue + 1;
|
|
}
|
|
|
|
return nReturnValue;
|
|
}
|
|
|
|
|
|
int EA::Thread::Mutex::GetLockCount() const
|
|
{
|
|
return mMutexData.mnLockCount;
|
|
}
|
|
|
|
|
|
bool EA::Thread::Mutex::HasLock() const
|
|
{
|
|
#if EAT_ASSERT_ENABLED
|
|
return (mMutexData.mnLockCount > 0) && (mMutexData.mThreadId == GetThreadId());
|
|
#else
|
|
return (mMutexData.mnLockCount > 0); // This is the best we can do.
|
|
#endif
|
|
}
|
|
|
|
|
|
#endif // EA_PLATFORM_XXX
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|