eathread_pool.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. /////////////////////////////////////////////////////////////////////////////
  5. // Implements a classic thread pool.
  6. /////////////////////////////////////////////////////////////////////////////
  7. #ifndef EATHREAD_EATHREAD_POOL_H
  8. #define EATHREAD_EATHREAD_POOL_H
  9. #ifndef EATHREAD_EATHREAD_THREAD_H
  10. #include <eathread/eathread_thread.h>
  11. #endif
  12. #ifndef EATHREAD_EATHREAD_CONDITION_H
  13. #include <eathread/eathread_condition.h>
  14. #endif
  15. #ifndef EATHREAD_EATHREAD_ATOMIC_H
  16. #include <eathread/eathread_atomic.h>
  17. #endif
  18. #ifndef EATHREAD_EATHREAD_LIST_H
  19. #include <eathread/eathread_list.h>
  20. #endif
  21. #include <stddef.h>
  22. #if defined(EA_DLL) && defined(EA_COMPILER_MSVC)
  23. // Suppress warning about class 'EA::Thread::simple_list<T>' needs to have
  24. // dll-interface to be used by clients of class which have a templated member.
  25. //
  26. // These templates cannot be instantiated outside of the DLL. If you try, a
  27. // link error will result. This compiler warning is intended to notify users
  28. // of this.
  29. EA_DISABLE_VC_WARNING(4251)
  30. #endif
  31. #if defined(EA_PRAGMA_ONCE_SUPPORTED)
  32. #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.
  33. #endif
  34. /////////////////////////////////////////////////////////////////////////////
  35. // EA_THREAD_POOL_MAX_SIZE
  36. //
  37. // Defines the maximum number of threads the pool can have.
  38. // Currently we have a limit of at most N threads in a pool, in order to
  39. // simplify memory management issues.
  40. //
  41. #ifndef EA_THREAD_POOL_MAX_SIZE
  42. #define EA_THREAD_POOL_MAX_SIZE 16
  43. #endif
  44. namespace EA
  45. {
  46. namespace Thread
  47. {
  48. /// ThreadPoolParameters
  49. /// Specifies how a thread pool is initialized
  50. struct EATHREADLIB_API ThreadPoolParameters
  51. {
  52. unsigned mnMinCount; /// Default is kDefaultMinCount.
  53. unsigned mnMaxCount; /// Default is kDefaultMaxCount.
  54. unsigned mnInitialCount; /// Default is kDefaultInitialCount
  55. ThreadTime mnIdleTimeoutMilliseconds; /// Default is kDefaultIdleTimeout. This is a relative time, not an absolute time. Can be a millisecond value or Thread::kTimeoutNone or Thread::kTimeoutImmediate.
  56. unsigned mnProcessorMask; /// Default is 0xffffffff. Controls which processors we are allowed to create threads on. Default is all processors.
  57. ThreadParameters mDefaultThreadParameters; /// Currently only the mnStackSize, mnPriority, and mpName fields from ThreadParameters are used.
  58. ThreadPoolParameters();
  59. private:
  60. // Prevent default generation of these functions by not defining them
  61. ThreadPoolParameters(const ThreadPoolParameters& rhs); // copy constructor
  62. ThreadPoolParameters& operator=(const ThreadPoolParameters& rhs); // assignment operator
  63. };
  64. /// class ThreadPool
  65. ///
  66. /// Implements a conventional thread pool. Thread pools are useful for situations where
  67. /// thread creation and destruction is common and the application speed would improve
  68. /// by using pre-made threads that are ready to execute.
  69. class EATHREADLIB_API ThreadPool
  70. {
  71. public:
  72. enum Default
  73. {
  74. kDefaultMinCount = 0,
  75. kDefaultMaxCount = 4,
  76. kDefaultInitialCount = 0,
  77. kDefaultIdleTimeout = 60000, // Milliseconds
  78. kDefaultProcessorMask = 0xffffffff
  79. };
  80. enum Result
  81. {
  82. kResultOK = 0,
  83. kResultError = -1,
  84. kResultTimeout = -2,
  85. kResultDeferred = -3
  86. };
  87. enum JobWait
  88. {
  89. kJobWaitNone, /// Wait for no jobs to complete, including those currently running.
  90. kJobWaitCurrent, /// Wait for currently proceeding jobs to complete but not those that haven't started.
  91. kJobWaitAll /// Wait for all jobs to complete, including those that haven't yet begun.
  92. };
  93. /// ThreadPool
  94. /// For immediate default initialization, use no args.
  95. /// For custom immediate initialization, supply a first argument.
  96. /// For deferred initialization, use ThreadPool(NULL, false) then later call Init.
  97. /// For deferred initialization of an array of objects, create an empty
  98. /// subclass whose default constructor chains back to ThreadPool(NULL, false).
  99. ThreadPool(const ThreadPoolParameters* pThreadPoolParameters = NULL, bool bDefaultParameters = true);
  100. /// ~ThreadPool
  101. /// Destroys the thread pool. Waits for any busy threads to complete.
  102. ~ThreadPool();
  103. /// Init
  104. /// Initializes the thread pool with given characteristics. If the thread pool is
  105. /// already initialized, this updates the settings.
  106. bool Init(const ThreadPoolParameters* pThreadPoolParameters);
  107. /// Shutdown
  108. /// Disables the thread pool, waits for busy threads to complete, destroys all threads.
  109. ///
  110. /// If bWaitForAllJobs is true, then Shutdown will wait until all jobs, including
  111. /// jobs that haven't been started yet, to complete. Otherwise, only currently
  112. /// proceeding jobs will be completed.
  113. ///
  114. /// Note that the timeout is specified in absolute time and not relative time.
  115. ///
  116. /// Note also that due to the way thread scheduling works -- particularly in a
  117. /// time-sliced threading environment -- that the timeout value is a hint and
  118. /// the actual amount of time passed before the timeout occurs may be significantly
  119. /// more or less than the specified timeout time.
  120. ///
  121. bool Shutdown(JobWait jobWait = kJobWaitAll, const ThreadTime& timeoutAbsolute = kTimeoutNone);
  122. /// Begin
  123. /// Starts a thread from the pool with the given parameters.
  124. /// Returns kResultError or a job id of >= kResultOK. A return of kResultDeferred is
  125. /// possible if the number of active threads is greater or equal to the max count.
  126. /// If input ppThread is non-NULL and return value is >= kResultOK, the returned thread
  127. /// will be the thread used for the job. Else the returned thread pointer will be NULL.
  128. /// If input bEnabledDeferred is false but the max count of active theads has been
  129. /// reached, a new thread is nevertheless created.
  130. int Begin(IRunnable* pRunnable, void* pContext = NULL, Thread** ppThread = NULL, bool bEnableDeferred = false);
  131. int Begin(RunnableFunction pFunction, void* pContext = NULL, Thread** ppThread = NULL, bool bEnableDeferred = false);
  132. /// WaitForJobCompletion
  133. /// Waits for an individual job or for all jobs (job id of -1) to complete.
  134. /// If a job id is given which doesn't correspond to any existing job,
  135. /// the job is assumed to have been completed and the wait completes immediately.
  136. /// If new jobs are added while the wait is occurring, this function will wait
  137. /// for those jobs to complete as well. jobWait is valid only if nJob is -1.
  138. /// Note that the timeout is specified in absolute time and not relative time.
  139. /// Returns one of enum Result.
  140. int WaitForJobCompletion(int nJob = -1, JobWait jobWait = kJobWaitAll, const ThreadTime& timeoutAbsolute = kTimeoutNone);
  141. /// Pause
  142. /// Enables or disables the activation of threads from the pool.
  143. /// When paused, calls to Begin will return kResultDeferred instead of kResultOK.
  144. void Pause(bool bPause);
  145. /// Locks the thread pool thread list.
  146. void Lock();
  147. void Unlock();
  148. struct Job
  149. {
  150. int mnJobID; /// Unique job id.
  151. IRunnable* mpRunnable; /// User-supplied IRunnable. This is an alternative to mpFunction.
  152. RunnableFunction mpFunction; /// User-supplied function. This is an alternative to mpRunnable.
  153. void* mpContext; /// User-supplied context.
  154. Job();
  155. };
  156. struct ThreadInfo
  157. {
  158. volatile bool mbActive; /// True if the thread is currently busy working on a job.
  159. volatile bool mbQuit; /// If set to true then this thread should quit at the next opportunity.
  160. //bool mbPersistent; /// If true then this thread is never quit at runtime. False by default.
  161. Thread* mpThread; /// The Thread itself.
  162. ThreadPool* mpThreadPool; /// The ThreadPool that owns this thread.
  163. Job mCurrentJob; /// The most recent job a thread is or was working on.
  164. ThreadInfo();
  165. };
  166. /// AddThread
  167. /// Adds a new thread with the given ThreadParameters.
  168. /// The return value is not safe to use unless this function is called
  169. /// and the result used within a Lock/Unlock pair.
  170. /// It's the user's responsibility to supply ThreadParameters that are sane.
  171. /// If bBeginThread is true, then the Thread is started via a call to
  172. /// pThreadInfo->mpThread->Begin(ThreadFunction, pThreadInfo, &tp);
  173. /// Otherwise the user is expected to manually start the thread.
  174. ThreadInfo* AddThread(const ThreadParameters& tp, bool bBeginThread);
  175. // Gets the ThreadInfo for the nth Thread identified by index.
  176. // You must call this function and use the info within a Lock/Unlock pair
  177. // on the thread pool.
  178. ThreadInfo* GetThreadInfo(int index);
  179. // Unless you call this function while the Pool is locked (via Lock), the return
  180. // value may be out of date by the time you read it.
  181. int GetThreadCount();
  182. protected:
  183. typedef EA::Thread::simple_list<Job> JobList;
  184. typedef EA::Thread::simple_list<ThreadInfo*> ThreadInfoList;
  185. // Member functions
  186. static intptr_t ThreadFunction(void* pContext);
  187. ThreadInfo* CreateThreadInfo();
  188. void SetupThreadParameters(ThreadParameters& tp);
  189. void AdjustThreadCount(unsigned nCount);
  190. Result QueueJob(const Job& job, Thread** ppThread, bool bEnableDeferred);
  191. void AddThread(ThreadInfo* pThreadInfo);
  192. void RemoveThread(ThreadInfo* pThreadInfo);
  193. void FixThreads();
  194. // Member data
  195. bool mbInitialized; //
  196. uint32_t mnMinCount; // Min number of threads to have available.
  197. uint32_t mnMaxCount; // Max number of threads to have available.
  198. AtomicInt32 mnCurrentCount; // Current number of threads available.
  199. AtomicInt32 mnActiveCount; // Current number of threads busy with jobs.
  200. ThreadTime mnIdleTimeoutMilliseconds; // Timeout before quitting threads that have had no jobs.
  201. uint32_t mnProcessorMask; // If mask is not 0xffffffff then we manually round-robin assign processors.
  202. uint32_t mnProcessorCount; // The number of processors currently present.
  203. uint32_t mnNextProcessor; // Used if we are manually round-robin assigning processors.
  204. AtomicInt32 mnPauseCount; // A positive value means we pause working on jobs.
  205. AtomicInt32 mnLastJobID; //
  206. ThreadParameters mDefaultThreadParameters; //
  207. Condition mThreadCondition; // Manages signalling mJobList.
  208. Mutex mThreadMutex; // Guards manipulation of mThreadInfoList and mJobList.
  209. ThreadInfoList mThreadInfoList; // List of threads in our pool.
  210. JobList mJobList; // List of waiting jobs.
  211. private:
  212. // Prevent default generation of these functions by not defining them
  213. ThreadPool(const ThreadPool& rhs); // copy constructor
  214. ThreadPool& operator=(const ThreadPool& rhs); // assignment operator
  215. };
  216. /// ThreadPoolFactory
  217. ///
  218. /// Implements a factory-based creation and destruction mechanism for class ThreadPool.
  219. /// A primary use of this would be to allow the ThreadPool implementation to reside in
  220. /// a private library while users of the class interact only with the interface
  221. /// header and the factory. The factory provides conventional create/destroy
  222. /// semantics which use global operator new, but also provides manual construction/
  223. /// destruction semantics so that the user can provide for memory allocation
  224. /// and deallocation.
  225. class EATHREADLIB_API ThreadPoolFactory
  226. {
  227. public:
  228. static ThreadPool* CreateThreadPool(); // Internally implemented as: return new ThreadPool;
  229. static void DestroyThreadPool(ThreadPool* pThreadPool); // Internally implemented as: delete pThreadPool;
  230. static size_t GetThreadPoolSize(); // Internally implemented as: return sizeof(ThreadPool);
  231. static ThreadPool* ConstructThreadPool(void* pMemory); // Internally implemented as: return new(pMemory) ThreadPool;
  232. static void DestructThreadPool(ThreadPool* pThreadPool); // Internally implemented as: pThreadPool->~ThreadPool();
  233. };
  234. } // namespace Thread
  235. } // namespace EA
  236. #if defined(EA_DLL) && defined(EA_COMPILER_MSVC)
  237. // re-enable warning 4251 (it's a level-1 warning and should not be suppressed globally)
  238. EA_RESTORE_VC_WARNING()
  239. #endif
  240. #endif // EATHREAD_EATHREAD_POOL_H