| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include <eathread/internal/config.h>
- #include <eathread/eathread_rwmutex_ip.h>
- #include <new> // include new for placement new operator
- #include <string.h>
- #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- ///////////////////////////////////////////////////////////////////////////
- // EARWMutexIPData
- ///////////////////////////////////////////////////////////////////////////
- EA::Thread::EARWMutexIPData::EARWMutexIPData()
- : mSharedData(), // This still needs to be Init-ed.
- mMutex(NULL),
- mReadSemaphore(NULL),
- mWriteSemaphore(NULL)
- {
- }
- EA::Thread::EARWMutexIPData::~EARWMutexIPData()
- {
- // mSharedData.Shutdown(); // This shouldn't be necessary, as the SharedData dtor will do this itself.
- }
- bool EA::Thread::EARWMutexIPData::Init(const char* pName)
- {
- char mutexName[256];
- mutexName[0] = '\0';
- if(pName)
- strcpy(mutexName, pName);
- strcat(mutexName, ".Mutex");
- mMutex = CreateMutexA(NULL, FALSE, mutexName);
- char readSemaphoreName[256];
- readSemaphoreName[0] = '\0';
- if(pName)
- strcpy(readSemaphoreName, pName);
- strcat(readSemaphoreName, ".SemR");
- mReadSemaphore = CreateSemaphoreA(NULL, 0, 9999, readSemaphoreName);
- char writeSemaphoreName[256];
- writeSemaphoreName[0] = '\0';
- if(pName)
- strcpy(writeSemaphoreName, pName);
- strcat(writeSemaphoreName, ".SemW");
- mWriteSemaphore = CreateSemaphoreA(NULL, 0, 9999, writeSemaphoreName);
- return mSharedData.Init(pName);
- }
- void EA::Thread::EARWMutexIPData::Shutdown()
- {
- if(mMutex)
- {
- CloseHandle(mMutex);
- mMutex = NULL;
- }
- if(mReadSemaphore)
- {
- CloseHandle(mReadSemaphore);
- mReadSemaphore = NULL;
- }
- if(mWriteSemaphore)
- {
- CloseHandle(mWriteSemaphore);
- mWriteSemaphore = NULL;
- }
- mSharedData.Shutdown();
- }
- ///////////////////////////////////////////////////////////////////////////
- // RWMutexIPParameters
- ///////////////////////////////////////////////////////////////////////////
- EA::Thread::RWMutexIPParameters::RWMutexIPParameters(bool bIntraProcess, const char* pName)
- : mbIntraProcess(bIntraProcess)
- {
- #ifdef EA_PLATFORM_WINDOWS
- if(pName)
- {
- strncpy(mName, pName, sizeof(mName)-1);
- mName[sizeof(mName)-1] = 0;
- }
- else
- mName[0] = 0;
- #else
- (void)pName; // Suppress possible warnings.
- #endif
- }
- ///////////////////////////////////////////////////////////////////////////
- // RWMutexIP
- ///////////////////////////////////////////////////////////////////////////
- EA::Thread::RWMutexIP::RWMutexIP(const RWMutexIPParameters* pRWMutexIPParameters, bool bDefaultParameters)
- {
- if(!pRWMutexIPParameters && bDefaultParameters)
- {
- RWMutexIPParameters parameters;
- Init(¶meters);
- }
- else
- Init(pRWMutexIPParameters);
- }
-
-
- EA::Thread::RWMutexIP::~RWMutexIP()
- {
- }
-
- bool EA::Thread::RWMutexIP::Init(const RWMutexIPParameters* pRWMutexIPParameters)
- {
- if(pRWMutexIPParameters)
- {
- // Must provide a valid name for inter-process RWMutex.
- EAT_ASSERT(pRWMutexIPParameters->mbIntraProcess || pRWMutexIPParameters->mName[0]);
- return mRWMutexIPData.Init(pRWMutexIPParameters->mName);
- }
- return false;
- }
- int EA::Thread::RWMutexIP::Lock(LockType lockType, const ThreadTime& /*timeoutAbsolute*/)
- {
- int result = 0;
- WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily.
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
-
- // We cannot obtain a write lock recursively, else we will deadlock.
- // Alternatively, we can build a bunch of extra logic to deal with this.
- EAT_ASSERT(mRWMutexIPData.mSharedData->mThreadIdWriter != ::GetCurrentThreadId());
-
- // Assert that there aren't both readers and writers at the same time.
- EAT_ASSERT(!((mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid) && mRWMutexIPData.mSharedData->mnReaders));
- if(lockType == kLockTypeRead)
- {
- while(mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid)
- {
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
-
- mRWMutexIPData.mSharedData->mnReadWaiters++;
- ReleaseMutex(mRWMutexIPData.mMutex);
- DWORD dwResult = WaitForSingleObject(mRWMutexIPData.mReadSemaphore, INFINITE); // To do: support timeoutAbsolute
- WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE);
- mRWMutexIPData.mSharedData->mnReadWaiters--;
-
- EAT_ASSERT(dwResult != WAIT_FAILED);
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
-
- if(dwResult == WAIT_TIMEOUT)
- {
- ReleaseMutex(mRWMutexIPData.mMutex);
- return kResultTimeout;
- }
- }
-
- result = ++mRWMutexIPData.mSharedData->mnReaders; // This is not an atomic operation. We are within a mutex lock.
- }
- else if(lockType == kLockTypeWrite)
- {
- while((mRWMutexIPData.mSharedData->mnReaders > 0) || // While somebody has the read lock or
- (mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid)) // somebody has the write lock... go back to waiting.
- {
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
-
- mRWMutexIPData.mSharedData->mnWriteWaiters++;
- ReleaseMutex(mRWMutexIPData.mMutex);
- DWORD dwResult = WaitForSingleObject(mRWMutexIPData.mWriteSemaphore, INFINITE); // To do: support timeoutAbsolute
- WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE);
- mRWMutexIPData.mSharedData->mnWriteWaiters--;
-
- EAT_ASSERT(dwResult != WAIT_FAILED);
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
-
- if(dwResult == WAIT_TIMEOUT)
- {
- ReleaseMutex(mRWMutexIPData.mMutex);
- return kResultTimeout;
- }
- }
-
- result = 1;
- mRWMutexIPData.mSharedData->mThreadIdWriter = ::GetCurrentThreadId();
- }
-
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
- ReleaseMutex(mRWMutexIPData.mMutex);
-
- return result;
- }
- int EA::Thread::RWMutexIP::Unlock()
- {
- WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily.
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
- if(mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid) // If we have a write lock...
- {
- EAT_ASSERT(mRWMutexIPData.mSharedData->mThreadIdWriter == ::GetCurrentThreadId());
- mRWMutexIPData.mSharedData->mThreadIdWriter = kSysThreadIdInvalid;
- }
- else // Else we have a read lock...
- {
- EAT_ASSERT(mRWMutexIPData.mSharedData->mnReaders >= 1);
- const int nNewReaders = --mRWMutexIPData.mSharedData->mnReaders; // This is not an atomic operation. We are within a mutex lock.
- if(nNewReaders > 0)
- {
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
- ReleaseMutex(mRWMutexIPData.mMutex);
- return nNewReaders;
- }
- }
- if(mRWMutexIPData.mSharedData->mnWriteWaiters > 0) // We ignore the possibility that
- {
- ReleaseSemaphore(mRWMutexIPData.mWriteSemaphore, 1, NULL);
- // We rely on the released write waiter to decrement mnWriteWaiters.
- // If the released write waiter doesn't wake up for a while, it's possible that the ReleaseMutex below
- // will be called and another read unlocker will execute this code and release the semaphore again and
- // we will have two writers that are released. But this isn't a problem because the released writers
- // must still lock our mMutex and contend for the write lock, and one of the two will fail and go back
- // to waiting on the semaphore.
- }
- else if(mRWMutexIPData.mSharedData->mnReadWaiters > 0)
- {
- // I'm a little concerned about this signal here. We release mnReadWaiters, though it's possible
- // that a reader could timeout before this function completes and not all the semaphore count
- // will be claimed by waiters. However, the read wait code in the Lock function above does
- // seem to be able to handle this case, as it does do a check to make sure it can hold the read
- // lock before it claims it.
- ReleaseSemaphore(mRWMutexIPData.mReadSemaphore, mRWMutexIPData.mSharedData->mnReadWaiters, NULL);
- }
- //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1);
- ReleaseMutex(mRWMutexIPData.mMutex);
- return 0;
- }
- int EA::Thread::RWMutexIP::GetLockCount(LockType lockType)
- {
- if(lockType == kLockTypeRead)
- return mRWMutexIPData.mSharedData->mnReaders;
- else if((lockType == kLockTypeWrite) && (mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid))
- return 1;
- return 0;
- }
- #else
- EA::Thread::RWMutexIPParameters::RWMutexIPParameters(bool /*bIntraProcess*/, const char* /*pName*/)
- {
- }
- EA::Thread::RWMutexIP::RWMutexIP(const RWMutexIPParameters* /*pRWMutexIPParameters*/, bool /*bDefaultParameters*/)
- {
- }
-
-
- EA::Thread::RWMutexIP::~RWMutexIP()
- {
- }
-
- bool EA::Thread::RWMutexIP::Init(const RWMutexIPParameters* /*pRWMutexIPParameters*/)
- {
- return false;
- }
- int EA::Thread::RWMutexIP::Lock(LockType /*lockType*/, const ThreadTime& /*timeoutAbsolute*/)
- {
- return 0;
- }
- int EA::Thread::RWMutexIP::Unlock()
- {
- return 0;
- }
- int EA::Thread::RWMutexIP::GetLockCount(LockType /*lockType*/)
- {
- return 0;
- }
- #endif // EA_PLATFORM_XXX
- namespace EA
- {
- namespace Thread
- {
- extern Allocator* gpAllocator;
- }
- }
- EA::Thread::RWMutexIP* EA::Thread::RWMutexIPFactory::CreateRWMutexIP()
- {
- if(gpAllocator)
- return new(gpAllocator->Alloc(sizeof(EA::Thread::RWMutexIP))) EA::Thread::RWMutexIP;
- else
- return new EA::Thread::RWMutexIP;
- }
- void EA::Thread::RWMutexIPFactory::DestroyRWMutexIP(EA::Thread::RWMutexIP* pRWMutexIP)
- {
- if(gpAllocator)
- {
- pRWMutexIP->~RWMutexIP();
- gpAllocator->Free(pRWMutexIP);
- }
- else
- delete pRWMutexIP;
- }
- size_t EA::Thread::RWMutexIPFactory::GetRWMutexIPSize()
- {
- return sizeof(EA::Thread::RWMutexIP);
- }
- EA::Thread::RWMutexIP* EA::Thread::RWMutexIPFactory::ConstructRWMutexIP(void* pMemory)
- {
- return new(pMemory) EA::Thread::RWMutexIP;
- }
- void EA::Thread::RWMutexIPFactory::DestructRWMutexIP(EA::Thread::RWMutexIP* pRWMutexIP)
- {
- pRWMutexIP->~RWMutexIP();
- }
|