eathread_condition.h 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. /////////////////////////////////////////////////////////////////////////////
  5. // Implements a condition variable in the style of Posix condition variables
  6. // and Java and C# thread Monitors (Java objects and C# monitors have built-in
  7. // locks and pthreads condition variables and EAThread::Conditions and Posix
  8. // condition variables do not. A Condition is usually the appropriate thread
  9. // synchronization mechanism for producer/consumer situations whereby one
  10. // or more threads create data for one or more other threads to work on,
  11. // such as is the case with a message queue.
  12. /////////////////////////////////////////////////////////////////////////////
  13. #ifndef EATHREAD_EATHREAD_CONDITION_H
  14. #define EATHREAD_EATHREAD_CONDITION_H
  15. #include <EABase/eabase.h>
  16. #include <eathread/eathread.h>
  17. #include <eathread/eathread_mutex.h>
  18. #if defined(EA_DLL) && defined(_MSC_VER)
  19. // Suppress warning about class 'EA::Thread::simple_list<T>' needs to have
  20. // dll-interface to be used by clients of class which have a templated member.
  21. //
  22. // These templates cannot be instantiated outside of the DLL. If you try, a
  23. // link error will result. This compiler warning is intended to notify users
  24. // of this.
  25. #pragma warning(push)
  26. #pragma warning(disable: 4251)
  27. #endif
  28. #if defined(EA_PRAGMA_ONCE_SUPPORTED)
  29. #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.
  30. #endif
  31. /////////////////////////////////////////////////////////////////////////
  32. /// EAConditionData
  33. ///
  34. /// This is used internally by class Condition.
  35. /// Todo: Consider moving this declaration into a platform-specific
  36. /// header file.
  37. ///
  38. #if defined(EA_PLATFORM_SONY)
  39. // Condition variables are built into Posix/Unix.
  40. #include <kernel.h>
  41. #include <eathread/internal/timings.h>
  42. struct EAConditionData
  43. {
  44. ScePthreadCond mCV;
  45. EAConditionData();
  46. };
  47. #elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE
  48. // Condition variables are built into Posix/Unix.
  49. #include <pthread.h>
  50. struct EAConditionData
  51. {
  52. pthread_cond_t mCV;
  53. EAConditionData();
  54. };
  55. #else // All other platforms
  56. #include <eathread/eathread_semaphore.h>
  57. #include <eathread/eathread_atomic.h>
  58. struct EATHREADLIB_API EAConditionData
  59. {
  60. EA::Thread::AtomicInt32 mnWaitersBlocked;
  61. int mnWaitersToUnblock;
  62. int mnWaitersDone;
  63. EA::Thread::Semaphore mSemaphoreBlockQueue;
  64. EA::Thread::Semaphore mSemaphoreBlockLock;
  65. EA::Thread::Mutex mUnblockLock;
  66. EAConditionData();
  67. private:
  68. // Prevent default generation of these functions by declaring but not defining them.
  69. EAConditionData(const EAConditionData& rhs); // copy constructor
  70. EAConditionData& operator=(const EAConditionData& rhs); // assignment operator
  71. };
  72. #endif
  73. namespace EA
  74. {
  75. namespace Thread
  76. {
  77. #if defined(EA_PLATFORM_SONY)
  78. static const int CONDITION_VARIABLE_NAME_LENGTH_MAX = 31;
  79. #else
  80. static const int CONDITION_VARIABLE_NAME_LENGTH_MAX = 15;
  81. #endif
  82. /// ConditionParameters
  83. /// Specifies condition variable settings.
  84. struct EATHREADLIB_API ConditionParameters
  85. {
  86. bool mbIntraProcess; /// True if the Condition is intra-process, else inter-process.
  87. char mName[CONDITION_VARIABLE_NAME_LENGTH_MAX + 1]; /// Condition name, applicable only to platforms that recognize named synchronization objects.
  88. ConditionParameters(bool bIntraProcess = true, const char* pName = NULL);
  89. };
  90. /// Condition
  91. /// Implements a condition variable thread synchronization primitive. A condition variable is usually the
  92. /// appropriate thread synchronization mechanism for producer/consumer situations whereby one or more
  93. /// threads create data for one or more other threads to work on, such as is the case with a message queue.
  94. ///
  95. /// To use a condition variable to wait for resource, you Lock the Mutex for that resource, then (in a loop)
  96. /// check and Wait on a condition variable that you associate with the mutex. Upon calling Wait,
  97. /// the Lock will be released so that other threads can adjust the resource. Upon return from Wait,
  98. /// the Mutex is re-locked for the caller. To use a Condition to signal a change in something, you simply
  99. /// call the Signal function. In the case of Signal(false), one blocking waiter will be released,
  100. /// whereas with Signal(true), all blocking waiters will be released. Upon release of single or multiple
  101. /// waiting threads, the Lock is contested for by all of them, so in the case or more than one waiter,
  102. /// only one will immediately come away with ownership of the lock.
  103. class EATHREADLIB_API Condition
  104. {
  105. public:
  106. enum Result
  107. {
  108. kResultOK = 0,
  109. kResultError = -1,
  110. kResultTimeout = -2
  111. };
  112. /// Condition
  113. /// For immediate default initialization, use no args.
  114. /// For custom immediate initialization, supply a first argument.
  115. /// For deferred initialization, use Condition(NULL, false) then later call Init.
  116. /// For deferred initialization of an array of objects, create an empty
  117. /// subclass whose default constructor chains back to Condition(NULL, false).
  118. Condition(const ConditionParameters* pConditionParameters = NULL, bool bDefaultParameters = true);
  119. /// ~Condition
  120. /// Destroys the Condition object. If any threads that are blocking while waiting on
  121. /// while the Condition is destroyed, the resulting behaviour is undefined.
  122. ~Condition();
  123. /// Init
  124. /// Initializes the Condition.
  125. bool Init(const ConditionParameters* pConditionParameters);
  126. /// Wait
  127. /// Waits for the Condition with timeout. You must have a Mutex
  128. /// (that you conceptually associate with the resource) locked before
  129. /// calling this function or else the resulting behaviour is undefined.
  130. /// Within a while loop, check the resource state and call Wait if the
  131. /// necessary condition is not met.
  132. ///
  133. /// The call to Wait associates the Condition with your mutex, so it can
  134. /// then unlock the mutex/resource (allows another thread to fill the resource).
  135. ///
  136. /// Upon non-error return of Wait, the mutex will be re-locked by the calling
  137. /// thread, even if the result is a timeout. Upon returning from wait, before
  138. /// doing any processing as a result of a Signal, your loop should always re-check
  139. /// the resource state. The Posix Wait specification explicitly notes
  140. /// that uncommon 'spurious wakeups' are possible and so should be tested
  141. /// for. It impossible to test for a spurious wakeup from within this Wait
  142. /// function, as this function can't know the resource state that caused the
  143. /// Signal to occur.
  144. ///
  145. /// It should be noted that upon a kResultOK return from Wait, the user should
  146. /// not assume that what the user was waiting on is still available. The signaling
  147. /// of a Condition should be considered merely a hint to the waiter that the user
  148. /// can probably proceed. Also, the user should usually call Wait only if the
  149. /// user has nothing to wait for; the user should check for this before calling Wait.
  150. ///
  151. /// Note that the timeout is specified in absolute time and not relative time.
  152. ///
  153. /// Note also that due to the way thread scheduling works -- particularly in a
  154. /// time-sliced threading environment -- that the timeout value is a hint and
  155. /// the actual amount of time passed before the timeout occurs may be significantly
  156. /// more or less than the specified timeout time.
  157. ///
  158. Result Wait(Mutex* pMutex, const ThreadTime& timeoutAbsolute = kTimeoutNone);
  159. /// Signal
  160. /// Releases one or all waiters, depending on the input 'bBroadcast' argument.
  161. /// The waiters will then contest for the Lock.
  162. bool Signal(bool bBroadcast = false);
  163. /// GetPlatformData
  164. /// Returns the platform-specific data handle for debugging uses or
  165. /// other cases whereby special (and non-portable) uses are required.
  166. void* GetPlatformData()
  167. { return &mConditionData; }
  168. protected:
  169. EAConditionData mConditionData;
  170. private:
  171. // Objects of this class are not copyable.
  172. Condition(const Condition&){}
  173. Condition& operator=(const Condition&){ return *this; }
  174. };
  175. /// ConditionFactory
  176. ///
  177. /// Implements a factory-based creation and destruction mechanism for class Condition.
  178. /// A primary use of this would be to allow the Condition implementation to reside in
  179. /// a private library while users of the class interact only with the interface
  180. /// header and the factory. The factory provides conventional create/destroy
  181. /// semantics which use global operator new, but also provides manual construction/
  182. /// destruction semantics so that the user can provide for memory allocation
  183. /// and deallocation.
  184. class EATHREADLIB_API ConditionFactory
  185. {
  186. public:
  187. static Condition* CreateCondition(); // Internally implemented as: return new Condition;
  188. static void DestroyCondition(Condition* pCondition); // Internally implemented as: delete pCondition;
  189. static size_t GetConditionSize(); // Internally implemented as: return sizeof(Condition);
  190. static Condition* ConstructCondition(void* pMemory); // Internally implemented as: return new(pMemory) Condition;
  191. static void DestructCondition(Condition* pCondition); // Internally implemented as: pCondition->~Condition();
  192. };
  193. } // namespace Thread
  194. } // namespace EA
  195. #if defined(EA_DLL) && defined(_MSC_VER)
  196. // re-enable warning 4251 (it's a level-1 warning and should not be suppressed globally)
  197. #pragma warning(pop)
  198. #endif
  199. #endif // EATHREAD_EATHREAD_CONDITION_H