eathread_rwmutex_ip.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. /////////////////////////////////////////////////////////////////////////////
  5. // Implements an interprocess mutex with multiple reads but single writer.
  6. // This allows for high performance systems whereby the consumers of mpData
  7. // are more common than the producers of mpData.
  8. /////////////////////////////////////////////////////////////////////////////
  9. #ifndef EATHREAD_EATHREAD_RWMUTEX_IP_H
  10. #define EATHREAD_EATHREAD_RWMUTEX_IP_H
  11. #include <EABase/eabase.h>
  12. #include <eathread/eathread.h>
  13. #include <new>
  14. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  15. #pragma warning(push, 0)
  16. #include <Windows.h>
  17. #pragma warning(pop)
  18. #endif
  19. #if defined(EA_PRAGMA_ONCE_SUPPORTED)
  20. #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.
  21. #endif
  22. #ifdef _MSC_VER
  23. #pragma warning(push) // We have to be careful about disabling this warning. Sometimes the warning is meaningful; sometimes it isn't.
  24. #pragma warning(disable: 4251) // class (some template) needs to have dll-interface to be used by clients.
  25. #pragma warning(disable: 6054) // String 'argument 2' might not be zero-terminated
  26. #endif
  27. namespace EA
  28. {
  29. namespace Thread
  30. {
  31. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  32. template<typename T>
  33. class Shared
  34. {
  35. public:
  36. Shared();
  37. Shared(const char* pName);
  38. ~Shared();
  39. bool Init(const char* pName);
  40. void Shutdown();
  41. bool IsNew() const { return mbCreated; }
  42. T* operator->() { return static_cast<T*>(mpData); }
  43. protected:
  44. uint32_t& GetRefCount();
  45. Shared(const Shared&);
  46. Shared& operator=(const Shared&);
  47. protected:
  48. HANDLE mMapping;
  49. void* mpData;
  50. bool mbCreated;
  51. char mName[32];
  52. T* mpT; // For debug purposes only.
  53. };
  54. template <typename T>
  55. inline Shared<T>::Shared()
  56. : mMapping(NULL)
  57. , mpData(NULL)
  58. , mbCreated(false)
  59. , mpT(NULL)
  60. {
  61. }
  62. template <typename T>
  63. inline Shared<T>::Shared(const char* pName)
  64. : mMapping(NULL)
  65. , mpData(NULL)
  66. , mbCreated(false)
  67. , mpT(NULL)
  68. {
  69. Init(pName);
  70. }
  71. template <typename T>
  72. inline Shared<T>::~Shared()
  73. {
  74. Shutdown();
  75. }
  76. template <typename T>
  77. inline bool Shared<T>::Init(const char* pName)
  78. {
  79. bool bReturnValue = false;
  80. if(pName)
  81. strncpy(mName, pName, sizeof(mName));
  82. else
  83. mName[0] = 0;
  84. mName[sizeof(mName) - 1] = 0;
  85. char mutexName[sizeof(mName) + 16];
  86. strcpy(mutexName, mName);
  87. strcat(mutexName, ".SharedMutex");
  88. HANDLE hMutex = CreateMutexA(NULL, FALSE, mutexName);
  89. EAT_ASSERT(hMutex != NULL);
  90. if(hMutex != NULL)
  91. {
  92. WaitForSingleObject(hMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily.
  93. const size_t kDataSize = sizeof(T) + 8; // Add bytes so that we can store a ref-count of our own after the mpData.
  94. mMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, kDataSize, mName);
  95. if(mMapping)
  96. {
  97. mbCreated = (GetLastError() != ERROR_ALREADY_EXISTS);
  98. mpData = MapViewOfFile(mMapping, FILE_MAP_ALL_ACCESS, 0, 0, kDataSize);
  99. uint32_t& refCount = GetRefCount(); // The ref count is stored at the end of the mapped data.
  100. if(mbCreated) // If we were the first one to create this, then construct it.
  101. {
  102. new(mpData) T;
  103. refCount = 1;
  104. }
  105. else
  106. refCount++;
  107. mpT = static_cast<T*>(mpData); // For debug purposes only.
  108. bReturnValue = true;
  109. }
  110. ReleaseMutex(hMutex);
  111. CloseHandle(hMutex);
  112. }
  113. return bReturnValue;
  114. }
  115. template <typename T>
  116. inline void Shared<T>::Shutdown()
  117. {
  118. char mutexName[sizeof(mName) + 16];
  119. strcpy(mutexName, mName);
  120. strcat(mutexName, ".SharedMutex");
  121. HANDLE hMutex = CreateMutexA(NULL, FALSE, mutexName);
  122. EAT_ASSERT(hMutex != NULL);
  123. if(hMutex != NULL)
  124. {
  125. WaitForSingleObject(hMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily.
  126. if(mMapping)
  127. {
  128. if(mpData)
  129. {
  130. uint32_t& refCount = GetRefCount(); // The ref count is stored at the end of the mapped data.
  131. if(refCount == 1) // If we are the last to use it,
  132. static_cast<T*>(mpData)->~T();
  133. else
  134. refCount--;
  135. UnmapViewOfFile(mpData);
  136. mpData = NULL;
  137. }
  138. CloseHandle(mMapping);
  139. mMapping = 0;
  140. }
  141. ReleaseMutex(hMutex);
  142. CloseHandle(hMutex);
  143. }
  144. }
  145. template <typename T>
  146. inline uint32_t& Shared<T>::GetRefCount()
  147. {
  148. // There will be space after T because we allocated it in Init.
  149. uint32_t* pData32 = (uint32_t*)(((uintptr_t)mpData + sizeof(T) + 3) & ~3); // Round up to next 32 bit boundary.
  150. return *pData32;
  151. }
  152. #else
  153. template<typename T>
  154. class Shared
  155. {
  156. public:
  157. Shared() { }
  158. Shared(const char*) { }
  159. bool Init(const char*) { return true; }
  160. void Shutdown() { }
  161. bool IsNew() const { return true; }
  162. T* operator->() { return &mT; }
  163. T mT;
  164. };
  165. #endif // #if defined(EA_PLATFORM_WINDOWS)
  166. } // namespace Thread
  167. } // namespace EA
  168. namespace EA
  169. {
  170. namespace Thread
  171. {
  172. /////////////////////////////////////////////////////////////////////////
  173. /// EARWMutexIPData
  174. ///
  175. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  176. struct EATHREADLIB_API SharedData
  177. {
  178. int mnReadWaiters;
  179. int mnWriteWaiters;
  180. int mnReaders;
  181. DWORD mThreadIdWriter; // Need to use a thread id instead of a thread handle.
  182. SharedData() : mnReadWaiters(0), mnWriteWaiters(0), mnReaders(0), mThreadIdWriter(EA::Thread::kSysThreadIdInvalid) { }
  183. };
  184. struct EATHREADLIB_API EARWMutexIPData
  185. {
  186. Shared<SharedData> mSharedData;
  187. HANDLE mMutex;
  188. HANDLE mReadSemaphore;
  189. HANDLE mWriteSemaphore;
  190. EARWMutexIPData();
  191. ~EARWMutexIPData();
  192. bool Init(const char* pName);
  193. void Shutdown();
  194. private:
  195. EARWMutexIPData(const EARWMutexIPData&);
  196. EARWMutexIPData& operator=(const EARWMutexIPData&);
  197. };
  198. #else
  199. struct EATHREADLIB_API EARWMutexIPData
  200. {
  201. EARWMutexIPData(){}
  202. private:
  203. EARWMutexIPData(const EARWMutexIPData&);
  204. EARWMutexIPData& operator=(const EARWMutexIPData&);
  205. };
  206. #endif
  207. /// RWMutexParameters
  208. struct EATHREADLIB_API RWMutexIPParameters
  209. {
  210. bool mbIntraProcess; /// True if the mutex is intra-process, else inter-process.
  211. char mName[16]; /// Mutex name, applicable only to platforms that recognize named synchronization objects.
  212. RWMutexIPParameters(bool bIntraProcess = true, const char* pName = NULL);
  213. };
  214. /// class RWMutexIP
  215. /// Implements an interprocess multiple reader / single writer mutex.
  216. /// This allows for significantly higher performance when mpData to be protected
  217. /// is read much more frequently than written. In this case, a waiting writer
  218. /// gets top priority and all new readers block after a waiter starts waiting.
  219. class EATHREADLIB_API RWMutexIP
  220. {
  221. public:
  222. enum Result
  223. {
  224. kResultError = -1,
  225. kResultTimeout = -2
  226. };
  227. enum LockType
  228. {
  229. kLockTypeNone = 0,
  230. kLockTypeRead = 1,
  231. kLockTypeWrite = 2
  232. };
  233. /// RWMutexIP
  234. /// For immediate default initialization, use no args.
  235. /// For custom immediate initialization, supply a first argument.
  236. /// For deferred initialization, use RWMutexIP(NULL, false) then later call Init.
  237. /// For deferred initialization of an array of objects, create an empty
  238. /// subclass whose default constructor chains back to RWMutexIP(NULL, false).
  239. RWMutexIP(const RWMutexIPParameters* pRWMutexIPParameters = NULL, bool bDefaultParameters = true);
  240. /// ~RWMutexIP
  241. /// Destroys an existing mutex. The mutex must not be locked by any thread,
  242. /// otherwise the resulting behaviour is undefined.
  243. ~RWMutexIP();
  244. /// Init
  245. /// Initializes the mutex if not done so in the constructor.
  246. /// This should only be called in the case that this class was constructed
  247. /// with RWMutexIP(NULL, false).
  248. bool Init(const RWMutexIPParameters* pRWMutexIPParameters);
  249. /// Lock
  250. /// Returns the new lock count for the given lock type.
  251. ///
  252. /// Note that the timeout is specified in absolute time and not relative time.
  253. ///
  254. /// Note also that due to the way thread scheduling works -- particularly in a
  255. /// time-sliced threading environment -- that the timeout value is a hint and
  256. /// the actual amount of time passed before the timeout occurs may be significantly
  257. /// more or less than the specified timeout time.
  258. ///
  259. int Lock(LockType lockType, const ThreadTime& timeoutAbsolute = EA::Thread::kTimeoutNone);
  260. /// Unlock
  261. /// Unlocks the mutex. The mutex must already be locked by the
  262. /// calling thread. Otherwise the behaviour is not defined.
  263. /// Return value is the lock count value immediately upon unlock
  264. /// or is one of enum Result.
  265. int Unlock();
  266. /// GetLockCount
  267. int GetLockCount(LockType lockType);
  268. /// GetPlatformData
  269. /// Returns the platform-specific mpData handle for debugging uses or
  270. /// other cases whereby special (and non-portable) uses are required.
  271. void* GetPlatformData()
  272. { return &mRWMutexIPData; }
  273. protected:
  274. EARWMutexIPData mRWMutexIPData;
  275. private:
  276. // Objects of this class are not copyable.
  277. RWMutexIP(const RWMutexIP&){}
  278. RWMutexIP& operator=(const RWMutexIP&){ return *this; }
  279. };
  280. /// RWMutexIPFactory
  281. ///
  282. /// Implements a factory-based creation and destruction mechanism for class RWMutexIP.
  283. /// A primary use of this would be to allow the RWMutexIP implementation to reside in
  284. /// a private library while users of the class interact only with the interface
  285. /// header and the factory. The factory provides conventional create/destroy
  286. /// semantics which use global operator new, but also provides manual construction/
  287. /// destruction semantics so that the user can provide for memory allocation
  288. /// and deallocation.
  289. class EATHREADLIB_API RWMutexIPFactory
  290. {
  291. public:
  292. static RWMutexIP* CreateRWMutexIP(); // Internally implemented as: return new RWMutexIP;
  293. static void DestroyRWMutexIP(RWMutexIP* pRWMutex); // Internally implemented as: delete pRWMutex;
  294. static size_t GetRWMutexIPSize(); // Internally implemented as: return sizeof(RWMutexIP);
  295. static RWMutexIP* ConstructRWMutexIP(void* pMemory); // Internally implemented as: return new(pMemory) RWMutexIP;
  296. static void DestructRWMutexIP(RWMutexIP* pRWMutex); // Internally implemented as: pRWMutex->~RWMutexIP();
  297. };
  298. } // namespace Thread
  299. } // namespace EA
  300. namespace EA
  301. {
  302. namespace Thread
  303. {
  304. /// class AutoRWMutexIP
  305. /// An AutoRWMutex locks the RWMutexIP in its constructor and
  306. /// unlocks the AutoRWMutex in its destructor (when it goes out of scope).
  307. class AutoRWMutexIP
  308. {
  309. public:
  310. AutoRWMutexIP(RWMutexIP& mutex, RWMutexIP::LockType lockType)
  311. : mMutex(mutex)
  312. { mMutex.Lock(lockType); }
  313. ~AutoRWMutexIP()
  314. { mMutex.Unlock(); }
  315. protected:
  316. RWMutexIP& mMutex;
  317. // Prevent copying by default, as copying is dangerous.
  318. AutoRWMutexIP(const AutoRWMutexIP&);
  319. const AutoRWMutexIP& operator=(const AutoRWMutexIP&);
  320. };
  321. } // namespace Thread
  322. } // namespace EA
  323. #ifdef _MSC_VER
  324. #pragma warning(pop)
  325. #endif
  326. #endif // EATHREAD_EATHREAD_RWMUTEX_IP_H