| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////
- // Implements an interprocess mutex with multiple reads but single writer.
- // This allows for high performance systems whereby the consumers of mpData
- // are more common than the producers of mpData.
- /////////////////////////////////////////////////////////////////////////////
- #ifndef EATHREAD_EATHREAD_RWMUTEX_IP_H
- #define EATHREAD_EATHREAD_RWMUTEX_IP_H
- #include <EABase/eabase.h>
- #include <eathread/eathread.h>
- #include <new>
- #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- #pragma warning(push, 0)
- #include <Windows.h>
- #pragma warning(pop)
- #endif
- #if defined(EA_PRAGMA_ONCE_SUPPORTED)
- #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
- #endif
- #ifdef _MSC_VER
- #pragma warning(push) // We have to be careful about disabling this warning. Sometimes the warning is meaningful; sometimes it isn't.
- #pragma warning(disable: 4251) // class (some template) needs to have dll-interface to be used by clients.
- #pragma warning(disable: 6054) // String 'argument 2' might not be zero-terminated
- #endif
- namespace EA
- {
- namespace Thread
- {
- #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- template<typename T>
- class Shared
- {
- public:
- Shared();
- Shared(const char* pName);
- ~Shared();
- bool Init(const char* pName);
- void Shutdown();
- bool IsNew() const { return mbCreated; }
- T* operator->() { return static_cast<T*>(mpData); }
- protected:
- uint32_t& GetRefCount();
- Shared(const Shared&);
- Shared& operator=(const Shared&);
- protected:
- HANDLE mMapping;
- void* mpData;
- bool mbCreated;
- char mName[32];
- T* mpT; // For debug purposes only.
- };
- template <typename T>
- inline Shared<T>::Shared()
- : mMapping(NULL)
- , mpData(NULL)
- , mbCreated(false)
- , mpT(NULL)
- {
- }
- template <typename T>
- inline Shared<T>::Shared(const char* pName)
- : mMapping(NULL)
- , mpData(NULL)
- , mbCreated(false)
- , mpT(NULL)
- {
- Init(pName);
- }
- template <typename T>
- inline Shared<T>::~Shared()
- {
- Shutdown();
- }
- template <typename T>
- inline bool Shared<T>::Init(const char* pName)
- {
- bool bReturnValue = false;
- if(pName)
- strncpy(mName, pName, sizeof(mName));
- else
- mName[0] = 0;
- mName[sizeof(mName) - 1] = 0;
-
- char mutexName[sizeof(mName) + 16];
- strcpy(mutexName, mName);
- strcat(mutexName, ".SharedMutex");
- HANDLE hMutex = CreateMutexA(NULL, FALSE, mutexName);
- EAT_ASSERT(hMutex != NULL);
- if(hMutex != NULL)
- {
- WaitForSingleObject(hMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily.
- const size_t kDataSize = sizeof(T) + 8; // Add bytes so that we can store a ref-count of our own after the mpData.
- mMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, kDataSize, mName);
- if(mMapping)
- {
- mbCreated = (GetLastError() != ERROR_ALREADY_EXISTS);
- mpData = MapViewOfFile(mMapping, FILE_MAP_ALL_ACCESS, 0, 0, kDataSize);
- uint32_t& refCount = GetRefCount(); // The ref count is stored at the end of the mapped data.
- if(mbCreated) // If we were the first one to create this, then construct it.
- {
- new(mpData) T;
- refCount = 1;
- }
- else
- refCount++;
- mpT = static_cast<T*>(mpData); // For debug purposes only.
- bReturnValue = true;
- }
- ReleaseMutex(hMutex);
- CloseHandle(hMutex);
- }
- return bReturnValue;
- }
- template <typename T>
- inline void Shared<T>::Shutdown()
- {
- char mutexName[sizeof(mName) + 16];
- strcpy(mutexName, mName);
- strcat(mutexName, ".SharedMutex");
- HANDLE hMutex = CreateMutexA(NULL, FALSE, mutexName);
- EAT_ASSERT(hMutex != NULL);
- if(hMutex != NULL)
- {
- WaitForSingleObject(hMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily.
- if(mMapping)
- {
- if(mpData)
- {
- uint32_t& refCount = GetRefCount(); // The ref count is stored at the end of the mapped data.
- if(refCount == 1) // If we are the last to use it,
- static_cast<T*>(mpData)->~T();
- else
- refCount--;
- UnmapViewOfFile(mpData);
- mpData = NULL;
- }
- CloseHandle(mMapping);
- mMapping = 0;
- }
- ReleaseMutex(hMutex);
- CloseHandle(hMutex);
- }
- }
- template <typename T>
- inline uint32_t& Shared<T>::GetRefCount()
- {
- // There will be space after T because we allocated it in Init.
- uint32_t* pData32 = (uint32_t*)(((uintptr_t)mpData + sizeof(T) + 3) & ~3); // Round up to next 32 bit boundary.
- return *pData32;
- }
- #else
- template<typename T>
- class Shared
- {
- public:
- Shared() { }
- Shared(const char*) { }
- bool Init(const char*) { return true; }
- void Shutdown() { }
- bool IsNew() const { return true; }
- T* operator->() { return &mT; }
- T mT;
- };
- #endif // #if defined(EA_PLATFORM_WINDOWS)
- } // namespace Thread
- } // namespace EA
- namespace EA
- {
- namespace Thread
- {
- /////////////////////////////////////////////////////////////////////////
- /// EARWMutexIPData
- ///
- #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- struct EATHREADLIB_API SharedData
- {
- int mnReadWaiters;
- int mnWriteWaiters;
- int mnReaders;
- DWORD mThreadIdWriter; // Need to use a thread id instead of a thread handle.
- SharedData() : mnReadWaiters(0), mnWriteWaiters(0), mnReaders(0), mThreadIdWriter(EA::Thread::kSysThreadIdInvalid) { }
- };
- struct EATHREADLIB_API EARWMutexIPData
- {
- Shared<SharedData> mSharedData;
- HANDLE mMutex;
- HANDLE mReadSemaphore;
- HANDLE mWriteSemaphore;
- EARWMutexIPData();
- ~EARWMutexIPData();
- bool Init(const char* pName);
- void Shutdown();
- private:
- EARWMutexIPData(const EARWMutexIPData&);
- EARWMutexIPData& operator=(const EARWMutexIPData&);
- };
- #else
- struct EATHREADLIB_API EARWMutexIPData
- {
- EARWMutexIPData(){}
- private:
- EARWMutexIPData(const EARWMutexIPData&);
- EARWMutexIPData& operator=(const EARWMutexIPData&);
- };
- #endif
- /// RWMutexParameters
- struct EATHREADLIB_API RWMutexIPParameters
- {
- bool mbIntraProcess; /// True if the mutex is intra-process, else inter-process.
- char mName[16]; /// Mutex name, applicable only to platforms that recognize named synchronization objects.
- RWMutexIPParameters(bool bIntraProcess = true, const char* pName = NULL);
- };
- /// class RWMutexIP
- /// Implements an interprocess multiple reader / single writer mutex.
- /// This allows for significantly higher performance when mpData to be protected
- /// is read much more frequently than written. In this case, a waiting writer
- /// gets top priority and all new readers block after a waiter starts waiting.
- class EATHREADLIB_API RWMutexIP
- {
- public:
- enum Result
- {
- kResultError = -1,
- kResultTimeout = -2
- };
- enum LockType
- {
- kLockTypeNone = 0,
- kLockTypeRead = 1,
- kLockTypeWrite = 2
- };
- /// RWMutexIP
- /// For immediate default initialization, use no args.
- /// For custom immediate initialization, supply a first argument.
- /// For deferred initialization, use RWMutexIP(NULL, false) then later call Init.
- /// For deferred initialization of an array of objects, create an empty
- /// subclass whose default constructor chains back to RWMutexIP(NULL, false).
- RWMutexIP(const RWMutexIPParameters* pRWMutexIPParameters = NULL, bool bDefaultParameters = true);
- /// ~RWMutexIP
- /// Destroys an existing mutex. The mutex must not be locked by any thread,
- /// otherwise the resulting behaviour is undefined.
- ~RWMutexIP();
- /// Init
- /// Initializes the mutex if not done so in the constructor.
- /// This should only be called in the case that this class was constructed
- /// with RWMutexIP(NULL, false).
- bool Init(const RWMutexIPParameters* pRWMutexIPParameters);
- /// Lock
- /// Returns the new lock count for the given lock type.
- ///
- /// Note that the timeout is specified in absolute time and not relative time.
- ///
- /// Note also that due to the way thread scheduling works -- particularly in a
- /// time-sliced threading environment -- that the timeout value is a hint and
- /// the actual amount of time passed before the timeout occurs may be significantly
- /// more or less than the specified timeout time.
- ///
- int Lock(LockType lockType, const ThreadTime& timeoutAbsolute = EA::Thread::kTimeoutNone);
- /// Unlock
- /// Unlocks the mutex. The mutex must already be locked by the
- /// calling thread. Otherwise the behaviour is not defined.
- /// Return value is the lock count value immediately upon unlock
- /// or is one of enum Result.
- int Unlock();
- /// GetLockCount
- int GetLockCount(LockType lockType);
- /// GetPlatformData
- /// Returns the platform-specific mpData handle for debugging uses or
- /// other cases whereby special (and non-portable) uses are required.
- void* GetPlatformData()
- { return &mRWMutexIPData; }
- protected:
- EARWMutexIPData mRWMutexIPData;
- private:
- // Objects of this class are not copyable.
- RWMutexIP(const RWMutexIP&){}
- RWMutexIP& operator=(const RWMutexIP&){ return *this; }
- };
- /// RWMutexIPFactory
- ///
- /// Implements a factory-based creation and destruction mechanism for class RWMutexIP.
- /// A primary use of this would be to allow the RWMutexIP implementation to reside in
- /// a private library while users of the class interact only with the interface
- /// header and the factory. The factory provides conventional create/destroy
- /// semantics which use global operator new, but also provides manual construction/
- /// destruction semantics so that the user can provide for memory allocation
- /// and deallocation.
- class EATHREADLIB_API RWMutexIPFactory
- {
- public:
- static RWMutexIP* CreateRWMutexIP(); // Internally implemented as: return new RWMutexIP;
- static void DestroyRWMutexIP(RWMutexIP* pRWMutex); // Internally implemented as: delete pRWMutex;
- static size_t GetRWMutexIPSize(); // Internally implemented as: return sizeof(RWMutexIP);
- static RWMutexIP* ConstructRWMutexIP(void* pMemory); // Internally implemented as: return new(pMemory) RWMutexIP;
- static void DestructRWMutexIP(RWMutexIP* pRWMutex); // Internally implemented as: pRWMutex->~RWMutexIP();
- };
- } // namespace Thread
- } // namespace EA
- namespace EA
- {
- namespace Thread
- {
- /// class AutoRWMutexIP
- /// An AutoRWMutex locks the RWMutexIP in its constructor and
- /// unlocks the AutoRWMutex in its destructor (when it goes out of scope).
- class AutoRWMutexIP
- {
- public:
- AutoRWMutexIP(RWMutexIP& mutex, RWMutexIP::LockType lockType)
- : mMutex(mutex)
- { mMutex.Lock(lockType); }
- ~AutoRWMutexIP()
- { mMutex.Unlock(); }
- protected:
- RWMutexIP& mMutex;
- // Prevent copying by default, as copying is dangerous.
- AutoRWMutexIP(const AutoRWMutexIP&);
- const AutoRWMutexIP& operator=(const AutoRWMutexIP&);
- };
- } // namespace Thread
- } // namespace EA
- #ifdef _MSC_VER
- #pragma warning(pop)
- #endif
- #endif // EATHREAD_EATHREAD_RWMUTEX_IP_H
|