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.
351 lines
8.9 KiB
C++
351 lines
8.9 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <eathread/eathread_storage.h>
|
|
#include <eathread/eathread.h>
|
|
|
|
EA_DISABLE_VC_WARNING(4574)
|
|
#include <new>
|
|
EA_RESTORE_VC_WARNING()
|
|
|
|
#if defined(EA_PLATFORM_SONY)
|
|
#include <kernel.h>
|
|
|
|
EA::Thread::ThreadLocalStorage::ThreadLocalStorage()
|
|
: mTLSData()
|
|
{
|
|
// To consider: Support the specification of a destructor instead of just passing NULL.
|
|
mTLSData.mResult = scePthreadKeyCreate(&mTLSData.mKey, NULL);
|
|
EAT_ASSERT(mTLSData.mResult == 0);
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage::~ThreadLocalStorage()
|
|
{
|
|
if(mTLSData.mResult == 0)
|
|
scePthreadKeyDelete(mTLSData.mKey);
|
|
}
|
|
|
|
|
|
void* EA::Thread::ThreadLocalStorage::GetValue()
|
|
{
|
|
return scePthreadGetspecific(mTLSData.mKey);
|
|
}
|
|
|
|
|
|
bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData)
|
|
{
|
|
if(scePthreadSetspecific(mTLSData.mKey, pData) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && !defined(EA_PLATFORM_NX)
|
|
#if defined(EA_PLATFORM_UNIX)
|
|
#include <unistd.h>
|
|
#elif defined(EA_PLATFORM_WINDOWS)
|
|
EA_DISABLE_ALL_VC_WARNINGS()
|
|
#include <Windows.h>
|
|
EA_RESTORE_ALL_VC_WARNINGS()
|
|
#endif
|
|
|
|
EA::Thread::ThreadLocalStorage::ThreadLocalStorage()
|
|
: mTLSData()
|
|
{
|
|
// To consider: Support the specification of a destructor instead of just passing NULL.
|
|
mTLSData.mResult = pthread_key_create(&mTLSData.mKey, NULL);
|
|
EAT_ASSERT(mTLSData.mResult == 0);
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage::~ThreadLocalStorage()
|
|
{
|
|
if(mTLSData.mResult == 0)
|
|
pthread_key_delete(mTLSData.mKey);
|
|
}
|
|
|
|
|
|
void* EA::Thread::ThreadLocalStorage::GetValue()
|
|
{
|
|
return pthread_getspecific(mTLSData.mKey);
|
|
}
|
|
|
|
|
|
bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData)
|
|
{
|
|
if(pthread_setspecific(mTLSData.mKey, pData) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
#elif defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_WINDOWS_PHONE) && !(defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
|
|
EA_DISABLE_ALL_VC_WARNINGS()
|
|
#include <Windows.h>
|
|
EA_RESTORE_ALL_VC_WARNINGS()
|
|
|
|
EA::Thread::ThreadLocalStorage::ThreadLocalStorage()
|
|
: mTLSData(TlsAlloc())
|
|
{
|
|
EAT_ASSERT(mTLSData != TLS_OUT_OF_INDEXES);
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage::~ThreadLocalStorage()
|
|
{
|
|
if(mTLSData != TLS_OUT_OF_INDEXES)
|
|
TlsFree(mTLSData);
|
|
}
|
|
|
|
|
|
void* EA::Thread::ThreadLocalStorage::GetValue()
|
|
{
|
|
return TlsGetValue(mTLSData);
|
|
}
|
|
|
|
|
|
bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData)
|
|
{
|
|
if(TlsSetValue(mTLSData, (void*)pData))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
#elif (!EA_THREADS_AVAILABLE || defined(EA_PLATFORM_CONSOLE)) && !defined(EA_PLATFORM_NX)
|
|
|
|
#include <string.h>
|
|
|
|
#if !EA_THREADS_AVAILABLE
|
|
#define OSEnableInterrupts()
|
|
#define OSDisableInterrupts()
|
|
#else
|
|
#error Need to define EnableInterrupts/DisableInterrupts for the given platform.
|
|
#endif
|
|
|
|
|
|
EAThreadLocalStorageData::ThreadToDataPair* EAThreadLocalStorageData::GetTLSEntry(bool bCreateIfNotFound)
|
|
{
|
|
const int kArraySize = (sizeof(mDataArray) / sizeof(mDataArray[0]));
|
|
ThreadToDataPair* pCurrent, *pEnd;
|
|
|
|
EA::Thread::ThreadUniqueId nCurrentThreadID;
|
|
EAThreadGetUniqueId(nCurrentThreadID);
|
|
|
|
// The code below is likely to execute very quickly and never transfers
|
|
// execution outside the function, so we can very briefly disable interrupts
|
|
// for the period needed to do the logic below.
|
|
OSDisableInterrupts();
|
|
|
|
// We make the assumption that there are likely to be less than 10 threads most of
|
|
// the time. Thus, instead of maintaining a sorted array and do a binary search
|
|
// within that array, we do a linear search. An improvement would be to make the
|
|
// array be sorted if it goes above some preset size, such as 20.
|
|
for(pCurrent = mDataArray, pEnd = mDataArray + mDataArrayCount; pCurrent < pEnd; ++pCurrent)
|
|
{
|
|
if(pCurrent->mThreadID == nCurrentThreadID)
|
|
{
|
|
OSEnableInterrupts();
|
|
return pCurrent;
|
|
}
|
|
}
|
|
|
|
if((pCurrent >= pEnd) && ((mDataArrayCount + 1) < kArraySize) && bCreateIfNotFound) // If we didn't find it above and there is more room and we should create if not found...
|
|
{
|
|
pCurrent = mDataArray + mDataArrayCount++;
|
|
pCurrent->mThreadID = nCurrentThreadID;
|
|
}
|
|
else
|
|
pCurrent = NULL;
|
|
|
|
OSEnableInterrupts();
|
|
|
|
return pCurrent;
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage::ThreadLocalStorage()
|
|
{
|
|
memset(mTLSData.mDataArray, 0, sizeof(mTLSData.mDataArray));
|
|
mTLSData.mDataArrayCount = 0;
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage::~ThreadLocalStorage()
|
|
{
|
|
// Nothing to do.
|
|
}
|
|
|
|
|
|
void* EA::Thread::ThreadLocalStorage::GetValue()
|
|
{
|
|
EAThreadLocalStorageData::ThreadToDataPair* const pTDP = mTLSData.GetTLSEntry(false);
|
|
if(pTDP)
|
|
return (void*)pTDP->mpData;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData)
|
|
{
|
|
if(pData == NULL)
|
|
{ // We remove it from the container so that the container can have room for others.
|
|
EAThreadLocalStorageData::ThreadToDataPair* pTDP = mTLSData.GetTLSEntry(false);
|
|
|
|
if(pTDP)
|
|
{
|
|
OSDisableInterrupts(); // Briefly disable interrupts for the duration of the logic below.
|
|
const EAThreadLocalStorageData::ThreadToDataPair* const pTDPEnd = mTLSData.mDataArray + mTLSData.mDataArrayCount;
|
|
while(++pTDP <= pTDPEnd) // What we do here is move all the other values downward. This is an O(n) operation,
|
|
pTDP[-1] = pTDP[0]; // but the number of unique threads usinug us is likely to be pretty small.
|
|
mTLSData.mDataArrayCount = (int)(pTDPEnd - mTLSData.mDataArray - 1);
|
|
OSEnableInterrupts();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
EAThreadLocalStorageData::ThreadToDataPair* const pTDP = mTLSData.GetTLSEntry(true);
|
|
if(pTDP)
|
|
pTDP->mpData = pData;
|
|
return (pTDP != NULL);
|
|
}
|
|
|
|
#else
|
|
|
|
// Use reference std::map implementation.
|
|
EA_DISABLE_VC_WARNING(4574)
|
|
#include <map>
|
|
EA_RESTORE_VC_WARNING()
|
|
|
|
#include <eathread/eathread_futex.h>
|
|
|
|
void** EAThreadLocalStorageData::GetTLSEntry(bool bCreateIfNotFound)
|
|
{
|
|
EA::Thread::ThreadUniqueId nThreadID;
|
|
EAThreadGetUniqueId(nThreadID);
|
|
|
|
EA::Thread::AutoFutex autoFutex(mFutex);
|
|
|
|
if(bCreateIfNotFound) // We expect this to be true most of the time.
|
|
{
|
|
// Create as needed
|
|
if (mThreadToDataMap == NULL)
|
|
{
|
|
mThreadToDataMap = new std::map<EA::Thread::ThreadUniqueId, const void*>;
|
|
}
|
|
|
|
return (void**)(char*)&((*mThreadToDataMap)[nThreadID]); // Dereferencing a std::map value by index inserts the value if it is not present.
|
|
}
|
|
|
|
if (mThreadToDataMap == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
std::map<EA::Thread::ThreadUniqueId, const void*>::iterator it(mThreadToDataMap->find(nThreadID));
|
|
if(it != mThreadToDataMap->end())
|
|
{
|
|
std::map<EA::Thread::ThreadUniqueId, const void*>::value_type& value = *it;
|
|
return (void**)(char*)&value.second;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage::ThreadLocalStorage()
|
|
{
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage::~ThreadLocalStorage()
|
|
{
|
|
// Nothing to do.
|
|
}
|
|
|
|
|
|
void* EA::Thread::ThreadLocalStorage::GetValue()
|
|
{
|
|
void** const ppData = mTLSData.GetTLSEntry(false);
|
|
if(ppData)
|
|
return *ppData;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData)
|
|
{
|
|
if(pData == NULL)
|
|
{
|
|
ThreadUniqueId nThreadID;
|
|
EAThreadGetUniqueId(nThreadID);
|
|
|
|
EA::Thread::AutoFutex autoFutex(mTLSData.mFutex);
|
|
|
|
if (mTLSData.mThreadToDataMap)
|
|
{
|
|
std::map<EA::Thread::ThreadUniqueId, const void*>::iterator it(mTLSData.mThreadToDataMap->find(nThreadID));
|
|
if(it != mTLSData.mThreadToDataMap->end())
|
|
mTLSData.mThreadToDataMap->erase(it);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void** const ppData = mTLSData.GetTLSEntry(true);
|
|
if(ppData)
|
|
*ppData = (void*)pData;
|
|
return (*ppData != NULL);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
namespace EA
|
|
{
|
|
namespace Thread
|
|
{
|
|
extern Allocator* gpAllocator;
|
|
}
|
|
}
|
|
|
|
|
|
EA::Thread::ThreadLocalStorage* EA::Thread::ThreadLocalStorageFactory::CreateThreadLocalStorage()
|
|
{
|
|
if(gpAllocator)
|
|
return new(gpAllocator->Alloc(sizeof(EA::Thread::ThreadLocalStorage))) EA::Thread::ThreadLocalStorage;
|
|
else
|
|
return new EA::Thread::ThreadLocalStorage;
|
|
}
|
|
|
|
void EA::Thread::ThreadLocalStorageFactory::DestroyThreadLocalStorage(EA::Thread::ThreadLocalStorage* pThreadLocalStorage)
|
|
{
|
|
if(gpAllocator)
|
|
{
|
|
pThreadLocalStorage->~ThreadLocalStorage();
|
|
gpAllocator->Free(pThreadLocalStorage);
|
|
}
|
|
else
|
|
delete pThreadLocalStorage;
|
|
}
|
|
|
|
size_t EA::Thread::ThreadLocalStorageFactory::GetThreadLocalStorageSize()
|
|
{
|
|
return sizeof(EA::Thread::ThreadLocalStorage);
|
|
}
|
|
|
|
EA::Thread::ThreadLocalStorage* EA::Thread::ThreadLocalStorageFactory::ConstructThreadLocalStorage(void* pMemory)
|
|
{
|
|
return new(pMemory) EA::Thread::ThreadLocalStorage;
|
|
}
|
|
|
|
void EA::Thread::ThreadLocalStorageFactory::DestructThreadLocalStorage(EA::Thread::ThreadLocalStorage* pThreadLocalStorage)
|
|
{
|
|
pThreadLocalStorage->~ThreadLocalStorage();
|
|
}
|
|
|
|
|
|
#undef OSEnableInterrupts
|
|
#undef OSDisableInterrupts
|