Leaked source code of windows server 2003
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.

572 lines
22 KiB

  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: work.h
  6. *
  7. * Content: DirectPlay Thread Pool work processing functions header file.
  8. *
  9. * History:
  10. * Date By Reason
  11. * ======== ======== =========
  12. * 10/31/01 VanceO Created.
  13. *
  14. ******************************************************************************/
  15. #ifndef __WORK_H__
  16. #define __WORK_H__
  17. //=============================================================================
  18. // Defines
  19. //=============================================================================
  20. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  21. #define MAX_TRACKED_CALLBACKSTATS 15 // maximum number of unique work callback functions to track
  22. #endif // DPNBUILD_THREADPOOLSTATISTICS
  23. //=============================================================================
  24. // Forward declarations
  25. //=============================================================================
  26. typedef struct _DPTHREADPOOLOBJECT DPTHREADPOOLOBJECT;
  27. //=============================================================================
  28. // Structures
  29. //=============================================================================
  30. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  31. typedef struct _CALLBACKSTATS
  32. {
  33. PFNDPTNWORKCALLBACK pfnWorkCallback; // pointer to work callback function whose stats are being tracked
  34. DWORD dwNumCreates; // number of times a work item with this callback was created
  35. DWORD dwTotalCompletionTime; // total time from creation to completion for all I/O operations using this callback, total time from setting to firing for all timers using this callback
  36. DWORD dwNumQueues; // number of times a work item with this callback was queued for completion
  37. DWORD dwTotalQueueTime; // total time from queuing to callback execution for all work items using this callback
  38. DWORD dwNumCalls; // number of times the callback was invoked
  39. DWORD dwTotalCallbackTime; // total time spent in the callback for all work items using this callback that did not reschedule
  40. DWORD dwNumNotRescheduled; // number of times the callback returned without rescheduling
  41. } CALLBACKSTATS, * PCALLBACKSTATS;
  42. #endif // DPNBUILD_THREADPOOLSTATISTICS
  43. typedef struct _DPTPWORKQUEUE
  44. {
  45. //
  46. // NOTE: NBQueueBlockInitial must be heap aligned, so it is first in the
  47. // structure.
  48. //
  49. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  50. DNNBQUEUE_BLOCK NBQueueBlockInitial; // initial tracking info for the work queue or free node list (cast as DNSLIST_ENTRY for the latter) required by NB Queue implementation
  51. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  52. BYTE Sig[4]; // debugging signature ('WRKQ')
  53. //
  54. // Volatile data that can get tweaked simultaneously by multiple threads
  55. //
  56. CFixedPool * pWorkItemPool; // (work) pool of currently unused work items
  57. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  58. DNSLIST_HEADER SlistFreeQueueNodes; // (work) pool of nodes used to track work items in non-blocking queue (because of Slist implementation, it can only hold sizeof(WORD) == 65,535 entries)
  59. PVOID pvNBQueueWorkItems; // (work) header for list of work items needing execution (because of Slist implementation, it can only hold sizeof(WORD) == 65,535 entries)
  60. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  61. #ifndef DPNBUILD_ONLYONETHREAD
  62. BOOL fTimerThreadNeeded; // (work) boolean indicated whether a worker thread is currently needed as a timer thread (there can be only one)
  63. DWORD dwNumThreadsExpected; // (work) number of threads threads currently starting up/shutting down
  64. DWORD dwNumBusyThreads; // (work) number of threads that are currently processing work items
  65. DWORD dwNumRunningThreads; // (work) number of threads that are currently running
  66. #endif // ! DPNBUILD_ONLYONETHREAD
  67. DNSLIST_HEADER * paSlistTimerBuckets; // (timer) pointer to array of list headers for the timer buckets (because of Slist implementation, each bucket can only hold sizeof(WORD) == 65,535 entries, but that should be plenty)
  68. #if ((! defined(DPNBUILD_DONTCHECKFORMISSEDTIMERS)) && (! defined(DPNBUILD_NOMISSEDTIMERSHINT)))
  69. DWORD dwPossibleMissedTimerWindow; // (timer) cumulative hint to timer thread about short timers that were possibly missed
  70. #endif // ! DPNBUILD_DONTCHECKFORMISSEDTIMERS and ! DPNBUILD_NOMISSEDTIMERSHINT
  71. DNSLIST_HEADER SlistOutstandingIO; // (I/O) header for list of outstanding I/O waiting for completion (because of Slist implementation, it can only hold sizeof(WORD) == 65,535 entries)
  72. //
  73. // Regularly updated data, but it should only get modified by the one timer
  74. // thread.
  75. //
  76. DWORD dwLastTimerProcessTime; // (timer) when we last handled timer entries
  77. //
  78. // "Constant" data that is read-only for all threads
  79. //
  80. #if ((! defined(DPNBUILD_ONLYONETHREAD)) && ((! defined(WINCE)) || (defined(DBG))))
  81. DNCRITICAL_SECTION csListLock; // (work) lock protecting list of tracked handles (and list of threads owned by this work queue in debug)
  82. #endif // ! DPNBUILD_ONLYONETHREAD and (! WINCE or DBG)
  83. #ifndef DPNBUILD_ONLYONEPROCESSOR
  84. DWORD dwCPUNum; // (work) the CPU number this queue represents
  85. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  86. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  87. DNHANDLE hIoCompletionPort; // (work) I/O completion port used to track I/O and queue work items
  88. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  89. DNHANDLE hAlertEvent; // (work) handle to the event used to wake up idle worker threads
  90. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  91. #ifndef DPNBUILD_ONLYONETHREAD
  92. DNHANDLE hExpectedThreadsEvent; // (work) temporary handle to the event to be set when the desired number of threads are started/stopped
  93. PFNDPNMESSAGEHANDLER pfnMsgHandler; // (work) user's message handler function, or NULL if none.
  94. PVOID pvMsgHandlerContext; // (work) user's context for message handler function
  95. DWORD dwWorkerThreadTlsIndex; // (work) Thread Local Storage index for storing the worker thread data
  96. #endif // ! DPNBUILD_ONLYONETHREAD
  97. CBilink blTrackedFiles; // (I/O) doubly linked list holding all files tracked by this CPU, protected by this work queue's list lock
  98. #ifdef DPNBUILD_DYNAMICTIMERSETTINGS
  99. DWORD dwTimerBucketGranularity; // (timer) the granularity in ms for each timer bucket
  100. DWORD dwTimerBucketGranularityCeiling; // (timer) precalculated addend used when rounding time stamps up to the appropriate granularity, it also happens to be the module mask, but its currently never used that way
  101. DWORD dwTimerBucketGranularityFloorMask; // (timer) precalculated pseudo-modulo bit mask for rounding down time stamps to the appropriate granularity
  102. DWORD dwTimerBucketGranularityDivisor; // (timer) precalculated pseudo-divisor bit shift for converting time into buckets
  103. DWORD dwNumTimerBuckets; // (timer) the number of timer buckets in the array
  104. DWORD dwNumTimerBucketsModMask; // (timer) precalculated pseudo-modulo bit mask for wrapping around the array
  105. #endif // DPNBUILD_DYNAMICTIMERSETTINGS
  106. #if ((defined(WINNT)) || ((defined(WIN95)) && (! defined(DPNBUILD_NOWAITABLETIMERSON9X))))
  107. DNHANDLE hTimer; // (timer) handle to the waitable timer object used to wake up a worker thread periodically
  108. #endif // WINNT or (WIN95 AND ! DPNBUILD_NOWAITABLETIMERSON9X)
  109. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  110. //
  111. // Debugging/tuning statistics.
  112. //
  113. DWORD dwTotalNumWorkItems; // (work) total number of work items placed in this queue
  114. #ifndef WINCE
  115. DWORD dwTotalTimeSpentUnsignalled; // (work) total number of milliseconds spent waiting for the alert event to be fired
  116. DWORD dwTotalTimeSpentInWorkCallbacks; // (work) total number of milliseconds spent in work item callbacks
  117. #endif // ! WINCE
  118. #ifndef DPNBUILD_ONLYONETHREAD
  119. DWORD dwTotalNumTimerThreadAbdications; // (work) total number of times the existing timer thread allowed another thread to become the timer thread
  120. #endif // ! DPNBUILD_ONLYONETHREAD
  121. DWORD dwTotalNumWakesWithoutWork; // (work) total number of times a worker thread woke up but found nothing to do
  122. DWORD dwTotalNumContinuousWork; // (work) total number of times a thread found another work item after completing a previous one
  123. DWORD dwTotalNumDoWorks; // (work) total number of times DoWork was called
  124. DWORD dwTotalNumDoWorksTimeLimit; // (work) total number of times DoWork stopped looping due to the time limit
  125. DWORD dwTotalNumSimultaneousQueues; // (work) total number of times more than one work item was queued at the same time
  126. CALLBACKSTATS aCallbackStats[MAX_TRACKED_CALLBACKSTATS]; // (work) array of stats for tracked callbacks
  127. DWORD dwTotalNumTimerChecks; // (timer) total number of times any expired timer buckets have been handled
  128. DWORD dwTotalNumBucketsProcessed; // (timer) total number of timer buckets that have been checked
  129. DWORD dwTotalNumTimersScheduled; // (timer) total number of timers that were scheduled
  130. DWORD dwTotalNumLongTimersRescheduled; // (timer) total number of long timers that were rescheduled back into a bucket
  131. DWORD dwTotalNumSuccessfulCancels; // (timer) total number of CancelTimer calls that succeeded
  132. DWORD dwTotalNumFailedCancels; // (timer) total number of CancelTimer calls that failed
  133. #if ((! defined(DPNBUILD_DONTCHECKFORMISSEDTIMERS)) && (! defined(DPNBUILD_NOMISSEDTIMERSHINT)))
  134. DWORD dwTotalPossibleMissedTimerWindows; // (timer) total of all hints to timer thread about possibly missed short timers
  135. #endif // ! DPNBUILD_DONTCHECKFORMISSEDTIMERS and ! DPNBUILD_NOMISSEDTIMERSHINT
  136. #endif // DPNBUILD_THREADPOOLSTATISTICS
  137. #ifdef DBG
  138. #ifndef DPNBUILD_ONLYONETHREAD
  139. //
  140. // Structures helpful for debugging.
  141. //
  142. CBilink blThreadList; // (work) list of all threads owned by this work queue, protected by this work queue's list lock
  143. #endif // ! DPNBUILD_ONLYONETHREAD
  144. #endif // DBG
  145. } DPTPWORKQUEUE, * PDPTPWORKQUEUE;
  146. #ifndef DPNBUILD_ONLYONETHREAD
  147. typedef struct _DPTPWORKERTHREAD
  148. {
  149. BYTE Sig[4]; // debugging signature ('WKTD')
  150. DPTPWORKQUEUE * pWorkQueue; // owning work queue
  151. DWORD dwRecursionCount; // recursion count
  152. BOOL fThreadIndicated; // whether CREATE_THREAD has returned and DESTROY_THREAD has not been started yet
  153. #ifdef DBG
  154. DWORD dwThreadID; // ID of thread
  155. DWORD dwMaxRecursionCount; // maximum recursion count over life of thread
  156. CBilink blList; // entry in work queue list of threads
  157. #endif // DBG
  158. } DPTPWORKERTHREAD, * PDPTPWORKERTHREAD;
  159. #ifdef DPNBUILD_MANDATORYTHREADS
  160. typedef struct _DPTPMANDATORYTHREAD
  161. {
  162. BYTE Sig[4]; // debugging signature ('MNDT')
  163. DPTHREADPOOLOBJECT * pDPTPObject; // owning thread pool object
  164. DNHANDLE hStartedEvent; // handle of event to set when thread has successfully started
  165. PFNDPNMESSAGEHANDLER pfnMsgHandler; // user's message handler function, or NULL if none.
  166. PVOID pvMsgHandlerContext; // user's context for message handler function
  167. LPTHREAD_START_ROUTINE lpStartAddress; // user start address for thread
  168. LPVOID lpParameter; // user parameter for thread
  169. #ifdef DBG
  170. DWORD dwThreadID; // ID of thread
  171. CBilink blList; // entry in work queue list of threads
  172. #endif // DBG
  173. } DPTPMANDATORYTHREAD, * PDPTPMANDATORYTHREAD;
  174. #endif // DPNBUILD_MANDATORYTHREADS
  175. #endif // ! DPNBUILD_ONLYONETHREAD
  176. //=============================================================================
  177. // Classes
  178. //=============================================================================
  179. //
  180. // It is critical to keep in mind that parts of this class must remain as valid
  181. // memory, even when the item is returned to the pool. Particularly, the NB
  182. // Queue code will use m_NBQueueBlock to track work item objects other than
  183. // the one whose member is used. Also, late timers use m_uiUniqueID to detect
  184. // that they are late.
  185. //
  186. // Basically this means the code needs to be revisited if pooling of CWorkItems
  187. // is turned off, or if the pool code is modified to be able to shrink the pool
  188. // (unlike the growth-only mechanism used now).
  189. //
  190. class CWorkItem
  191. {
  192. public:
  193. #undef DPF_MODNAME
  194. #define DPF_MODNAME "CWorkItem::FPM_Alloc"
  195. static BOOL FPM_Alloc(void * pvItem, void * pvContext)
  196. {
  197. CWorkItem * pWorkItem = (CWorkItem*) pvItem;
  198. pWorkItem->m_Sig[0] = 'W';
  199. pWorkItem->m_Sig[1] = 'O';
  200. pWorkItem->m_Sig[2] = 'R';
  201. pWorkItem->m_Sig[3] = 'K';
  202. //
  203. // Remember the owning work queue.
  204. //
  205. pWorkItem->m_pWorkQueue = (DPTPWORKQUEUE*) pvContext;
  206. #ifdef DBG
  207. memset(&pWorkItem->m_Overlapped, 0x10, sizeof(pWorkItem->m_Overlapped));
  208. #endif // DBG
  209. //
  210. // We will start the unique ID sequence at 0, but it really doesn't
  211. // matter. It's technically safe for it to be stack garbage since
  212. // we only use it for comparison.
  213. //
  214. pWorkItem->m_uiUniqueID = 0;
  215. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  216. //
  217. // Throw the embedded DNNBQUEUE_BLOCK structure into the free list
  218. // for the queue. Remember that it may be used to track work items
  219. // other than this object.
  220. //
  221. DNInterlockedPushEntrySList(&((DPTPWORKQUEUE*) pvContext)->SlistFreeQueueNodes,
  222. (DNSLIST_ENTRY*) (&pWorkItem->m_NBQueueBlock));
  223. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  224. return TRUE;
  225. }
  226. #undef DPF_MODNAME
  227. #define DPF_MODNAME "CWorkItem::FPM_Get"
  228. static void FPM_Get(void * pvItem, void * pvContext)
  229. {
  230. CWorkItem * pWorkItem = (CWorkItem*) pvItem;
  231. #ifdef DBG
  232. memset(&pWorkItem->m_Overlapped, 0, sizeof(pWorkItem->m_Overlapped));
  233. DNASSERT(pWorkItem->m_pWorkQueue == (DPTPWORKQUEUE*) pvContext);
  234. #endif // DBG
  235. //
  236. // Make sure the object is ready to be cancelled. Really really
  237. // late cancel attempts on a previous instance should have hit the
  238. // m_dwUniqueID check.
  239. //
  240. pWorkItem->m_fCancelledOrCompleting = FALSE;
  241. }
  242. #undef DPF_MODNAME
  243. #define DPF_MODNAME "CWorkItem::FPM_Release"
  244. static void FPM_Release(void * pvItem)
  245. {
  246. CWorkItem * pWorkItem = (CWorkItem*) pvItem;
  247. //
  248. // Change the unique ID so that future late cancellation attempts
  249. // don't bother us. If the late attempt occurred before we do
  250. // this, it should have hit the m_fCancelledOrCompleting check.
  251. // And for non-timer work items, in debug builds we set
  252. // m_fCancelledOrCompleting to TRUE before queueing so that this
  253. // assert succeeds as well.
  254. //
  255. DNASSERT(pWorkItem->m_fCancelledOrCompleting);
  256. pWorkItem->m_uiUniqueID++;
  257. #ifdef DBG
  258. memset(&pWorkItem->m_Overlapped, 0x10, sizeof(pWorkItem->m_Overlapped));
  259. #endif // DBG
  260. }
  261. /*
  262. #undef DPF_MODNAME
  263. #define DPF_MODNAME "CWorkItem::FPM_Dealloc"
  264. static void FPM_Dealloc(void * pvItem)
  265. {
  266. CWorkItem * pWorkItem = (CWorkItem*) pvItem;
  267. }
  268. */
  269. #ifdef DBG
  270. BOOL IsValid(void)
  271. {
  272. if ((m_Sig[0] == 'W') &&
  273. (m_Sig[1] == 'O') &&
  274. (m_Sig[2] == 'R') &&
  275. (m_Sig[3] == 'K'))
  276. {
  277. return TRUE;
  278. }
  279. return FALSE;
  280. }
  281. #endif // DBG
  282. //
  283. // Generic work item information.
  284. //
  285. // NOTE: m_SlistEntry and m_NBQueueBlock must be heap aligned.
  286. //
  287. union
  288. {
  289. DNSLIST_ENTRY m_SlistEntry; // tracking info for the timer bucket list
  290. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  291. BYTE Alignment[16]; // alignment padding
  292. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  293. };
  294. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  295. DNNBQUEUE_BLOCK m_NBQueueBlock; // tracking info for the work queue or free node list (cast as DNSLIST_ENTRY for the latter)
  296. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  297. BYTE m_Sig[4]; // debugging signature ('WORK')
  298. DPTPWORKQUEUE * m_pWorkQueue; // pointer to owning work queue
  299. PFNDPTNWORKCALLBACK m_pfnWorkCallback; // pointer to function that should be called to perform the work
  300. PVOID m_pvCallbackContext; // pointer to context for performing the work
  301. //
  302. // I/O specific information.
  303. //
  304. OVERLAPPED m_Overlapped; // overlapped structure use to identify I/O operation to the OS
  305. //
  306. // Timer specific information.
  307. //
  308. DWORD m_dwDueTime; // expiration time for the work item
  309. BOOL m_fCancelledOrCompleting; // boolean set to TRUE if timer should be cancelled or it's queued to be processed
  310. UINT m_uiUniqueID; // continually incrementing identifier so the user can cancel the intended timer
  311. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  312. DWORD m_dwCreationTime; // time when work item was retrieved from the pool
  313. DWORD m_dwQueueTime; // time when work item was queued to be completed
  314. DWORD m_dwCallbackTime; // time when work item callback function began executing
  315. CALLBACKSTATS * m_pCallbackStats; // pointer to callback stats slot, or NULL if none
  316. #endif // DPNBUILD_THREADPOOLSTATISTICS
  317. };
  318. //=============================================================================
  319. // Inline thread pool statistics helper function implementations
  320. //=============================================================================
  321. inline void ThreadpoolStatsCreate(CWorkItem * const pWorkItem)
  322. {
  323. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  324. CALLBACKSTATS * pCallbackStats;
  325. PFNDPTNWORKCALLBACK pfnWorkCallback;
  326. //
  327. // Loop through all callback stats slots looking for the first one that
  328. // matches our callback or is NULL. If we don't find one, m_pCallbackStats
  329. // will remain NULL.
  330. //
  331. pWorkItem->m_pCallbackStats = NULL;
  332. pCallbackStats = pWorkItem->m_pWorkQueue->aCallbackStats;
  333. while (pCallbackStats < &pWorkItem->m_pWorkQueue->aCallbackStats[MAX_TRACKED_CALLBACKSTATS])
  334. {
  335. //
  336. // Retrieve this slot's current callback pointer. If it was NULL,
  337. // we'll fill it with our callback pointer in the process.
  338. //
  339. pfnWorkCallback = (PFNDPTNWORKCALLBACK) DNInterlockedCompareExchangePointer((PVOID*) (&pCallbackStats->pfnWorkCallback),
  340. pWorkItem->m_pfnWorkCallback,
  341. NULL);
  342. //
  343. // If the callback was already ours, or it was NULL (and thus got set
  344. // to ours), we've got a slot.
  345. //
  346. if ((pfnWorkCallback == pWorkItem->m_pfnWorkCallback) ||
  347. (pfnWorkCallback == NULL))
  348. {
  349. pWorkItem->m_pCallbackStats = pCallbackStats;
  350. DNInterlockedIncrement((LPLONG) (&pCallbackStats->dwNumCreates));
  351. break;
  352. }
  353. //
  354. // Move to next slot.
  355. //
  356. pCallbackStats++;
  357. }
  358. //
  359. // Remember the creation time.
  360. //
  361. pWorkItem->m_dwCreationTime = GETTIMESTAMP();
  362. #endif // DPNBUILD_THREADPOOLSTATISTICS
  363. }
  364. inline void ThreadpoolStatsQueue(CWorkItem * const pWorkItem)
  365. {
  366. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  367. //
  368. // Remember the queue time.
  369. //
  370. pWorkItem->m_dwQueueTime = GETTIMESTAMP();
  371. //
  372. // If we have a callback stats slot, update the additional info.
  373. //
  374. if (pWorkItem->m_pCallbackStats != NULL)
  375. {
  376. DNInterlockedIncrement((LPLONG) (&pWorkItem->m_pCallbackStats->dwNumQueues));
  377. #ifndef WINCE
  378. DNInterlockedExchangeAdd((LPLONG) (&pWorkItem->m_pCallbackStats->dwTotalCompletionTime),
  379. (pWorkItem->m_dwQueueTime - pWorkItem->m_dwCreationTime));
  380. #endif // ! WINCE
  381. }
  382. #endif // DPNBUILD_THREADPOOLSTATISTICS
  383. }
  384. inline void ThreadpoolStatsBeginExecuting(CWorkItem * const pWorkItem)
  385. {
  386. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  387. //
  388. // Remember when the callback began executing.
  389. //
  390. pWorkItem->m_dwCallbackTime = GETTIMESTAMP();
  391. //
  392. // If we have a callback stats slot, update the additional info.
  393. //
  394. if (pWorkItem->m_pCallbackStats != NULL)
  395. {
  396. DNInterlockedIncrement((LPLONG) (&pWorkItem->m_pCallbackStats->dwNumCalls));
  397. #ifndef WINCE
  398. DNInterlockedExchangeAdd((LPLONG) (&pWorkItem->m_pCallbackStats->dwTotalQueueTime),
  399. (pWorkItem->m_dwCallbackTime - pWorkItem->m_dwQueueTime));
  400. #endif // ! WINCE
  401. }
  402. #endif // DPNBUILD_THREADPOOLSTATISTICS
  403. }
  404. inline void ThreadpoolStatsEndExecuting(CWorkItem * const pWorkItem)
  405. {
  406. #ifdef DPNBUILD_THREADPOOLSTATISTICS
  407. #ifndef WINCE
  408. DWORD dwCallbackTime;
  409. dwCallbackTime = GETTIMESTAMP() - pWorkItem->m_dwCallbackTime;
  410. //
  411. // Track global stats on how long callbacks take.
  412. //
  413. DNInterlockedExchangeAdd((LPLONG) (&pWorkItem->m_pWorkQueue->dwTotalTimeSpentInWorkCallbacks),
  414. dwCallbackTime);
  415. #endif // WINCE
  416. //
  417. // If we have a callback stats slot, update the additional info.
  418. //
  419. if (pWorkItem->m_pCallbackStats != NULL)
  420. {
  421. DNInterlockedIncrement((LPLONG) (&pWorkItem->m_pCallbackStats->dwNumNotRescheduled));
  422. #ifndef WINCE
  423. DNInterlockedExchangeAdd((LPLONG) (&pWorkItem->m_pCallbackStats->dwTotalCallbackTime),
  424. dwCallbackTime);
  425. #endif // ! WINCE
  426. }
  427. #endif // DPNBUILD_THREADPOOLSTATISTICS
  428. }
  429. inline void ThreadpoolStatsEndExecutingRescheduled(CWorkItem * const pWorkItem)
  430. {
  431. //
  432. // Right now, we can't update any stats because we lost the work item pointer.
  433. //
  434. }
  435. //=============================================================================
  436. // Function prototypes
  437. //=============================================================================
  438. #ifdef DPNBUILD_ONLYONETHREAD
  439. #ifdef DPNBUILD_ONLYONEPROCESSOR
  440. HRESULT InitializeWorkQueue(DPTPWORKQUEUE * const pWorkQueue);
  441. #else // ! DPNBUILD_ONLYONEPROCESSOR
  442. HRESULT InitializeWorkQueue(DPTPWORKQUEUE * const pWorkQueue,
  443. const DWORD dwCPUNum);
  444. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  445. #else // ! DPNBUILD_ONLYONETHREAD
  446. HRESULT InitializeWorkQueue(DPTPWORKQUEUE * const pWorkQueue,
  447. #ifndef DPNBUILD_ONLYONEPROCESSOR
  448. const DWORD dwCPUNum,
  449. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  450. const PFNDPNMESSAGEHANDLER pfnMsgHandler,
  451. PVOID const pvMsgHandlerContext,
  452. const DWORD dwWorkerThreadTlsIndex);
  453. #endif // ! DPNBUILD_ONLYONETHREAD
  454. void DeinitializeWorkQueue(DPTPWORKQUEUE * const pWorkQueue);
  455. BOOL QueueWorkItem(DPTPWORKQUEUE * const pWorkQueue,
  456. const PFNDPTNWORKCALLBACK pfnWorkCallback,
  457. PVOID const pvCallbackContext);
  458. #ifndef DPNBUILD_ONLYONETHREAD
  459. HRESULT StartThreads(DPTPWORKQUEUE * const pWorkQueue,
  460. const DWORD dwNumThreads);
  461. HRESULT StopThreads(DPTPWORKQUEUE * const pWorkQueue,
  462. const DWORD dwNumThreads);
  463. #endif // ! DPNBUILD_ONLYONETHREAD
  464. void DoWork(DPTPWORKQUEUE * const pWorkQueue,
  465. const DWORD dwMaxDoWorkTime);
  466. #ifndef DPNBUILD_ONLYONETHREAD
  467. DWORD WINAPI DPTPWorkerThreadProc(PVOID pvParameter);
  468. #ifdef DPNBUILD_MANDATORYTHREADS
  469. DWORD WINAPI DPTPMandatoryThreadProc(PVOID pvParameter);
  470. #endif // DPNBUILD_MANDATORYTHREADS
  471. void DPTPWorkerLoop(DPTPWORKQUEUE * const pWorkQueue);
  472. #endif // ! DPNBUILD_ONLYONETHREAD
  473. #endif // __WORK_H__