Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
12 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. EXECQ.H
  5. Abstract:
  6. Defines classes related to execution queues.
  7. Classes defined:
  8. CExecRequest An abstract request.
  9. CExecQueue A queue of requests with an associated thread
  10. History:
  11. 23-Jul-96 a-raymcc Created.
  12. 3/10/97 a-levn Fully documented
  13. 9/6/97 a-levn Rewrote for thread pool
  14. --*/
  15. #ifndef __EXECQUEUE__H_
  16. #define __EXECQUEUE__H_
  17. #include "sync.h"
  18. #include "wbemutil.h"
  19. #ifdef __COLLECT_ALLOC_STAT
  20. #include "stackcom.h"
  21. #endif
  22. //******************************************************************************
  23. //******************************************************************************
  24. //
  25. // class CExecRequest
  26. //
  27. // Abstract base class for any schedulable request
  28. //
  29. //******************************************************************************
  30. //
  31. // Execute
  32. //
  33. // Primary method. Executes the request, whatever that means.
  34. //
  35. // Returns:
  36. //
  37. // int: return code. 0 means success, everything else --- failure.
  38. // Exact error codes are request-specific.
  39. //
  40. //******************************************************************************
  41. class POLARITY CExecRequest
  42. {
  43. protected:
  44. #ifdef WINMGMT_THREAD_DEBUG
  45. static CCritSec mstatic_cs;
  46. static CPointerArray<CExecRequest> mstatic_apOut;
  47. #endif
  48. #ifdef __COLLECT_ALLOC_STAT
  49. public:
  50. CStackRecord m_Stack;
  51. protected:
  52. #endif
  53. HANDLE m_hWhenDone;
  54. CExecRequest* m_pNext;
  55. long m_lPriority;
  56. bool m_fOk;
  57. public:
  58. void SetWhenDoneHandle(HANDLE h) {m_hWhenDone = h;}
  59. HANDLE GetWhenDoneHandle() {return m_hWhenDone;}
  60. void SetNext(CExecRequest* pNext) {m_pNext = pNext;}
  61. CExecRequest* GetNext() {return m_pNext;}
  62. void SetPriority(long lPriority) {m_lPriority = lPriority;}
  63. long GetPriority() {return m_lPriority;}
  64. virtual void DumpError(){ DEBUGTRACE((LOG_WBEMCORE,
  65. "No additional info\n"));};
  66. bool IsOk( void ) { return m_fOk; }
  67. public:
  68. CExecRequest();
  69. virtual ~CExecRequest();
  70. virtual HRESULT Execute() = 0;
  71. };
  72. class CDavidsRequest
  73. {
  74. protected:
  75. LPTHREAD_START_ROUTINE m_pfn;
  76. void* m_pParam;
  77. public:
  78. CDavidsRequest(LPTHREAD_START_ROUTINE pFunctionToExecute, void* pParam)
  79. : m_pfn(pFunctionToExecute), m_pParam(pParam)
  80. {}
  81. HRESULT Execute()
  82. {
  83. return (HRESULT)m_pfn(m_pParam);
  84. }
  85. };
  86. //******************************************************************************
  87. //******************************************************************************
  88. //
  89. // class CExecQueue
  90. //
  91. // CExecQueue represents the concept of a queue of requests with an associated
  92. // thread to execute those requests. In a lot of respects, it is similar to
  93. // a message queue. Requests are added to the queue (which is represented by
  94. // an array) and the thread (created by the Run function) picks them up one
  95. // by one and executes them.
  96. //
  97. // The trick is what to do if while processing one request, another one
  98. // is generated and needs to be processed before the first one succeeds. This
  99. // is similar to a SendMessage, but trickier: the thread generating the new
  100. // request may not be the thread attached to the queue!
  101. //
  102. // To overcome this problem, we make all our waits interruptible in the
  103. // following sense. Whenever the thread attached to the queue needs to block
  104. // waiting for something to happen (which is when another thread may post a
  105. // new request and deadlock the system), it uses QueueWaitForSingleObject
  106. // instead. This function will wait for the object that the thread wanted to
  107. // wait for but it will also wake up if a new Critical request is added to
  108. // the queue and process any such request while waiting.
  109. //
  110. // See QueueWaitForSingleObject for details.
  111. //
  112. // Operations of CExecQueue are protected by a critical section, so multiple
  113. // threads can add requests simultaneously.
  114. //
  115. //******************************************************************************
  116. //
  117. // Constructor
  118. //
  119. // Creates and initializes all the synchronization objects, as well as the
  120. // thread local storage required by QueueWaitForSingleObject.
  121. //
  122. //******************************************************************************
  123. //
  124. // Destructor
  125. //
  126. // Deletes synchronization objects.
  127. //
  128. //******************************************************************************
  129. //
  130. // virtual Enqueue
  131. //
  132. // Adds a request to the queue. The acction depends on whether the request is
  133. // critical or not. If not, it is added to the queue and the semaphor of
  134. // non-critical requests is incremented. The processing thread will pick it up
  135. // in FIFO order. If critical, request is added to the front of the queye and
  136. // the semaphor of critical requests is incremented. This will cause the
  137. // processing thread to take this request the next time it enters into a
  138. // waiting state (see QueueWaitForSingleObject).
  139. //
  140. //******************************************************************************
  141. //
  142. // QueueWaitForSingleObject
  143. //
  144. // The core of the trick. In WINMGMT, whenever a thread needs to wait for an
  145. // object, it calls this function instead. This function checks if the calling
  146. // thread is the registered processing thread for any CExecQueue object (by
  147. // looking up the m_dwTlsIndex thread local variable for the thread). If it
  148. // is not, the function simply calls WaitForSingleObject.
  149. //
  150. // If it is, the function queries the queue for the semaphore indicating the
  151. // number of critical requests on the queue. It then calls
  152. // WaitForMultipleObjects with the original handle and the semaphore. If the
  153. // semaphore is signaled during the wait (or was singlaled when we came in),
  154. // this function picks up the first requests on the queue and executes it;
  155. // once that request is complete, it resumes the wait (with adjusted timeout).
  156. //
  157. // Parameters:
  158. //
  159. // HANDLE hHandle The handle of synchronization object to wait for.
  160. // DWORD dwTimeout Timeout in milliseconds.
  161. //
  162. // Returns:
  163. //
  164. // Same values as WaitForSingleObject:
  165. // WAIT_OBJECT_0 hHandle became signaled
  166. // WAIT_TIMEOUT Timed out.
  167. //
  168. //******************************************************************************
  169. //**************************** protected ***************************************
  170. //
  171. // Register
  172. //
  173. // Registers the calling thread as the processing thread of this queue by
  174. // storing the pointer to the queue in the m_dwTlsIndex thread local storage
  175. // variable. QueueWaitForSingleObject reads this index to interrupt waits
  176. // when needed (see QueueWaitForSingleObject).
  177. //
  178. // Returns:
  179. //
  180. // CExecQueue*: the previous CExecQueue this thread was registered for,
  181. // or NULL if none. The caller MUST not delete this object.
  182. //
  183. //******************************************************************************
  184. //
  185. // ThreadMain
  186. //
  187. // This is the function that the thread created by Run executes. It sits in
  188. // an infinite loop, retrieving requests and executing them one by one.
  189. // This function never returns.
  190. //
  191. //******************************************************************************
  192. //
  193. // Dequeue
  194. //
  195. // Retrieves the request at the head of the queue and removes it from the
  196. // queue.
  197. //
  198. // Returns:
  199. //
  200. // CExecRequest*: the request that was at the head of the queue, or NULL
  201. // if the queue was empty. The caller must delete this
  202. // object when no longer needed.
  203. //
  204. //******************************************************************************
  205. //
  206. // static _ThreadEntry
  207. //
  208. // Stub function used to create the tread. Calls ThreadEntry on the real
  209. // CExecQueue.
  210. //
  211. // Parameters:
  212. //
  213. // LPVOID pObj Actually CExecQueue* to the queue this thread is
  214. // supposed to serve.
  215. //
  216. // Returns:
  217. //
  218. // never.
  219. //
  220. //******************************************************************************
  221. //
  222. // static InitTls
  223. //
  224. // Invoked only once during the life of the system (not the life of a queue),
  225. // creates a thread local storage location where the pointer to the queue is
  226. // stored for the attached threads (see Register and QueueWaitForSingleObject)
  227. //
  228. //******************************************************************************
  229. //
  230. // GetNormalReadyHandle
  231. //
  232. // Returns the handle to the semaphore which contains the number of
  233. // non-critical requests currently on the queue.
  234. //
  235. // Returns:
  236. //
  237. // HANDLE: the the semaphore
  238. //
  239. //******************************************************************************
  240. //
  241. // GetCriticalReadyHandle
  242. //
  243. // Returns the handle to the semaphore which contains the number of
  244. // critical requests currently on the queue.
  245. //
  246. // Returns:
  247. //
  248. // HANDLE: the the semaphore
  249. //
  250. //******************************************************************************
  251. //
  252. // Execute
  253. //
  254. // Dequeues and executes a single request.
  255. //
  256. //******************************************************************************
  257. class POLARITY CExecQueue
  258. {
  259. protected:
  260. class CThreadRecord
  261. {
  262. public:
  263. CExecQueue* m_pQueue;
  264. CExecRequest* m_pCurrentRequest;
  265. BOOL m_bReady;
  266. BOOL m_bExitNow;
  267. HANDLE m_hThread;
  268. HANDLE m_hAttention;
  269. public:
  270. CThreadRecord(CExecQueue* pQueue);
  271. ~CThreadRecord();
  272. void Signal();
  273. };
  274. protected:
  275. static long mstatic_lNumInits;
  276. long m_lRef;
  277. CRITICAL_SECTION m_cs;
  278. CFlexArray m_aThreads;
  279. CExecRequest* m_pHead;
  280. CExecRequest* m_pTail;
  281. long m_lNumThreads;
  282. long m_lNumIdle;
  283. long m_lNumRequests;
  284. long m_lMaxThreads;
  285. long m_lHiPriBound;
  286. long m_lHiPriMaxThreads;
  287. long m_lStartSlowdownCount;
  288. long m_lAbsoluteLimitCount;
  289. long m_lOneSecondDelayCount;
  290. double m_dblAlpha;
  291. double m_dblBeta;
  292. DWORD m_dwTimeout;
  293. DWORD m_dwOverflowTimeout;
  294. protected:
  295. virtual void ThreadMain(CThreadRecord* pRecord);
  296. virtual void LogError(CExecRequest* pRequest, int nRes);
  297. static DWORD WINAPI _ThreadEntry(LPVOID pObj);
  298. static void InitTls();
  299. virtual void InitializeThread();
  300. virtual void UninitializeThread();
  301. virtual BOOL CreateNewThread();
  302. static void Register(CThreadRecord* pRecord);
  303. virtual void ShutdownThread(CThreadRecord* pRecord);
  304. virtual BOOL IsSuitableThread(CThreadRecord* pRecord, CExecRequest* pReq);
  305. virtual BOOL DoesNeedNewThread(CExecRequest* pReq);
  306. virtual BOOL IsIdleTooLong(CThreadRecord* pRecord, DWORD dwIdle);
  307. virtual DWORD GetIdleTimeout(CThreadRecord* pRecord);
  308. virtual BOOL IsAppropriateThread();
  309. virtual DWORD WaitForSingleObjectWhileBusy(HANDLE hHandle, DWORD dwWait,
  310. CThreadRecord* pRecord);
  311. virtual DWORD UnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait,
  312. CThreadRecord* pRecord);
  313. virtual BOOL Execute(CThreadRecord* pRecord);
  314. virtual BOOL IsSTA() {return FALSE;}
  315. virtual CExecRequest* SearchForSuitableRequest(CThreadRecord* pRecord);
  316. virtual void SitOutPenalty(long lRequestIndex);
  317. virtual DWORD CalcSitOutPenalty(long lRequestIndex);
  318. virtual void AdjustInitialPriority(CExecRequest* pRequest){}
  319. virtual void AdjustPriorityForPassing(CExecRequest* pRequest){}
  320. public:
  321. CExecQueue();
  322. ~CExecQueue();
  323. void AddRef() {InterlockedIncrement(&m_lRef);}
  324. void Release() {if(InterlockedDecrement(&m_lRef) == 0) delete this;}
  325. static DWORD GetTlsIndex();
  326. void Enter();
  327. void Leave();
  328. virtual HRESULT Enqueue(CExecRequest* pRequest, HANDLE* phWhenDone = NULL);
  329. HRESULT EnqueueWithoutSleep(CExecRequest* pRequest, HANDLE* phWhenDone = NULL );
  330. HRESULT EnqueueAndWait(CExecRequest* pRequest);
  331. virtual LPCWSTR GetType() {return L"";}
  332. void SetThreadLimits(long lMaxThreads, long lHiPriMaxThreads = -1,
  333. long lHiPriBound = 0);
  334. void SetIdleTimeout(DWORD dwTimeout) {m_dwTimeout = dwTimeout;}
  335. void SetOverflowIdleTimeout(DWORD dwTimeout)
  336. {m_dwOverflowTimeout = dwTimeout;}
  337. void SetRequestLimits(long lAbsoluteLimitCount,
  338. long lStartSlowdownCount = -1, long lOneSecondDelayCount = -1);
  339. void Shutdown();
  340. DWORD GetSitoutPenalty( void ) { return CalcSitOutPenalty( m_lNumRequests ); }
  341. static DWORD QueueWaitForSingleObject(HANDLE hHandle, DWORD dwWait);
  342. static DWORD QueueUnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait);
  343. static BOOL IsSTAThread();
  344. };
  345. #endif