eathread_semaphore.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. /////////////////////////////////////////////////////////////////////////////
  5. // Implements a semaphore thread synchronization class.
  6. /////////////////////////////////////////////////////////////////////////////
  7. #ifndef EATHREAD_EATHREAD_SEMAPHORE_H
  8. #define EATHREAD_EATHREAD_SEMAPHORE_H
  9. #include <eathread/internal/config.h>
  10. #include <eathread/eathread.h>
  11. #if defined(EA_PRAGMA_ONCE_SUPPORTED)
  12. #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.
  13. #endif
  14. ///////////////////////////////////////////////////////////////////////////////
  15. // EATHREAD_USE_SYNTHESIZED_SEMAPHORE
  16. //
  17. // Defined as 0 or 1. Defined as 1 if the OS provides no native semaphore support.
  18. //
  19. #ifndef EATHREAD_USE_SYNTHESIZED_SEMAPHORE
  20. #define EATHREAD_USE_SYNTHESIZED_SEMAPHORE 0
  21. #endif
  22. ///////////////////////////////////////////////////////////////////////////////
  23. // EATHREAD_FAST_MS_SEMAPHORE_ENABLED
  24. //
  25. // Defined as 0 or 1.
  26. // Enables the usage of a faster intra-process semaphore on Microsoft platforms.
  27. // By faster we mean that it is typically 10x or more faster.
  28. // Has the downside that it is not interchangeable with the SEMAPHORE built-in
  29. // type and it's behaviour won't be strictly identical.
  30. // Even if this option is enabled, you can still get the built-in behaviour
  31. // of Microsoft semaphores by specifying the semaphore as inter-process.
  32. //
  33. #ifndef EATHREAD_FAST_MS_SEMAPHORE_ENABLED
  34. #define EATHREAD_FAST_MS_SEMAPHORE_ENABLED 1
  35. #endif
  36. /////////////////////////////////////////////////////////////////////////
  37. /// EASemaphoreData
  38. ///
  39. /// This is used internally by class Semaphore.
  40. /// Todo: Consider moving this declaration into a platform-specific
  41. /// header file.
  42. ///
  43. #if !EA_THREADS_AVAILABLE
  44. struct EASemaphoreData
  45. {
  46. volatile int mnCount;
  47. int mnMaxCount;
  48. EASemaphoreData();
  49. };
  50. #elif EATHREAD_USE_SYNTHESIZED_SEMAPHORE
  51. #include <eathread/eathread_condition.h>
  52. #include <eathread/eathread_mutex.h>
  53. #include <eathread/eathread_atomic.h>
  54. struct EASemaphoreData
  55. {
  56. EA::Thread::Condition mCV;
  57. EA::Thread::Mutex mMutex;
  58. EA::Thread::AtomicInt32 mnCount;
  59. int mnMaxCount;
  60. bool mbValid;
  61. EASemaphoreData();
  62. };
  63. #elif defined(EA_PLATFORM_APPLE)
  64. #include <mach/semaphore.h>
  65. #include <eathread/eathread_atomic.h>
  66. struct EASemaphoreData
  67. {
  68. semaphore_t mSemaphore;
  69. EA::Thread::AtomicInt32 mnCount;
  70. int mnMaxCount;
  71. bool mbIntraProcess;
  72. EASemaphoreData();
  73. };
  74. #elif defined(EA_PLATFORM_SONY)
  75. #include <kernel/semaphore.h>
  76. #include <eathread/eathread_atomic.h>
  77. #include <eathread/internal/timings.h>
  78. struct EASemaphoreData
  79. {
  80. SceKernelSema mSemaphore;
  81. int mnMaxCount;
  82. EA::Thread::AtomicInt32 mnCount;
  83. EASemaphoreData();
  84. };
  85. #elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
  86. #include <semaphore.h>
  87. #include <eathread/eathread_atomic.h>
  88. #if defined(EA_PLATFORM_WINDOWS)
  89. #ifdef CreateSemaphore
  90. #undef CreateSemaphore // Windows #defines CreateSemaphore to CreateSemaphoreA or CreateSemaphoreW.
  91. #endif
  92. #endif
  93. struct EASemaphoreData
  94. {
  95. sem_t mSemaphore;
  96. EA::Thread::AtomicInt32 mnCount;
  97. int mnMaxCount;
  98. bool mbIntraProcess;
  99. EASemaphoreData();
  100. };
  101. #elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE
  102. #ifdef CreateSemaphore
  103. #undef CreateSemaphore // Windows #defines CreateSemaphore to CreateSemaphoreA or CreateSemaphoreW.
  104. #endif
  105. struct EATHREADLIB_API EASemaphoreData
  106. {
  107. void* mhSemaphore; // We use void* instead of HANDLE in order to avoid #including windows.h. HANDLE is typedef'd to (void*) on all Windows-like platforms.
  108. int32_t mnCount; // Number of available posts. Under the fast semaphore pathway, a negative value means there are waiters.
  109. int32_t mnCancelCount; // Used by fast semaphore logic. Is the deferred cancel count.
  110. int32_t mnMaxCount; //
  111. bool mbIntraProcess; // Used under Windows, which can have multiple processes. Always true for XBox.
  112. EASemaphoreData();
  113. void UpdateCancelCount(int32_t n);
  114. };
  115. #endif
  116. /////////////////////////////////////////////////////////////////////////
  117. namespace EA
  118. {
  119. namespace Thread
  120. {
  121. /// SemaphoreParameters
  122. /// Specifies semaphore settings.
  123. struct EATHREADLIB_API SemaphoreParameters
  124. {
  125. int mInitialCount; /// Initial available count
  126. int mMaxCount; /// Max possible count. Defaults to INT_MAX.
  127. bool mbIntraProcess; /// True if the semaphore is intra-process, else inter-process.
  128. char mName[16]; /// Semaphore name, applicable only to platforms that recognize named synchronization objects.
  129. SemaphoreParameters(int initialCount = 0, bool bIntraProcess = true, const char* pName = NULL);
  130. };
  131. /// class Semaphore
  132. /// A semaphore is an object which has an associated count which is >= 0 and
  133. /// a value > 0 means that a thread can 'grab' the semaphore and decrement its
  134. /// value by one. A value of 0 means that threads must wait until another thread
  135. /// 'un-grabs' the semaphore. Thus a semaphore is like a car rental agency which
  136. /// has a limited number of cars for rent and if they are out of cars, you have
  137. /// to wait until one of the renters returns their car.
  138. class EATHREADLIB_API Semaphore
  139. {
  140. public:
  141. enum Result{
  142. kResultError = -1,
  143. kResultTimeout = -2
  144. };
  145. /// Semaphore
  146. /// For immediate default initialization, use no args.
  147. /// For custom immediate initialization, supply a first argument.
  148. /// For deferred initialization, use Semaphore(NULL, false) then later call Init.
  149. /// For deferred initialization of an array of objects, create an empty
  150. /// subclass whose default constructor chains back to Semaphore(NULL, false).
  151. Semaphore(const SemaphoreParameters* pSemaphoreParameters = NULL, bool bDefaultParameters = true);
  152. /// Semaphore
  153. /// This is a constructor which initializes the Semaphore to a specific count
  154. /// and intializes the other Semaphore parameters to default values. See the
  155. /// SemaphoreParameters struct for info on these default values.
  156. Semaphore(int initialCount);
  157. /// ~Semaphore
  158. /// Destroys an existing semaphore. The semaphore must not be locked
  159. /// by any thread, otherwise the resulting behaviour is undefined.
  160. ~Semaphore();
  161. /// Init
  162. /// Initializes the semaphore with given parameters.
  163. bool Init(const SemaphoreParameters* pSemaphoreParameters);
  164. /// Wait
  165. /// Locks the semaphore (reducing its count by one) or gives up trying to
  166. /// lock it after a given timeout has expired. If the semaphore count is > 0
  167. /// then the count will be reduced by one. If the semaphore count is 0, the
  168. /// call will block until another thread unlocks it or the timeout expires.
  169. ///
  170. /// Note that the timeout is specified in absolute time and not relative time.
  171. ///
  172. /// Note also that due to the way thread scheduling works -- particularly in a
  173. /// time-sliced threading environment -- that the timeout value is a hint and
  174. /// the actual amount of time passed before the timeout occurs may be significantly
  175. /// more or less than the specified timeout time.
  176. ///
  177. /// Return value:
  178. /// kResultError The semaphore could not be obtained due to error.
  179. /// kResultTimeout The semaphore could not be obtained due to timeout.
  180. /// >= 0 The new count for the semaphore.
  181. ///
  182. /// It's possible that two threads waiting on the same semaphore will return
  183. /// with a result of zero. Thus you cannot rely on the semaphore's return value
  184. /// to ascertain which was the last thread to return from the Wait.
  185. int Wait(const ThreadTime& timeoutAbsolute = kTimeoutNone);
  186. /// Post
  187. /// Increments the signalled value of the semaphore by the count.
  188. /// Returns the available count after the operation has completed.
  189. /// Returns kResultError upon error. A Wait is often eventually
  190. /// followed by a corresponding Post.
  191. /// For the case of count being greater than 1, not all platforms
  192. /// act the same. If count results in exceeding the max count then
  193. /// kResultError is returned. Some platforms return kResultError
  194. /// before any of account is applied, while others return
  195. /// kResultError after some of count has been applied.
  196. int Post(int count = 1);
  197. /// GetCount
  198. /// Returns current number of available locks associated with the semaphore.
  199. /// This is useful for debugging and for quick polling checks of the
  200. /// status of the semaphore. This value changes over time as multiple
  201. /// threads wait and post to the semaphore. This value cannot be trusted
  202. /// to exactly represent the count upon its return if multiple threads are
  203. /// using this Semaphore at the time.
  204. int GetCount() const;
  205. /// GetPlatformData
  206. /// Returns the platform-specific data handle for debugging uses or
  207. /// other cases whereby special (and non-portable) uses are required.
  208. void* GetPlatformData()
  209. { return &mSemaphoreData; }
  210. protected:
  211. EASemaphoreData mSemaphoreData;
  212. private:
  213. // Objects of this class are not copyable.
  214. Semaphore(const Semaphore&){}
  215. Semaphore& operator=(const Semaphore&){ return *this; }
  216. };
  217. /// SemaphoreFactory
  218. ///
  219. /// Implements a factory-based creation and destruction mechanism for class Semaphore.
  220. /// A primary use of this would be to allow the Semaphore implementation to reside in
  221. /// a private library while users of the class interact only with the interface
  222. /// header and the factory. The factory provides conventional create/destroy
  223. /// semantics which use global operator new, but also provides manual construction/
  224. /// destruction semantics so that the user can provide for memory allocation
  225. /// and deallocation.
  226. class EATHREADLIB_API SemaphoreFactory
  227. {
  228. public:
  229. static Semaphore* CreateSemaphore(); // Internally implemented as: return new Semaphore;
  230. static void DestroySemaphore(Semaphore* pSemaphore); // Internally implemented as: delete pSemaphore;
  231. static size_t GetSemaphoreSize(); // Internally implemented as: return sizeof(Semaphore);
  232. static Semaphore* ConstructSemaphore(void* pMemory); // Internally implemented as: return new(pMemory) Semaphore;
  233. static void DestructSemaphore(Semaphore* pSemaphore); // Internally implemented as: pSemaphore->~Semaphore();
  234. };
  235. } // namespace Thread
  236. } // namespace EA
  237. namespace EA
  238. {
  239. namespace Thread
  240. {
  241. /// class AutoSemaphore
  242. /// An AutoSemaphore grabs the Semaphore in its constructor and posts
  243. /// the Semaphore once in its destructor (when it goes out of scope).
  244. class EATHREADLIB_API AutoSemaphore
  245. {
  246. public:
  247. AutoSemaphore(Semaphore& semaphore)
  248. : mSemaphore(semaphore)
  249. { mSemaphore.Wait(); }
  250. ~AutoSemaphore()
  251. { mSemaphore.Post(1); }
  252. protected:
  253. Semaphore& mSemaphore;
  254. // Prevent copying by default, as copying is dangerous.
  255. AutoSemaphore(const AutoSemaphore&);
  256. const AutoSemaphore& operator=(const AutoSemaphore&);
  257. };
  258. } // namespace Thread
  259. } // namespace EA
  260. #endif // EATHREAD_EATHREAD_SEMAPHORE_H