/********************************************************************** Filename : GAtomic.h Content : Contains atomic operations and inline fastest locking functionality. Will contain #ifdefs for OS efficiency. Have non-thread-safe implementaion if not available. Created : May 5, 2003 Authors : Michael Antonov, Andrew Reisse Copyright : (c) 2003-2007 Scaleform Corp. All Rights Reserved. Licensees may use this file in accordance with the valid Scaleform Commercial License Agreement provided with the software. This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE. **********************************************************************/ #include "GAtomic.h" #ifndef GFC_NO_THREADSUPPORT // ***** Windows Lock implementation #if defined(GFC_OS_WIN32) || defined(GFC_OS_WINCE) || defined(GFC_OS_XBOX) || defined(GFC_OS_XBOX360) #if !defined(GFC_FAST_GLOCK) // ***** Standard Win32 GLock implementation #if defined(GFC_OS_WIN32) // Spin count init critical section function prototype for Window NT typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); #endif // Constructors GLock::GLock(UInt spinCount) { #if RSG_DURANGO ::InitializeCriticalSectionAndSpinCount(&cs, spinCount); #elif defined(GFC_OS_WIN32) // Try to load function dynamically so that we don't require NT // On Windows NT we will use InitializeCriticalSectionAndSpinCount static bool initTried = 0; static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0; if (!initTried) { HMODULE hmodule = ::LoadLibrary(GSTR("kernel32.dll")); pInitFn = (Function_InitializeCriticalSectionAndSpinCount) ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount"); initTried = 1; } // Initialize the critical section if (pInitFn) pInitFn(&cs, spinCount); else ::InitializeCriticalSection(&cs); #elif defined(GFC_OS_XBOX360) ::InitializeCriticalSectionAndSpinCount(&cs, spinCount); #else ::InitializeCriticalSection(&cs); #endif } GLock::~GLock() { DeleteCriticalSection(&cs); } #else // ***** Fast Win32 + X86 GLock implementation GLock::GLock(UInt spinCount) { RecursiveLockCount = 0; LockedThreadId.Value= 0; WaiterCount.Value = 0; hSemaphore = 0; SetSpinMax(spinCount); } GLock::~GLock() { if (hSemaphore) CloseHandle(hSemaphore); } // *** Locking functions. void GLock::Lock() { DWORD threadId = GetCurrentThreadId(); if (threadId != LockedThreadId.Value) { if ((LockedThreadId.Value == 0) && PerfLockImmediate(threadId)) { // Successful single instruction quick-lock } else { PerfLock(threadId); } } RecursiveLockCount++; } void GLock::Unlock() { GASSERT(RecursiveLockCount > 0); --RecursiveLockCount; if (RecursiveLockCount == 0) PerfUnlock(); } #include void GLock::PerfUnlock() { //_WriteBarrier(); // We need to use release semantics here, however calling 'Store_Release' // on x86 is slow here because it uses 'xchg'. It is performance critical // to perform an unlock through a regular assignment, even though if this // was non-x86 we would've used LockedThreadId.Store_Release(0). // TBD: Details of Release effect on X86 need to be researched, to see // where xchg is actually necessary. LockedThreadId.Value = 0; //_ReadWriteBarrier(); // AFTER it is released we check if there're waiters. if (WaiterCount.Load_Acquire() > 0) { WaiterMinus(); GASSERT(hSemaphore); GASSERT(ReleaseSemaphore(hSemaphore, 1, NULL) != 0); } } void GLock::PerfLock(DWORD threadId) { // Attempt spin-lock for (UInt spin = 0; spin < SpinMax; spin++) { if (PerfLockImmediate(threadId)) return; #ifdef GFC_CPU_X86 GASM { pause }; #endif //YieldProcessor(); } // Ensure we have the kernel event created AllocateKernelSemaphore(); // Do a kernel lock with potential semaphore wait. while (1) { WaiterPlus(); if (PerfLockImmediate(threadId)) break; GASSERT(WaitForSingleObject(hSemaphore, INFINITE) == WAIT_OBJECT_0); } WaiterMinus(); } // Utility functions to access system Info & Init semaphore. static UInt SpinMax_ProcessorCount = 0xFFFFFFFF; void GLock::SetSpinMax(UInt spinMax) { if (SpinMax_ProcessorCount == 0xFFFFFFFF) { SYSTEM_INFO stSI; GetSystemInfo(&stSI); SpinMax_ProcessorCount = (UInt) stSI.dwNumberOfProcessors; } SpinMax = (SpinMax_ProcessorCount > 1) ? spinMax : 0; } void GLock::AllocateKernelSemaphore() { if (!hSemaphore) { HANDLE hnewSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); GASSERT(hnewSemaphore != 0); if (InterlockedCompareExchangePointer(&hSemaphore, hnewSemaphore, NULL)) CloseHandle(hnewSemaphore); // we're late } } #endif #elif defined(GFC_OS_ORBIS) ScePthreadMutexattr GLock::RecursiveAttr; bool GLock::RecursiveAttrInit; #endif // OS #endif // GFC_NO_THREADSUPPORT