You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
623 lines
16 KiB
C++
623 lines
16 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
|
|
#pragma once
|
|
|
|
#include <AzCore/PlatformRestrictedFileDef.h>
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
#include <sched.h>
|
|
|
|
#include <ISystem.h>
|
|
#include <ILog.h>
|
|
#include <errno.h>
|
|
|
|
// Section dictionary
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_REGISTER_THREAD 1
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_TRAITS 2
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_PTHREADCOND 3
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_CONSTRUCT 4
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_DESTROY 5
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_ACQUIRE 6
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_RELEASE 7
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_TRY_RLOCK 8
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_TRY_WLOCK 9
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_START_RUNNABLE 10
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_START_CPUMASK 11
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_START_CPUMASK_POSTCREATE 12
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_SETCPUMASK 13
|
|
#define CRYTHREAD_PTHREADS_H_SECTION_START_RUNNABLE_CPUMASK_POSTCREATE 14
|
|
#endif
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_REGISTER_THREAD
|
|
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#endif
|
|
|
|
#if !defined(RegisterThreadName)
|
|
#define RegisterThreadName(id, name)
|
|
#define UnRegisterThreadName(id)
|
|
#endif
|
|
|
|
#if defined(APPLE) || defined(ANDROID)
|
|
// PTHREAD_MUTEX_FAST_NP is only defined by Pthreads-w32, thus not on MAC
|
|
#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL
|
|
#endif
|
|
|
|
// Define LARGE_THREAD_STACK to use larger than normal per-thread stack
|
|
#if defined(_DEBUG) && (defined(MAC) || defined(LINUX) || defined(AZ_PLATFORM_IOS))
|
|
#define LARGE_THREAD_STACK
|
|
#endif
|
|
|
|
#if defined(LINUX)
|
|
#undef RegisterThreadName
|
|
ILINE void RegisterThreadName(pthread_t id, const char* name)
|
|
{
|
|
if ((!name) || (!id))
|
|
{
|
|
return;
|
|
}
|
|
int ret;
|
|
// pthread names on linux are limited to 16 char
|
|
if (strlen(name) >= 16)
|
|
{
|
|
char thread_name[16];
|
|
memcpy(thread_name, name, 15);
|
|
thread_name[15] = 0;
|
|
ret = pthread_setname_np(id, thread_name);
|
|
}
|
|
else
|
|
{
|
|
ret = pthread_setname_np(id, name);
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
CryLog("Failed to set thread name for %" PRI_THREADID ", name: %s", id, name);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined _CRYTHREAD_HAVE_LOCK
|
|
template<class LockClass>
|
|
class _PthreadCond;
|
|
template<int PthreadMutexType>
|
|
class _PthreadLockBase;
|
|
|
|
template<int PthreadMutexType>
|
|
class _PthreadLockAttr
|
|
{
|
|
friend class _PthreadLockBase<PthreadMutexType>;
|
|
|
|
protected:
|
|
_PthreadLockAttr()
|
|
{
|
|
pthread_mutexattr_init(&m_Attr);
|
|
pthread_mutexattr_settype(&m_Attr, PthreadMutexType);
|
|
}
|
|
~_PthreadLockAttr()
|
|
{
|
|
pthread_mutexattr_destroy(&m_Attr);
|
|
}
|
|
pthread_mutexattr_t m_Attr;
|
|
};
|
|
|
|
template<int PthreadMutexType>
|
|
class _PthreadLockBase
|
|
{
|
|
protected:
|
|
static pthread_mutexattr_t& GetAttr()
|
|
{
|
|
static _PthreadLockAttr<PthreadMutexType> m_Attr;
|
|
return m_Attr.m_Attr;
|
|
}
|
|
};
|
|
|
|
template<class LockClass, int PthreadMutexType>
|
|
class _PthreadLock
|
|
: public _PthreadLockBase<PthreadMutexType>
|
|
{
|
|
friend class _PthreadCond<LockClass>;
|
|
|
|
//#if defined(_DEBUG)
|
|
public:
|
|
//#endif
|
|
pthread_mutex_t m_Lock;
|
|
|
|
public:
|
|
_PthreadLock()
|
|
: LockCount(0)
|
|
{
|
|
pthread_mutex_init(
|
|
&m_Lock,
|
|
&_PthreadLockBase<PthreadMutexType>::GetAttr());
|
|
}
|
|
~_PthreadLock() { pthread_mutex_destroy(&m_Lock); }
|
|
|
|
void Lock() { pthread_mutex_lock(&m_Lock); CryInterlockedIncrement(&LockCount); }
|
|
|
|
bool TryLock()
|
|
{
|
|
const int rc = pthread_mutex_trylock(&m_Lock);
|
|
if (0 == rc)
|
|
{
|
|
CryInterlockedIncrement(&LockCount);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Unlock() { CryInterlockedDecrement(&LockCount); pthread_mutex_unlock(&m_Lock); }
|
|
|
|
// Get the POSIX pthread_mutex_t.
|
|
// Warning:
|
|
// This method will not be available in the Win32 port of CryThread.
|
|
pthread_mutex_t& Get_pthread_mutex_t() { return m_Lock; }
|
|
|
|
bool IsLocked()
|
|
{
|
|
#if defined(LINUX) || defined(APPLE)
|
|
// implementation taken from CrysisWars
|
|
return LockCount > 0;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
volatile int LockCount;
|
|
};
|
|
|
|
#if defined CRYLOCK_HAVE_FASTLOCK
|
|
#if defined(_DEBUG) && defined(PTHREAD_MUTEX_ERRORCHECK_NP)
|
|
template<>
|
|
class CryLockT<CRYLOCK_FAST>
|
|
: public _PthreadLock<CryLockT<CRYLOCK_FAST>, PTHREAD_MUTEX_ERRORCHECK_NP>
|
|
#else
|
|
template<>
|
|
class CryLockT<CRYLOCK_FAST>
|
|
: public _PthreadLock<CryLockT<CRYLOCK_FAST>, PTHREAD_MUTEX_FAST_NP>
|
|
#endif
|
|
{
|
|
CryLockT(const CryLockT<CRYLOCK_FAST>&);
|
|
void operator = (const CryLockT<CRYLOCK_FAST>&);
|
|
|
|
public:
|
|
CryLockT() { }
|
|
};
|
|
#endif // CRYLOCK_HAVE_FASTLOCK
|
|
|
|
template<>
|
|
class CryLockT<CRYLOCK_RECURSIVE>
|
|
: public _PthreadLock<CryLockT<CRYLOCK_RECURSIVE>, PTHREAD_MUTEX_RECURSIVE>
|
|
{
|
|
CryLockT(const CryLockT<CRYLOCK_RECURSIVE>&);
|
|
void operator = (const CryLockT<CRYLOCK_RECURSIVE>&);
|
|
|
|
public:
|
|
CryLockT() { }
|
|
};
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_TRAITS
|
|
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
|
|
#else
|
|
#if !defined(LINUX) && !defined(APPLE)
|
|
#define CRYTHREAD_PTHREADS_H_TRAIT_DEFINE_CRYMUTEX 1
|
|
#endif
|
|
#if !defined(LINUX) && !defined(APPLE)
|
|
#define CRYTHREAD_PTHREADS_H_TRAIT_SET_THREAD_NAME 1
|
|
#endif
|
|
#endif
|
|
|
|
#if CRYTHREAD_PTHREADS_H_TRAIT_DEFINE_CRYMUTEX
|
|
#if defined CRYLOCK_HAVE_FASTLOCK
|
|
class CryMutex
|
|
: public CryLockT<CRYLOCK_FAST>
|
|
{
|
|
};
|
|
#else
|
|
class CryMutex
|
|
: public CryLockT<CRYLOCK_RECURSIVE>
|
|
{
|
|
};
|
|
#endif
|
|
#endif // CRYTHREAD_PTHREADS_TRAIT_DEFINE_CRYMUTEX
|
|
|
|
template<class LockClass>
|
|
class _PthreadCond
|
|
{
|
|
pthread_cond_t m_Cond;
|
|
|
|
public:
|
|
_PthreadCond() { pthread_cond_init(&m_Cond, NULL); }
|
|
~_PthreadCond() { pthread_cond_destroy(&m_Cond); }
|
|
void Notify() { pthread_cond_broadcast(&m_Cond); }
|
|
void NotifySingle() { pthread_cond_signal(&m_Cond); }
|
|
void Wait(LockClass& Lock) { pthread_cond_wait(&m_Cond, &Lock.m_Lock); }
|
|
bool TimedWait(LockClass& Lock, uint32 milliseconds)
|
|
{
|
|
struct timeval now;
|
|
struct timespec timeout;
|
|
int err;
|
|
|
|
gettimeofday(&now, NULL);
|
|
while (true)
|
|
{
|
|
timeout.tv_sec = now.tv_sec + milliseconds / 1000;
|
|
uint64 nsec = (uint64)now.tv_usec * 1000 + (uint64)milliseconds * 1000000;
|
|
if (nsec >= 1000000000)
|
|
{
|
|
timeout.tv_sec += (long)(nsec / 1000000000);
|
|
nsec %= 1000000000;
|
|
}
|
|
timeout.tv_nsec = (long)nsec;
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_PTHREADCOND
|
|
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#else
|
|
err = pthread_cond_timedwait(&m_Cond, &Lock.m_Lock, &timeout);
|
|
if (err == EINTR)
|
|
{
|
|
// Interrupted by a signal.
|
|
continue;
|
|
}
|
|
else if (err == ETIMEDOUT)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
assert(err == 0);
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Get the POSIX pthread_cont_t.
|
|
// Warning:
|
|
// This method will not be available in the Win32 port of CryThread.
|
|
pthread_cond_t& Get_pthread_cond_t() { return m_Cond; }
|
|
};
|
|
|
|
#if AZ_LEGACY_CRYCOMMON_TRAIT_USE_PTHREADS
|
|
template <class LockClass>
|
|
class CryConditionVariableT
|
|
: public _PthreadCond<LockClass>
|
|
{
|
|
};
|
|
|
|
#if defined CRYLOCK_HAVE_FASTTLOCK
|
|
template<>
|
|
class CryConditionVariableT< CryLockT<CRYLOCK_FAST> >
|
|
: public _PthreadCond< CryLockT<CRYLOCK_FAST> >
|
|
{
|
|
typedef CryLockT<CRYLOCK_FAST> LockClass;
|
|
CryConditionVariableT(const CryConditionVariableT<LockClass>&);
|
|
CryConditionVariableT<LockClass>& operator = (const CryConditionVariableT<LockClass>&);
|
|
|
|
public:
|
|
CryConditionVariableT() { }
|
|
};
|
|
#endif // CRYLOCK_HAVE_FASTLOCK
|
|
|
|
template<>
|
|
class CryConditionVariableT< CryLockT<CRYLOCK_RECURSIVE> >
|
|
: public _PthreadCond< CryLockT<CRYLOCK_RECURSIVE> >
|
|
{
|
|
typedef CryLockT<CRYLOCK_RECURSIVE> LockClass;
|
|
CryConditionVariableT(const CryConditionVariableT<LockClass>&);
|
|
CryConditionVariableT<LockClass>& operator = (const CryConditionVariableT<LockClass>&);
|
|
|
|
public:
|
|
CryConditionVariableT() { }
|
|
};
|
|
|
|
#if !defined(_CRYTHREAD_CONDLOCK_GLITCH)
|
|
typedef CryConditionVariableT< CryLockT<CRYLOCK_RECURSIVE> > CryConditionVariable;
|
|
#else
|
|
typedef CryConditionVariableT< CryLockT<CRYLOCK_FAST> > CryConditionVariable;
|
|
#endif
|
|
|
|
#define _CRYTHREAD_HAVE_LOCK 1
|
|
|
|
#else // LINUX MAC
|
|
|
|
#if defined CRYLOCK_HAVE_FASTLOCK
|
|
template<>
|
|
class CryConditionVariable
|
|
: public _PthreadCond< CryLockT<CRYLOCK_FAST> >
|
|
{
|
|
typedef CryLockT<CRYLOCK_FAST> LockClass;
|
|
CryConditionVariable(const CryConditionVariable&);
|
|
CryConditionVariable& operator = (const CryConditionVariable&);
|
|
|
|
public:
|
|
CryConditionVariable() { }
|
|
};
|
|
#endif // CRYLOCK_HAVE_FASTLOCK
|
|
|
|
template<>
|
|
class CryConditionVariable
|
|
: public _PthreadCond< CryLockT<CRYLOCK_RECURSIVE> >
|
|
{
|
|
typedef CryLockT<CRYLOCK_RECURSIVE> LockClass;
|
|
CryConditionVariable(const CryConditionVariable&);
|
|
CryConditionVariable& operator = (const CryConditionVariable&);
|
|
|
|
public:
|
|
CryConditionVariable() { }
|
|
};
|
|
|
|
#define _CRYTHREAD_HAVE_LOCK 1
|
|
|
|
#endif // LINUX MAC
|
|
#endif // !defined _CRYTHREAD_HAVE_LOCK
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Platform independet wrapper for a counting semaphore
|
|
class CrySemaphore
|
|
{
|
|
public:
|
|
CrySemaphore(int nMaximumCount, int nInitialCount = 0);
|
|
~CrySemaphore();
|
|
|
|
void Acquire();
|
|
void Release();
|
|
|
|
private:
|
|
#if defined(APPLE)
|
|
// Apple only supports named semaphores so have to use sem_open/unlink/sem_close instead
|
|
// of sem_open/sem_destroy, passing in this array for the name.
|
|
char m_semaphoreName[L_tmpnam];
|
|
#endif
|
|
sem_t* m_Semaphore;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
inline CrySemaphore::CrySemaphore(int nMaximumCount, int nInitialCount)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_CONSTRUCT
|
|
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(APPLE)
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
tmpnam(m_semaphoreName);
|
|
# pragma clang diagnostic pop
|
|
m_Semaphore = sem_open(m_semaphoreName, O_CREAT | O_EXCL, 0644, nInitialCount);
|
|
#else
|
|
m_Semaphore = new sem_t;
|
|
sem_init(m_Semaphore, 0, nInitialCount);
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
inline CrySemaphore::~CrySemaphore()
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_DESTROY
|
|
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(APPLE)
|
|
sem_close(m_Semaphore);
|
|
sem_unlink(m_semaphoreName);
|
|
#else
|
|
sem_destroy(m_Semaphore);
|
|
delete m_Semaphore;
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
inline void CrySemaphore::Acquire()
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_ACQUIRE
|
|
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#else
|
|
while (sem_wait(m_Semaphore) != 0 && errno == EINTR)
|
|
{
|
|
;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
inline void CrySemaphore::Release()
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_SEMAPHORE_RELEASE
|
|
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#else
|
|
sem_post(m_Semaphore);
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Platform independet wrapper for a counting semaphore
|
|
// except that this version uses C-A-S only until a blocking call is needed
|
|
// -> No kernel call if there are object in the semaphore
|
|
|
|
class CryFastSemaphore
|
|
{
|
|
public:
|
|
CryFastSemaphore(int nMaximumCount, int nInitialCount = 0);
|
|
~CryFastSemaphore();
|
|
void Acquire();
|
|
void Release();
|
|
|
|
private:
|
|
CrySemaphore m_Semaphore;
|
|
volatile int32 m_nCounter;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
inline CryFastSemaphore::CryFastSemaphore(int nMaximumCount, int nInitialCount)
|
|
: m_Semaphore(nMaximumCount)
|
|
, m_nCounter(nInitialCount)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
inline CryFastSemaphore::~CryFastSemaphore()
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
inline void CryFastSemaphore::Acquire()
|
|
{
|
|
int nCount = ~0;
|
|
do
|
|
{
|
|
nCount = *const_cast<volatile int*>(&m_nCounter);
|
|
} while (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&m_nCounter), nCount - 1, nCount) != nCount);
|
|
|
|
// if the count would have been 0 or below, go to kernel semaphore
|
|
if ((nCount - 1) < 0)
|
|
{
|
|
m_Semaphore.Acquire();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
inline void CryFastSemaphore::Release()
|
|
{
|
|
int nCount = ~0;
|
|
do
|
|
{
|
|
nCount = *const_cast<volatile int*>(&m_nCounter);
|
|
} while (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&m_nCounter), nCount + 1, nCount) != nCount);
|
|
|
|
// wake up kernel semaphore if we have waiter
|
|
if (nCount < 0)
|
|
{
|
|
m_Semaphore.Release();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Provide TLS implementation using pthreads for those platforms without __thread
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct SCryPthreadTLSBase
|
|
{
|
|
SCryPthreadTLSBase(void (*pDestructor)(void*))
|
|
{
|
|
pthread_key_create(&m_kKey, pDestructor);
|
|
}
|
|
|
|
~SCryPthreadTLSBase()
|
|
{
|
|
pthread_key_delete(m_kKey);
|
|
}
|
|
|
|
void* GetSpecific()
|
|
{
|
|
return pthread_getspecific(m_kKey);
|
|
}
|
|
|
|
void SetSpecific(const void* pValue)
|
|
{
|
|
pthread_setspecific(m_kKey, pValue);
|
|
}
|
|
|
|
pthread_key_t m_kKey;
|
|
};
|
|
|
|
template <typename T, bool bDirect>
|
|
struct SCryPthreadTLSImpl{};
|
|
|
|
template <typename T>
|
|
struct SCryPthreadTLSImpl<T, true>
|
|
: private SCryPthreadTLSBase
|
|
{
|
|
SCryPthreadTLSImpl()
|
|
: SCryPthreadTLSBase(NULL)
|
|
{
|
|
}
|
|
|
|
T Get()
|
|
{
|
|
void* pSpecific(GetSpecific());
|
|
return *reinterpret_cast<const T*>(&pSpecific);
|
|
}
|
|
|
|
void Set(const T& kValue)
|
|
{
|
|
SetSpecific(*reinterpret_cast<const void* const*>(&kValue));
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct SCryPthreadTLSImpl<T, false>
|
|
: private SCryPthreadTLSBase
|
|
{
|
|
SCryPthreadTLSImpl()
|
|
: SCryPthreadTLSBase(&Destroy)
|
|
{
|
|
}
|
|
|
|
T* GetPtr()
|
|
{
|
|
T* pPtr(static_cast<T*>(GetSpecific()));
|
|
if (pPtr == NULL)
|
|
{
|
|
pPtr = new T();
|
|
SetSpecific(pPtr);
|
|
}
|
|
return pPtr;
|
|
}
|
|
|
|
static void Destroy(void* pPointer)
|
|
{
|
|
delete static_cast<T*>(pPointer);
|
|
}
|
|
|
|
const T& Get()
|
|
{
|
|
return *GetPtr();
|
|
}
|
|
|
|
void Set(const T& kValue)
|
|
{
|
|
*GetPtr() = kValue;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct SCryPthreadTLS
|
|
: SCryPthreadTLSImpl<T, sizeof(T) <= sizeof(void*)>
|
|
{
|
|
};
|
|
|
|
#include "MemoryAccess.h"
|