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.

3372 lines
101 KiB

  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: threadpoolapi.cpp
  6. *
  7. * Content: DirectPlay Thread Pool API implementation functions.
  8. *
  9. * History:
  10. * Date By Reason
  11. * ======== ======== =========
  12. * 10/31/01 VanceO Created.
  13. *
  14. ******************************************************************************/
  15. #include "dpnthreadpooli.h"
  16. //=============================================================================
  17. // Macros
  18. //=============================================================================
  19. #ifdef DPNBUILD_ONLYONEPROCESSOR
  20. #define GET_OR_CHOOSE_WORKQUEUE(pDPTPObject, dwCPU) (&pDPTPObject->WorkQueue)
  21. #else // ! DPNBUILD_ONLYONEPROCESSOR
  22. #define GET_OR_CHOOSE_WORKQUEUE(pDPTPObject, dwCPU) ((dwCPU == -1) ? ChooseWorkQueue(pDPTPObject) : WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU))
  23. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  24. //=============================================================================
  25. // Local function prototypes
  26. //=============================================================================
  27. #ifndef DPNBUILD_ONLYONEPROCESSOR
  28. DPTPWORKQUEUE * ChooseWorkQueue(DPTHREADPOOLOBJECT * const pDPTPObject);
  29. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  30. #ifndef DPNBUILD_ONLYONETHREAD
  31. HRESULT SetTotalNumberOfThreads(DPTHREADPOOLOBJECT * const pDPTPObject,
  32. const DWORD dwNumThreads);
  33. #endif // ! DPNBUILD_ONLYONETHREAD
  34. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_LIBINTERFACE)))
  35. #undef DPF_MODNAME
  36. #define DPF_MODNAME "DPTP_Initialize"
  37. //=============================================================================
  38. // DPTP_Initialize
  39. //-----------------------------------------------------------------------------
  40. //
  41. // Description: Initializes the thread pool interface for the process. Only
  42. // one thread pool object per process is used. If another
  43. // IDirectPlay8ThreadPool interface was created and initialized,
  44. // this interface will return DPNERR_ALREADYINITIALIZED.
  45. //
  46. // The interface cannot be initialized if a DirectPlay object
  47. // has already created threads. DPNERR_NOTALLOWED will be
  48. // returned in that case.
  49. //
  50. // Arguments:
  51. // xxx pInterface - Pointer to interface.
  52. // PVOID pvUserContext - User context for all message callbacks.
  53. // PFNDPNMESSAGEHANDLER pfn - Pointer to function called to handle
  54. // thread pool messages.
  55. // DWORD dwFlags - Flags to use when initializing.
  56. //
  57. // Returns: HRESULT
  58. // DPN_OK - Initializing was successful.
  59. // DPNERR_ALREADYINITIALIZED - The interface has already been initialized.
  60. // DPNERR_INVALIDFLAGS - Invalid flags were specified.
  61. // DPNERR_INVALIDPARAM - An invalid parameter was specified.
  62. // DPNERR_NOTALLOWED - Threads have already been started.
  63. //=============================================================================
  64. STDMETHODIMP DPTP_Initialize(IDirectPlay8ThreadPool * pInterface,
  65. PVOID const pvUserContext,
  66. const PFNDPNMESSAGEHANDLER pfn,
  67. const DWORD dwFlags)
  68. {
  69. HRESULT hr;
  70. DPTHREADPOOLOBJECT * pDPTPObject;
  71. #ifndef DPNBUILD_ONLYONETHREAD
  72. DWORD dwTemp;
  73. DPTPWORKQUEUE * pWorkQueue;
  74. #endif // ! DPNBUILD_ONLYONETHREAD
  75. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%p, 0x%p, 0x%x)",
  76. pInterface, pvUserContext, pfn, dwFlags);
  77. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  78. DNASSERT(pDPTPObject != NULL);
  79. #ifndef DPNBUILD_NOPARAMVAL
  80. //if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_PARAMVALIDATION)
  81. {
  82. //
  83. // Validate parameters.
  84. //
  85. hr = DPTPValidateInitialize(pInterface, pvUserContext, pfn, dwFlags);
  86. if (hr != DPN_OK)
  87. {
  88. DPF_RETURN(hr);
  89. }
  90. }
  91. #endif // ! DPNBUILD_NOPARAMVAL
  92. //
  93. // Lock the object to prevent multiple threads from trying to change the
  94. // flags or thread count simultaneously.
  95. //
  96. DNEnterCriticalSection(&pDPTPObject->csLock);
  97. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_INITIALIZED)
  98. {
  99. DPFX(DPFPREP, 0, "Thread pool object already initialized!");
  100. hr = DPNERR_ALREADYINITIALIZED;
  101. goto Failure;
  102. }
  103. #ifndef DPNBUILD_ONLYONETHREAD
  104. DNASSERT(pDPTPObject->dwTotalUserThreadCount == -1);
  105. //
  106. // If a Work interface has already spun up some threads, we must fail.
  107. //
  108. if (pDPTPObject->dwTotalDesiredWorkThreadCount != -1)
  109. {
  110. DPFX(DPFPREP, 0, "Threads already exist, can't initialize!");
  111. hr = DPNERR_NOTALLOWED;
  112. goto Failure;
  113. }
  114. #ifdef DPNBUILD_MANDATORYTHREADS
  115. if (pDPTPObject->dwMandatoryThreadCount > 0)
  116. {
  117. DPFX(DPFPREP, 0, "Mandatory threads already exist, can't initialize!");
  118. hr = DPNERR_NOTALLOWED;
  119. goto Failure;
  120. }
  121. #endif // DPNBUILD_MANDATORYTHREADS
  122. //
  123. // Update all the work queues with the new message handler and context.
  124. //
  125. for(dwTemp = 0; dwTemp < NUM_CPUS(pDPTPObject); dwTemp++)
  126. {
  127. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp);
  128. DNASSERT(pWorkQueue->pfnMsgHandler == NULL);
  129. pWorkQueue->pfnMsgHandler = pfn;
  130. pWorkQueue->pvMsgHandlerContext = pvUserContext;
  131. }
  132. #endif // ! DPNBUILD_ONLYONETHREAD
  133. //
  134. // Mark the user's interface as ready.
  135. //
  136. pDPTPObject->dwFlags |= DPTPOBJECTFLAG_USER_INITIALIZED;
  137. #ifndef DPNBUILD_NOPARAMVAL
  138. //
  139. // If user doesn't want validation, turn it off.
  140. //
  141. if (dwFlags & DPNINITIALIZE_DISABLEPARAMVAL)
  142. {
  143. pDPTPObject->dwFlags &= ~DPTPOBJECTFLAG_USER_PARAMVALIDATION;
  144. }
  145. #endif // ! DPNBUILD_NOPARAMVAL
  146. DNLeaveCriticalSection(&pDPTPObject->csLock);
  147. hr = DPN_OK;
  148. Exit:
  149. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  150. return hr;
  151. Failure:
  152. DNLeaveCriticalSection(&pDPTPObject->csLock);
  153. goto Exit;
  154. } // DPTP_Initialize
  155. #undef DPF_MODNAME
  156. #define DPF_MODNAME "DPTP_Close"
  157. //=============================================================================
  158. // DPTP_Close
  159. //-----------------------------------------------------------------------------
  160. //
  161. // Description: Closes the thread pool interface. Any threads that exist
  162. // will call the message handler with DPN_MSGID_DESTROY_THREAD
  163. // before this method returns.
  164. //
  165. // This method cannot be called while a call to DoWork has not
  166. // returned, or from a thread pool thread. DPNERR_NOTALLOWED is
  167. // returned in these cases.
  168. //
  169. // Arguments:
  170. // xxx pInterface - Pointer to interface.
  171. // DWORD dwFlags - Flags to use when closing.
  172. //
  173. // Returns: HRESULT
  174. // DPN_OK - Closing was successful.
  175. // DPNERR_INVALIDFLAGS - Invalid flags were specified.
  176. // DPNERR_NOTALLOWED - A thread is in a call to DoWork or this is a
  177. // thread pool thread.
  178. // DPNERR_UNINITIALIZED - The interface has not yet been initialized.
  179. //=============================================================================
  180. STDMETHODIMP DPTP_Close(IDirectPlay8ThreadPool * pInterface,
  181. const DWORD dwFlags)
  182. {
  183. HRESULT hr;
  184. DPTHREADPOOLOBJECT * pDPTPObject;
  185. #ifndef DPNBUILD_ONLYONETHREAD
  186. DPTPWORKERTHREAD * pWorkerThread;
  187. DWORD dwTemp;
  188. DPTPWORKQUEUE * pWorkQueue;
  189. #endif // ! DPNBUILD_ONLYONETHREAD
  190. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%x)", pInterface, dwFlags);
  191. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  192. DNASSERT(pDPTPObject != NULL);
  193. #ifndef DPNBUILD_NOPARAMVAL
  194. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_PARAMVALIDATION)
  195. {
  196. //
  197. // Validate parameters.
  198. //
  199. hr = DPTPValidateClose(pInterface, dwFlags);
  200. if (hr != DPN_OK)
  201. {
  202. DPF_RETURN(hr);
  203. }
  204. }
  205. #endif // ! DPNBUILD_NOPARAMVAL
  206. //
  207. // Lock the object to prevent multiple threads from trying to change the
  208. // flags or thread count simultaneously.
  209. //
  210. DNEnterCriticalSection(&pDPTPObject->csLock);
  211. if (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_INITIALIZED))
  212. {
  213. DPFX(DPFPREP, 0, "Thread pool object not initialized!");
  214. hr = DPNERR_UNINITIALIZED;
  215. goto Failure;
  216. }
  217. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK)
  218. {
  219. DPFX(DPFPREP, 0, "Another thread is in a call to DoWork!");
  220. hr = DPNERR_NOTALLOWED;
  221. goto Failure;
  222. }
  223. #ifndef DPNBUILD_ONLYONETHREAD
  224. //
  225. // If this is a thread pool thread, fail.
  226. //
  227. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  228. if (pWorkerThread != NULL)
  229. {
  230. DPFX(DPFPREP, 0, "Cannot call Close from a thread pool thread!");
  231. hr = DPNERR_NOTALLOWED;
  232. goto Failure;
  233. }
  234. //
  235. // If a thread is currently changing the thread count (or trying
  236. // to but we got the lock first), bail.
  237. //
  238. if ((pDPTPObject->dwFlags & DPTPOBJECTFLAG_THREADCOUNTCHANGING) ||
  239. (pDPTPObject->lNumThreadCountChangeWaiters > 0))
  240. {
  241. DPFX(DPFPREP, 0, "Cannot call Close with other threads still using other methods!");
  242. hr = DPNERR_NOTALLOWED;
  243. goto Failure;
  244. }
  245. #ifdef DPNBUILD_MANDATORYTHREADS
  246. //
  247. // If there are mandatory threads still running, we can't close yet.
  248. // There is no way to have them issue DESTROY_THREAD callbacks.
  249. //
  250. if (pDPTPObject->dwMandatoryThreadCount > 0)
  251. {
  252. DPFX(DPFPREP, 0, "Mandatory threads still exist, can't close!");
  253. hr = DPNERR_NOTALLOWED;
  254. goto Failure;
  255. }
  256. #endif // DPNBUILD_MANDATORYTHREADS
  257. //
  258. // Clear the message handler information.
  259. //
  260. for(dwTemp = 0; dwTemp < NUM_CPUS(pDPTPObject); dwTemp++)
  261. {
  262. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp);
  263. DNASSERT(pWorkQueue->pfnMsgHandler != NULL);
  264. pWorkQueue->pfnMsgHandler = NULL;
  265. pWorkQueue->pvMsgHandlerContext = NULL;
  266. }
  267. //
  268. // If there were any threads, we must shut them down so they stop using the
  269. // user's callback.
  270. //
  271. #pragma TODO(vanceo, "Is there no efficient way to ensure all threads process a 'RemoveCallback' job?")
  272. if (((pDPTPObject->dwTotalUserThreadCount != -1) && (pDPTPObject->dwTotalUserThreadCount != 0)) ||
  273. (pDPTPObject->dwTotalDesiredWorkThreadCount != -1))
  274. {
  275. hr = SetTotalNumberOfThreads(pDPTPObject, 0);
  276. if (hr != DPN_OK)
  277. {
  278. DPFX(DPFPREP, 0, "Couldn't shut down existing threads!");
  279. goto Failure;
  280. }
  281. //
  282. // If some Work interface wanted threads, we need to spin them back up
  283. // because we don't know if the user is closing his/her interface
  284. // before all work is truly done.
  285. //
  286. if (pDPTPObject->dwTotalDesiredWorkThreadCount != -1)
  287. {
  288. hr = SetTotalNumberOfThreads(pDPTPObject, pDPTPObject->dwTotalDesiredWorkThreadCount);
  289. if (hr != DPN_OK)
  290. {
  291. DPFX(DPFPREP, 0, "Couldn't restart Work interface requested number of threads!");
  292. goto Failure;
  293. }
  294. }
  295. }
  296. //
  297. // In case the user had set the thread count, restore it to the "unknown"
  298. // value.
  299. //
  300. pDPTPObject->dwTotalUserThreadCount = -1;
  301. #endif // ! DPNBUILD_ONLYONETHREAD
  302. //
  303. // Mark the user's interface as no longer available.
  304. //
  305. pDPTPObject->dwFlags &= ~DPTPOBJECTFLAG_USER_INITIALIZED;
  306. #ifndef DPNBUILD_NOPARAMVAL
  307. //
  308. // Re-enable validation, in case it was off.
  309. //
  310. pDPTPObject->dwFlags |= DPTPOBJECTFLAG_USER_PARAMVALIDATION;
  311. #endif // ! DPNBUILD_NOPARAMVAL
  312. DNLeaveCriticalSection(&pDPTPObject->csLock);
  313. hr = DPN_OK;
  314. Exit:
  315. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  316. return hr;
  317. Failure:
  318. DNLeaveCriticalSection(&pDPTPObject->csLock);
  319. goto Exit;
  320. } // DPTP_Close
  321. #undef DPF_MODNAME
  322. #define DPF_MODNAME "DPTP_GetThreadCount"
  323. //=============================================================================
  324. // DPTP_GetThreadCount
  325. //-----------------------------------------------------------------------------
  326. //
  327. // Description: Retrieves the current number of threads for the given
  328. // processor, of if dwProcessorNum is -1, the total number of
  329. // threads for all processors.
  330. //
  331. // Arguments:
  332. // xxx pInterface - Pointer to interface.
  333. // DWORD dwProcessorNum - Processor whose thread count should be retrieved,
  334. // or -1 to retrieve the total number of threads.
  335. // DWORD * pdwNumThreads - Pointer to DWORD in which to store the current
  336. // number of threads.
  337. // DWORD dwFlags - Flags to use when retrieving thread count.
  338. //
  339. // Returns: HRESULT
  340. // DPN_OK - Retrieving the number of threads was successful.
  341. // DPNERR_INVALIDFLAGS - Invalid flags were specified.
  342. // DPNERR_INVALIDPARAM - An invalid parameter was specified.
  343. // DPNERR_UNINITIALIZED - The interface has not yet been initialized.
  344. //=============================================================================
  345. STDMETHODIMP DPTP_GetThreadCount(IDirectPlay8ThreadPool * pInterface,
  346. const DWORD dwProcessorNum,
  347. DWORD * const pdwNumThreads,
  348. const DWORD dwFlags)
  349. {
  350. HRESULT hr;
  351. DPTHREADPOOLOBJECT * pDPTPObject;
  352. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %i, 0x%p, 0x%x)",
  353. pInterface, dwProcessorNum, pdwNumThreads, dwFlags);
  354. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  355. DNASSERT(pDPTPObject != NULL);
  356. #ifndef DPNBUILD_NOPARAMVAL
  357. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_PARAMVALIDATION)
  358. {
  359. //
  360. // Validate parameters.
  361. //
  362. hr = DPTPValidateGetThreadCount(pInterface,
  363. dwProcessorNum,
  364. pdwNumThreads,
  365. dwFlags);
  366. if (hr != DPN_OK)
  367. {
  368. DPF_RETURN(hr);
  369. }
  370. }
  371. #endif // ! DPNBUILD_NOPARAMVAL
  372. //
  373. // Check object state (note: done without object lock).
  374. //
  375. if (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_INITIALIZED))
  376. {
  377. DPFX(DPFPREP, 0, "Thread pool object not initialized!");
  378. DPF_RETURN(DPNERR_UNINITIALIZED);
  379. }
  380. #ifdef DPNBUILD_ONLYONETHREAD
  381. *pdwNumThreads = 0;
  382. #else // ! DPNBUILD_ONLYONETHREAD
  383. if (dwProcessorNum == -1)
  384. {
  385. if (pDPTPObject->dwTotalUserThreadCount != -1)
  386. {
  387. *pdwNumThreads = pDPTPObject->dwTotalUserThreadCount;
  388. }
  389. else if (pDPTPObject->dwTotalDesiredWorkThreadCount != -1)
  390. {
  391. *pdwNumThreads = pDPTPObject->dwTotalDesiredWorkThreadCount;
  392. }
  393. else
  394. {
  395. *pdwNumThreads = 0;
  396. }
  397. }
  398. else
  399. {
  400. *pdwNumThreads = (WORKQUEUE_FOR_CPU(pDPTPObject, dwProcessorNum))->dwNumRunningThreads;
  401. }
  402. #endif // ! DPNBUILD_ONLYONETHREAD
  403. DPFX(DPFPREP, 7, "Number of threads = %u.", (*pdwNumThreads));
  404. hr = DPN_OK;
  405. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  406. return hr;
  407. } // DPTP_GetThreadCount
  408. #undef DPF_MODNAME
  409. #define DPF_MODNAME "DPTP_SetThreadCount"
  410. //=============================================================================
  411. // DPTP_SetThreadCount
  412. //-----------------------------------------------------------------------------
  413. //
  414. // Description: Alters the current number of threads for the given processor
  415. // number, or if dwProcessorNum is -1, the total number of threads
  416. // for all processors.
  417. //
  418. // If the new thread count is higher than the previous count,
  419. // the correct number of threads will be started (generating
  420. // DPN_MSGID_CREATE_THREAD messages) before this method returns.
  421. //
  422. // If the new thread count is lower than the previous count,
  423. // the correct number of threads will be shutdown (generating
  424. // DPN_MSGID_DESTROY_THREAD messages) before this method returns.
  425. //
  426. // This method cannot be used while another thread is
  427. // performing work. If a thread is in a call to DoWork, then
  428. // DPNERR_NOTALLOWED is returned and the thread count remains
  429. // unchanged.
  430. //
  431. // Thread pool threads cannot reduce the thread count. If this
  432. // thread is owned by the thread pool and dwNumThreads is less
  433. // than the current number of threads for the processor,
  434. // DPNERR_NOTALLOWED is returned and the thread count remains
  435. // unchanged.
  436. //
  437. // Arguments:
  438. // xxx pInterface - Pointer to interface.
  439. // DWORD dwProcessorNum - Processor number, or -1 for all processors.
  440. // DWORD dwNumThreads - Desired number of threads per processor.
  441. // DWORD dwFlags - Flags to use when setting the thread count.
  442. //
  443. // Returns: HRESULT
  444. // DPN_OK - Setting the number of threads was successful.
  445. // DPNERR_INVALIDFLAGS - Invalid flags were specified.
  446. // DPNERR_INVALIDPARAM - An invalid parameter was specified.
  447. // DPNERR_NOTALLOWED - A thread is currently calling DoWork, or this
  448. // thread pool thread is trying to reduce the
  449. // thread count.
  450. // DPNERR_UNINITIALIZED - The interface has not yet been initialized.
  451. //=============================================================================
  452. STDMETHODIMP DPTP_SetThreadCount(IDirectPlay8ThreadPool * pInterface,
  453. const DWORD dwProcessorNum,
  454. const DWORD dwNumThreads,
  455. const DWORD dwFlags)
  456. {
  457. HRESULT hr;
  458. DPTHREADPOOLOBJECT * pDPTPObject;
  459. #ifndef DPNBUILD_ONLYONETHREAD
  460. BOOL fSetThreadCountChanging = FALSE;
  461. DPTPWORKQUEUE * pWorkQueue;
  462. DPTPWORKERTHREAD * pWorkerThread;
  463. DWORD dwDelta;
  464. #endif // ! DPNBUILD_ONLYONETHREAD
  465. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %i, %u, 0x%x)",
  466. pInterface, dwProcessorNum, dwNumThreads, dwFlags);
  467. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  468. DNASSERT(pDPTPObject != NULL);
  469. #ifndef DPNBUILD_NOPARAMVAL
  470. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_PARAMVALIDATION)
  471. {
  472. //
  473. // Validate parameters.
  474. //
  475. hr = DPTPValidateSetThreadCount(pInterface,
  476. dwProcessorNum,
  477. dwNumThreads,
  478. dwFlags);
  479. if (hr != DPN_OK)
  480. {
  481. DPF_RETURN(hr);
  482. }
  483. }
  484. #endif // ! DPNBUILD_NOPARAMVAL
  485. //
  486. // Check object state (note: done without object lock).
  487. //
  488. if (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_INITIALIZED))
  489. {
  490. DPFX(DPFPREP, 0, "Thread pool object not initialized!");
  491. DPF_RETURN(DPNERR_UNINITIALIZED);
  492. }
  493. //
  494. // Lock the object to prevent multiple threads from trying to change the
  495. // thread count simultaneously.
  496. //
  497. DNEnterCriticalSection(&pDPTPObject->csLock);
  498. //
  499. // Make sure no one is trying to perform work at the moment.
  500. //
  501. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK)
  502. {
  503. DPFX(DPFPREP, 0, "Cannot change thread count while a thread is in a call to DoWork!");
  504. hr = DPNERR_NOTALLOWED;
  505. goto Exit;
  506. }
  507. #ifdef DPNBUILD_ONLYONETHREAD
  508. DPFX(DPFPREP, 0, "Not changing thread count to %u!", dwNumThreads);
  509. hr = DPNERR_UNSUPPORTED;
  510. #else // ! DPNBUILD_ONLYONETHREAD
  511. //
  512. // See if another thread is already changing the thread count. If so, wait
  513. // until they're done, unless this is a thread pool thread in the middle of
  514. // a CREATE_THREAD or DESTROY_THREAD indication.
  515. //
  516. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_THREADCOUNTCHANGING)
  517. {
  518. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  519. if ((pWorkerThread != NULL) && (! pWorkerThread->fThreadIndicated))
  520. {
  521. //
  522. // This is a thread pool thread that isn't marked as indicated to
  523. // the user, i.e. it's before CREATE_THREAD returned or it's after
  524. // the DESTROY_THREAD has started to be indicated.
  525. //
  526. DPFX(DPFPREP, 0, "Cannot change thread count from a thread pool thread in CREATE_THREAD or DESTROY_THREAD callback!");
  527. hr = DPNERR_NOTALLOWED;
  528. goto Exit;
  529. }
  530. //
  531. // Otherwise, wait for the previous thread to finish.
  532. //
  533. do
  534. {
  535. DNASSERT(pDPTPObject->lNumThreadCountChangeWaiters >= 0);
  536. pDPTPObject->lNumThreadCountChangeWaiters++;
  537. DPFX(DPFPREP, 1, "Waiting for thread count change to complete (waiters = %i).",
  538. pDPTPObject->lNumThreadCountChangeWaiters);
  539. //
  540. // Drop the lock while we wait.
  541. //
  542. DNLeaveCriticalSection(&pDPTPObject->csLock);
  543. DNWaitForSingleObject(pDPTPObject->hThreadCountChangeComplete, INFINITE);
  544. //
  545. // Retake the lock and see if we can move on.
  546. //
  547. DNEnterCriticalSection(&pDPTPObject->csLock);
  548. DNASSERT(pDPTPObject->lNumThreadCountChangeWaiters > 0);
  549. pDPTPObject->lNumThreadCountChangeWaiters--;
  550. }
  551. while (pDPTPObject->dwFlags & DPTPOBJECTFLAG_THREADCOUNTCHANGING);
  552. //
  553. // It's safe to proceed now.
  554. //
  555. DPFX(DPFPREP, 1, "Thread count change completed, continuing.");
  556. //
  557. // The user would need to be doing something spectacularly silly if
  558. // we're no longer initialized, or another thread is now calling
  559. // DoWork. We'll crash in retail, assert in debug.
  560. //
  561. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_INITIALIZED);
  562. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK);
  563. }
  564. #ifdef DPNBUILD_MANDATORYTHREADS
  565. //
  566. // Make sure we're not stopping all threads if there are any mandatory
  567. // threads.
  568. //
  569. if ((dwNumThreads == 0) && (pDPTPObject->dwMandatoryThreadCount > 0))
  570. {
  571. DPFX(DPFPREP, 0, "Cannot set number of threads to 0 because there is already at least one mandatory thread!");
  572. hr = DPNERR_NOTALLOWED;
  573. goto Exit;
  574. }
  575. #endif // DPNBUILD_MANDATORYTHREADS
  576. //
  577. // If the thread count really did change, start or stop the right number of
  578. // threads for all processors or the specific processor.
  579. //
  580. if (dwProcessorNum == -1)
  581. {
  582. if (dwNumThreads != pDPTPObject->dwTotalUserThreadCount)
  583. {
  584. if (dwNumThreads != pDPTPObject->dwTotalDesiredWorkThreadCount)
  585. {
  586. if ((dwNumThreads != 0) ||
  587. (pDPTPObject->dwTotalUserThreadCount != -1) ||
  588. (pDPTPObject->dwTotalDesiredWorkThreadCount != -1))
  589. {
  590. //
  591. // Prevent the user from trying to reduce the total thread
  592. // count from a worker thread.
  593. //
  594. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  595. if (pWorkerThread != NULL)
  596. {
  597. DWORD dwNumThreadsPerProcessor;
  598. DWORD dwExtraThreads;
  599. DWORD dwTemp;
  600. //
  601. // Make sure the thread count for any individual
  602. // processor isn't shrinking.
  603. //
  604. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  605. dwNumThreadsPerProcessor = dwNumThreads;
  606. dwExtraThreads = 0;
  607. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  608. dwNumThreadsPerProcessor = dwNumThreads / NUM_CPUS(pDPTPObject);
  609. dwExtraThreads = dwNumThreads % NUM_CPUS(pDPTPObject);
  610. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  611. for (dwTemp = 0; dwTemp < NUM_CPUS(pDPTPObject); dwTemp++)
  612. {
  613. dwDelta = dwNumThreadsPerProcessor - (WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp))->dwNumRunningThreads;
  614. if (dwTemp < dwExtraThreads)
  615. {
  616. dwDelta++;
  617. }
  618. if ((int) dwDelta < 0)
  619. {
  620. DPFX(DPFPREP, 0, "Cannot reduce thread count from a thread pool thread (processor %u)!",
  621. dwTemp);
  622. hr = DPNERR_NOTALLOWED;
  623. goto Exit;
  624. }
  625. }
  626. }
  627. //
  628. // Drop the lock while changing the thread count to prevent
  629. // deadlocks. Set the flag to alert other threads while
  630. // we're doing this.
  631. //
  632. DNASSERT(! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_THREADCOUNTCHANGING));
  633. pDPTPObject->dwFlags |= DPTPOBJECTFLAG_THREADCOUNTCHANGING;
  634. fSetThreadCountChanging = TRUE;
  635. DNLeaveCriticalSection(&pDPTPObject->csLock);
  636. //
  637. // Actually set the total number of threads.
  638. //
  639. hr = SetTotalNumberOfThreads(pDPTPObject, dwNumThreads);
  640. //
  641. // Retake the lock. We'll clear the alert flag and release
  642. // any waiting threads at the bottom.
  643. //
  644. DNEnterCriticalSection(&pDPTPObject->csLock);
  645. if (hr != DPN_OK)
  646. {
  647. DPFX(DPFPREP, 0, "Couldn't set total number of threads!");
  648. goto Exit;
  649. }
  650. pDPTPObject->dwTotalUserThreadCount = dwNumThreads;
  651. }
  652. else
  653. {
  654. DPFX(DPFPREP, 1, "No threads running, no change necessary.");
  655. pDPTPObject->dwTotalUserThreadCount = 0;
  656. }
  657. }
  658. else
  659. {
  660. DPFX(DPFPREP, 1, "Correct total number of threads (%u) already running.", dwNumThreads);
  661. pDPTPObject->dwTotalUserThreadCount = dwNumThreads;
  662. }
  663. }
  664. else
  665. {
  666. DPFX(DPFPREP, 1, "Total thread count unchanged (%u).", dwNumThreads);
  667. }
  668. }
  669. else
  670. {
  671. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwProcessorNum);
  672. dwDelta = dwNumThreads - pWorkQueue->dwNumRunningThreads;
  673. if (dwDelta == 0)
  674. {
  675. if (pDPTPObject->dwTotalUserThreadCount == -1)
  676. {
  677. if (pDPTPObject->dwTotalDesiredWorkThreadCount != -1)
  678. {
  679. DPFX(DPFPREP, 1, "Correct number of threads (%u) already running on processor.", dwNumThreads);
  680. pDPTPObject->dwTotalUserThreadCount = pDPTPObject->dwTotalDesiredWorkThreadCount;
  681. }
  682. else
  683. {
  684. DNASSERT(dwNumThreads == 0);
  685. DPFX(DPFPREP, 1, "No threads are running on processor, no change necessary.");
  686. pDPTPObject->dwTotalUserThreadCount = 0;
  687. }
  688. }
  689. else
  690. {
  691. DPFX(DPFPREP, 1, "Correct number of threads (%u) already set for processor.", dwNumThreads);
  692. }
  693. }
  694. else
  695. {
  696. //
  697. // Drop the lock while changing the thread count to prevent
  698. // deadlocks. Set the flag to alert other threads while we're
  699. // doing this.
  700. //
  701. DNASSERT(! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_THREADCOUNTCHANGING));
  702. pDPTPObject->dwFlags |= DPTPOBJECTFLAG_THREADCOUNTCHANGING;
  703. fSetThreadCountChanging = TRUE;
  704. DNLeaveCriticalSection(&pDPTPObject->csLock);
  705. if ((int) dwDelta > 0)
  706. {
  707. //
  708. // We need to add threads.
  709. //
  710. hr = StartThreads(pWorkQueue, dwDelta);
  711. if (hr != DPN_OK)
  712. {
  713. DPFX(DPFPREP, 0, "Couldn't start %u threads for processor!", dwDelta);
  714. //
  715. // Retake the lock before bailing.
  716. //
  717. DNEnterCriticalSection(&pDPTPObject->csLock);
  718. goto Exit;
  719. }
  720. }
  721. else
  722. {
  723. //
  724. // Prevent the user from trying to reduce the processor's
  725. // thread count from a worker thread (for any processor).
  726. //
  727. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  728. if (pWorkerThread != NULL)
  729. {
  730. DPFX(DPFPREP, 0, "Cannot reduce thread count from a thread pool thread!");
  731. //
  732. // Retake the lock before bailing.
  733. //
  734. DNEnterCriticalSection(&pDPTPObject->csLock);
  735. hr = DPNERR_NOTALLOWED;
  736. goto Exit;
  737. }
  738. //
  739. // We need to remove {absolute value of delta} threads.
  740. //
  741. hr = StopThreads(pWorkQueue, ((int) dwDelta * -1));
  742. if (hr != DPN_OK)
  743. {
  744. DPFX(DPFPREP, 0, "Couldn't stop %u threads for processor!", ((int) dwDelta * -1));
  745. //
  746. // Retake the lock before bailing.
  747. //
  748. DNEnterCriticalSection(&pDPTPObject->csLock);
  749. goto Exit;
  750. }
  751. }
  752. DNASSERT(pWorkQueue->dwNumRunningThreads == dwNumThreads);
  753. //
  754. // Retake the lock. We'll clear the alert flag and release any
  755. // waiting threads at the bottom.
  756. //
  757. DNEnterCriticalSection(&pDPTPObject->csLock);
  758. if (pDPTPObject->dwTotalUserThreadCount == -1)
  759. {
  760. pDPTPObject->dwTotalUserThreadCount = dwDelta;
  761. if (pDPTPObject->dwTotalDesiredWorkThreadCount != -1)
  762. {
  763. pDPTPObject->dwTotalUserThreadCount += pDPTPObject->dwTotalDesiredWorkThreadCount;
  764. }
  765. }
  766. else
  767. {
  768. pDPTPObject->dwTotalUserThreadCount += dwDelta;
  769. }
  770. DNASSERT(pDPTPObject->dwTotalUserThreadCount != -1);
  771. }
  772. }
  773. hr = DPN_OK;
  774. #endif // ! DPNBUILD_ONLYONETHREAD
  775. Exit:
  776. //
  777. // If we start changing the thread count, clear the flag, and release any
  778. // threads waiting on us (they'll block until we drop the lock again
  779. // shortly).
  780. //
  781. if (fSetThreadCountChanging)
  782. {
  783. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_THREADCOUNTCHANGING);
  784. pDPTPObject->dwFlags &= ~DPTPOBJECTFLAG_THREADCOUNTCHANGING;
  785. fSetThreadCountChanging = FALSE;
  786. if (pDPTPObject->lNumThreadCountChangeWaiters > 0)
  787. {
  788. DPFX(DPFPREP, 1, "Releasing %i waiters.",
  789. pDPTPObject->lNumThreadCountChangeWaiters);
  790. DNReleaseSemaphore(pDPTPObject->hThreadCountChangeComplete,
  791. pDPTPObject->lNumThreadCountChangeWaiters,
  792. NULL);
  793. }
  794. }
  795. DNLeaveCriticalSection(&pDPTPObject->csLock);
  796. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  797. return hr;
  798. } // DPTP_SetThreadCount
  799. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_LIBINTERFACE
  800. #undef DPF_MODNAME
  801. #define DPF_MODNAME "DPTP_DoWork"
  802. //=============================================================================
  803. // DPTP_DoWork
  804. //-----------------------------------------------------------------------------
  805. //
  806. // Description: Performs any work that is currently scheduled. This allows
  807. // DirectPlay to operate without any threads of its own. It is
  808. // expected that this will be called frequently and at regular
  809. // intervals so that time critical operations can be performed
  810. // with reasonable accuracy.
  811. //
  812. // This method will return DPN_OK when no additional work is
  813. // immediately available. If the allowed time slice is not
  814. // INFINITE, this method will return DPNSUCCESS_PENDING if the
  815. // time limit is exceeded but there is still work remaining. If
  816. // the allowed time slice is 0, only the first work item (if any)
  817. // will be performed. The allowed time slice must be less than
  818. // 60,000 milliseconds (1 minute) if it is not INFINITE.
  819. //
  820. // This method cannot be called unless the thread count has
  821. // been set to 0. It will return DPNERR_NOTREADY if there are
  822. // threads currently active.
  823. //
  824. // If an attempt is made to call this method by more than one
  825. // thread simultaneously, recursively, or within a DirectPlay
  826. // callback, DPNERR_NOTALLOWED is returned.
  827. //
  828. //
  829. //
  830. // Arguments:
  831. // xxx pInterface - Pointer to interface.
  832. // DWORD dwAllowedTimeSlice - The maximum number of milliseconds to perform
  833. // work, or INFINITE to allow all immediately
  834. // available items to be executed.
  835. // DWORD dwFlags - Flags to use when performing work.
  836. //
  837. // Returns: HRESULT
  838. // DPN_OK - Performing the work was successful.
  839. // DPNSUCCESS_PENDING - No errors occurred, but there is work that could
  840. // not be accomplished due to the time limit.
  841. // DPNERR_INVALIDFLAGS - Invalid flags were specified.
  842. // DPNERR_NOTALLOWED - This method is already being called by some
  843. // thread.
  844. // DPNERR_NOTREADY - The thread count has not been set to 0.
  845. // DPNERR_UNINITIALIZED - The interface has not yet been initialized.
  846. //=============================================================================
  847. #ifdef DPNBUILD_LIBINTERFACE
  848. STDMETHODIMP DPTP_DoWork(const DWORD dwAllowedTimeSlice,
  849. const DWORD dwFlags)
  850. #else // ! DPNBUILD_LIBINTERFACE
  851. STDMETHODIMP DPTP_DoWork(IDirectPlay8ThreadPool * pInterface,
  852. const DWORD dwAllowedTimeSlice,
  853. const DWORD dwFlags)
  854. #endif // ! DPNBUILD_LIBINTERFACE
  855. {
  856. HRESULT hr;
  857. DPTHREADPOOLOBJECT * pDPTPObject;
  858. DWORD dwMaxDoWorkTime;
  859. #ifndef DPNBUILD_ONLYONEPROCESSOR
  860. DWORD dwCPU;
  861. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  862. BOOL fRemainingItems;
  863. #ifdef DPNBUILD_LIBINTERFACE
  864. DPFX(DPFPREP, 8, "Parameters: (%i, 0x%x)",
  865. dwAllowedTimeSlice, dwFlags);
  866. #else // ! DPNBUILD_LIBINTERFACE
  867. DPFX(DPFPREP, 8, "Parameters: (0x%p, %i, 0x%x)",
  868. pInterface, dwAllowedTimeSlice, dwFlags);
  869. #endif // ! DPNBUILD_LIBINTERFACE
  870. #ifdef DPNBUILD_LIBINTERFACE
  871. #ifdef DPNBUILD_MULTIPLETHREADPOOLS
  872. #pragma error("Multiple thread pools support under DPNBUILD_LIBINTERFACE requires more work")
  873. #else // ! DPNBUILD_MULTIPLETHREADPOOLS
  874. pDPTPObject = g_pDPTPObject;
  875. #endif // ! DPNBUILD_MULTIPLETHREADPOOLS
  876. #else // ! DPNBUILD_LIBINTERFACE
  877. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  878. #endif // ! DPNBUILD_LIBINTERFACE
  879. DNASSERT(pDPTPObject != NULL);
  880. #ifndef DPNBUILD_NOPARAMVAL
  881. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_PARAMVALIDATION)
  882. {
  883. //
  884. // Validate parameters.
  885. //
  886. #ifdef DPNBUILD_LIBINTERFACE
  887. hr = DPTPValidateDoWork(dwAllowedTimeSlice, dwFlags);
  888. #else // ! DPNBUILD_LIBINTERFACE
  889. hr = DPTPValidateDoWork(pInterface, dwAllowedTimeSlice, dwFlags);
  890. #endif // ! DPNBUILD_LIBINTERFACE
  891. if (hr != DPN_OK)
  892. {
  893. DPF_RETURN(hr);
  894. }
  895. }
  896. #endif // ! DPNBUILD_NOPARAMVAL
  897. #ifndef DPNBUILD_LIBINTERFACE
  898. //
  899. // Check object state (note: done without object lock).
  900. //
  901. if (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_INITIALIZED))
  902. {
  903. DPFX(DPFPREP, 0, "Thread pool object not initialized!");
  904. DPF_RETURN(DPNERR_UNINITIALIZED);
  905. }
  906. #endif // ! DPNBUILD_LIBINTERFACE
  907. //
  908. // Save the time limit we need to use.
  909. //
  910. if (dwAllowedTimeSlice != INFINITE)
  911. {
  912. dwMaxDoWorkTime = GETTIMESTAMP() + dwAllowedTimeSlice;
  913. //
  914. // Make sure the timer never lands exactly on INFINITE, that value has
  915. // special meaning.
  916. //
  917. if (dwMaxDoWorkTime == INFINITE)
  918. {
  919. dwMaxDoWorkTime--;
  920. }
  921. }
  922. else
  923. {
  924. dwMaxDoWorkTime = INFINITE;
  925. }
  926. DNEnterCriticalSection(&pDPTPObject->csLock);
  927. #ifndef DPNBUILD_ONLYONETHREAD
  928. if (pDPTPObject->dwTotalUserThreadCount != 0)
  929. {
  930. DPFX(DPFPREP, 0, "Thread count must be set to 0 prior to using DoWork!");
  931. hr = DPNERR_NOTREADY;
  932. goto Failure;
  933. }
  934. #endif // ! DPNBUILD_ONLYONETHREAD
  935. //
  936. // Make sure only one person is trying to call us at a time.
  937. //
  938. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK)
  939. {
  940. DPFX(DPFPREP, 0, "DoWork cannot be performed recursively, or by multiple threads simultaneously!");
  941. hr = DPNERR_NOTALLOWED;
  942. goto Failure;
  943. }
  944. pDPTPObject->dwFlags |= DPTPOBJECTFLAG_USER_DOINGWORK;
  945. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  946. pDPTPObject->dwCurrentDoWorkThreadID = GetCurrentThreadId();
  947. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  948. DNLeaveCriticalSection(&pDPTPObject->csLock);
  949. //
  950. // Set the recursion depth.
  951. //
  952. #ifdef DPNBUILD_ONLYONETHREAD
  953. DNASSERT(pDPTPObject->dwWorkRecursionCount == 0);
  954. pDPTPObject->dwWorkRecursionCount = 1;
  955. #else // ! DPNBUILD_ONLYONETHREAD
  956. DNASSERT((DWORD) ((DWORD_PTR) (TlsGetValue(pDPTPObject->dwWorkRecursionCountTlsIndex))) == 0);
  957. TlsSetValue(pDPTPObject->dwWorkRecursionCountTlsIndex,
  958. (PVOID) ((DWORD_PTR) 1));
  959. #endif // ! DPNBUILD_ONLYONETHREAD
  960. //
  961. // Actually perform the work.
  962. //
  963. #ifdef DPNBUILD_ONLYONEPROCESSOR
  964. DoWork(&pDPTPObject->WorkQueue, dwMaxDoWorkTime);
  965. fRemainingItems = ! DNIsNBQueueEmpty(pDPTPObject->WorkQueue.pvNBQueueWorkItems);
  966. #else // ! DPNBUILD_ONLYONEPROCESSOR
  967. //
  968. // Since we're in DoWork mode, technically only one CPU work queue needs to
  969. // be used, but it's possible that work got scheduled to one of the other
  970. // CPUs. Rather than trying to figure out the logic of when and how to
  971. // move everything from that queue to the first CPU's queue, we will just
  972. // process all of them every time.
  973. //
  974. fRemainingItems = FALSE;
  975. for(dwCPU = 0; dwCPU < NUM_CPUS(pDPTPObject); dwCPU++)
  976. {
  977. DoWork(WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU), dwMaxDoWorkTime);
  978. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  979. #pragma BUGBUG(vanceo, "Find equivalent for I/O completion ports")
  980. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  981. fRemainingItems |= ! DNIsNBQueueEmpty((WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU))->pvNBQueueWorkItems);
  982. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  983. //
  984. // Even if the time has expired on this CPU, we will continue to the
  985. // rest. That way, we execute at least one item for every CPU queue
  986. // each time through (to prevent total starvation). This may make us
  987. // go even farther over the time limit, but hopefully not by much. Of
  988. // course, it's a bit silly to be using DoWork mode on a multiprocessor
  989. // machine in the first place.
  990. //
  991. }
  992. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  993. //
  994. // Decrement the recursion count and allow other callers again.
  995. //
  996. #ifdef DPNBUILD_ONLYONETHREAD
  997. DNASSERT(pDPTPObject->dwWorkRecursionCount == 1);
  998. pDPTPObject->dwWorkRecursionCount = 0;
  999. #else // ! DPNBUILD_ONLYONETHREAD
  1000. DNASSERT((DWORD) ((DWORD_PTR) (TlsGetValue(pDPTPObject->dwWorkRecursionCountTlsIndex))) == 1);
  1001. TlsSetValue(pDPTPObject->dwWorkRecursionCountTlsIndex,
  1002. (PVOID) ((DWORD_PTR) 0));
  1003. #endif // ! DPNBUILD_ONLYONETHREAD
  1004. DNEnterCriticalSection(&pDPTPObject->csLock);
  1005. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK);
  1006. pDPTPObject->dwFlags &= ~DPTPOBJECTFLAG_USER_DOINGWORK;
  1007. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  1008. DNASSERT(pDPTPObject->dwCurrentDoWorkThreadID == GetCurrentThreadId());
  1009. pDPTPObject->dwCurrentDoWorkThreadID = 0;
  1010. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  1011. DNLeaveCriticalSection(&pDPTPObject->csLock);
  1012. //
  1013. // Return the appropriate error code.
  1014. //
  1015. if (fRemainingItems)
  1016. {
  1017. DPFX(DPFPREP, 7, "Some items remain unprocessed.");
  1018. hr = DPNSUCCESS_PENDING;
  1019. }
  1020. else
  1021. {
  1022. hr = DPN_OK;
  1023. }
  1024. Exit:
  1025. DPFX(DPFPREP, 8, "Returning: [0x%lx]", hr);
  1026. return hr;
  1027. Failure:
  1028. DNLeaveCriticalSection(&pDPTPObject->csLock);
  1029. goto Exit;
  1030. } // DPTP_DoWork
  1031. #pragma TODO(vanceo, "Make validation for private interface a build flag (off by default)")
  1032. #undef DPF_MODNAME
  1033. #define DPF_MODNAME "DPTPW_QueueWorkItem"
  1034. //=============================================================================
  1035. // DPTPW_QueueWorkItem
  1036. //-----------------------------------------------------------------------------
  1037. //
  1038. // Description: Queues a new work item for processing.
  1039. //
  1040. // Arguments:
  1041. // xxx pInterface - Pointer to interface.
  1042. // DWORD dwCPU - CPU queue on which item is to be
  1043. // placed, or -1 for any.
  1044. // PFNDPTNWORKCALLBACK pfnWorkCallback - Callback to execute as soon as
  1045. // possible.
  1046. // PVOID pvCallbackContext - User specified context to pass to
  1047. // callback.
  1048. // DWORD dwFlags - Flags to use when queueing.
  1049. //
  1050. // Returns: HRESULT
  1051. // DPN_OK - Queuing the work item was successful.
  1052. // DPNERR_OUTOFMEMORY - Not enough memory to queue the work item.
  1053. //=============================================================================
  1054. STDMETHODIMP DPTPW_QueueWorkItem(IDirectPlay8ThreadPoolWork * pInterface,
  1055. const DWORD dwCPU,
  1056. const PFNDPTNWORKCALLBACK pfnWorkCallback,
  1057. PVOID const pvCallbackContext,
  1058. const DWORD dwFlags)
  1059. {
  1060. HRESULT hr;
  1061. DPTHREADPOOLOBJECT * pDPTPObject;
  1062. DPTPWORKQUEUE * pWorkQueue;
  1063. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %i, 0x%p, 0x%p, 0x%x)",
  1064. pInterface, dwCPU, pfnWorkCallback, pvCallbackContext, dwFlags);
  1065. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1066. DNASSERT(pDPTPObject != NULL);
  1067. //
  1068. // Figure out which CPU queue to use.
  1069. //
  1070. DNASSERT((dwCPU == -1) || (dwCPU < NUM_CPUS(pDPTPObject)));
  1071. pWorkQueue = GET_OR_CHOOSE_WORKQUEUE(pDPTPObject, dwCPU);
  1072. //
  1073. // Call the implementation function.
  1074. //
  1075. if (! QueueWorkItem(pWorkQueue, pfnWorkCallback, pvCallbackContext))
  1076. {
  1077. hr = DPNERR_OUTOFMEMORY;
  1078. }
  1079. else
  1080. {
  1081. hr = DPN_OK;
  1082. }
  1083. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  1084. return hr;
  1085. } // DPTPW_QueueWorkItem
  1086. #undef DPF_MODNAME
  1087. #define DPF_MODNAME "DPTPW_ScheduleTimer"
  1088. //=============================================================================
  1089. // DPTPW_ScheduleTimer
  1090. //-----------------------------------------------------------------------------
  1091. //
  1092. // Description: Schedules a new work item for some point in the future.
  1093. //
  1094. // Arguments:
  1095. // xxx pInterface - Pointer to interface.
  1096. // DWORD dwCPU - CPU on which item is to be scheduled,
  1097. // or -1 for any.
  1098. // DWORD dwDelay - How much time should elapsed before
  1099. // executing the work item, in ms.
  1100. // PFNDPTNWORKCALLBACK pfnWorkCallback - Callback to execute when timer
  1101. // elapses.
  1102. // PVOID pvCallbackContext - User specified context to pass to
  1103. // callback.
  1104. // void ** ppvTimerData - Place to store pointer to data for
  1105. // timer so that it can be cancelled.
  1106. // UINT * puiTimerUnique - Place to store uniqueness value for
  1107. // timer so that it can be cancelled.
  1108. //
  1109. // Returns: HRESULT
  1110. // DPN_OK - Scheduling the timer was successful.
  1111. // DPNERR_OUTOFMEMORY - Not enough memory to schedule the timer.
  1112. //=============================================================================
  1113. STDMETHODIMP DPTPW_ScheduleTimer(IDirectPlay8ThreadPoolWork * pInterface,
  1114. const DWORD dwCPU,
  1115. const DWORD dwDelay,
  1116. const PFNDPTNWORKCALLBACK pfnWorkCallback,
  1117. PVOID const pvCallbackContext,
  1118. void ** const ppvTimerData,
  1119. UINT * const puiTimerUnique,
  1120. const DWORD dwFlags)
  1121. {
  1122. HRESULT hr;
  1123. DPTHREADPOOLOBJECT * pDPTPObject;
  1124. DPTPWORKQUEUE * pWorkQueue;
  1125. DPFX(DPFPREP, 8, "Parameters: (0x%p, %i, %u, 0x%p, 0x%p, 0x%p, 0x%p, 0x%x)",
  1126. pInterface, dwCPU, dwDelay, pfnWorkCallback, pvCallbackContext, ppvTimerData, puiTimerUnique, dwFlags);
  1127. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1128. DNASSERT(pDPTPObject != NULL);
  1129. //
  1130. // Figure out which CPU queue to use.
  1131. //
  1132. DNASSERT((dwCPU == -1) || (dwCPU < NUM_CPUS(pDPTPObject)));
  1133. pWorkQueue = GET_OR_CHOOSE_WORKQUEUE(pDPTPObject, dwCPU);
  1134. //
  1135. // Call the implementation function.
  1136. //
  1137. if (! ScheduleTimer(pWorkQueue,
  1138. dwDelay,
  1139. pfnWorkCallback,
  1140. pvCallbackContext,
  1141. ppvTimerData,
  1142. puiTimerUnique))
  1143. {
  1144. hr = DPNERR_OUTOFMEMORY;
  1145. }
  1146. else
  1147. {
  1148. hr = DPN_OK;
  1149. }
  1150. DPFX(DPFPREP, 8, "Returning: [0x%lx]", hr);
  1151. return hr;
  1152. } // DPTPW_ScheduleTimer
  1153. #undef DPF_MODNAME
  1154. #define DPF_MODNAME "DPTPW_StartTrackingFileIo"
  1155. //=============================================================================
  1156. // DPTPW_StartTrackingFileIo
  1157. //-----------------------------------------------------------------------------
  1158. //
  1159. // Description: Starts tracking overlapped I/O for a given file handle on
  1160. // the specified CPU (or all CPUs). The handle is not duplicated
  1161. // and it should remain valid until
  1162. // IDirectPlay8ThreadPoolWork::StopTrackingFileIo is called.
  1163. //
  1164. // This method is not available on Windows CE because it does
  1165. // not support overlapped I/O.
  1166. //
  1167. // Arguments:
  1168. // xxx pInterface - Pointer to interface.
  1169. // DWORD dwCPU - CPU with which I/O is to be tracked, or -1 for all.
  1170. // HANDLE hFile - Handle of file to track.
  1171. // DWORD dwFlags - Flags to use when starting to track file I/O.
  1172. //
  1173. // Returns: HRESULT
  1174. // DPN_OK - Starting tracking for the file was successful.
  1175. // DPNERR_ALREADYREGISTERED - The specified file handle is already being
  1176. // tracked.
  1177. // DPNERR_OUTOFMEMORY - Not enough memory to track the file.
  1178. //=============================================================================
  1179. STDMETHODIMP DPTPW_StartTrackingFileIo(IDirectPlay8ThreadPoolWork * pInterface,
  1180. const DWORD dwCPU,
  1181. const HANDLE hFile,
  1182. const DWORD dwFlags)
  1183. {
  1184. #ifdef WINCE
  1185. DPFX(DPFPREP, 0, "Overlapped I/O not supported on Windows CE!", 0);
  1186. return DPNERR_UNSUPPORTED;
  1187. #else // ! WINCE
  1188. HRESULT hr;
  1189. DPTHREADPOOLOBJECT * pDPTPObject;
  1190. DWORD dwTemp;
  1191. DPTPWORKQUEUE * pWorkQueue;
  1192. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %i, 0x%p, 0x%x)",
  1193. pInterface, dwCPU, hFile, dwFlags);
  1194. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1195. DNASSERT(pDPTPObject != NULL);
  1196. //
  1197. // Call the implementation function for all relevant CPUs.
  1198. //
  1199. if (dwCPU == -1)
  1200. {
  1201. for(dwTemp = 0; dwTemp < NUM_CPUS(pDPTPObject); dwTemp++)
  1202. {
  1203. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp);
  1204. hr = StartTrackingFileIo(pWorkQueue, hFile);
  1205. if (hr != DPN_OK)
  1206. {
  1207. //
  1208. // Stop tracking the file on all CPUs where we had already
  1209. // succeeded. Ignore any error the function might return.
  1210. //
  1211. while (dwTemp > 0)
  1212. {
  1213. dwTemp--;
  1214. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp);
  1215. StopTrackingFileIo(pWorkQueue, hFile);
  1216. }
  1217. break;
  1218. }
  1219. }
  1220. }
  1221. else
  1222. {
  1223. DNASSERT(dwCPU < NUM_CPUS(pDPTPObject));
  1224. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU);
  1225. hr = StartTrackingFileIo(pWorkQueue, hFile);
  1226. }
  1227. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  1228. return hr;
  1229. #endif // ! WINCE
  1230. } // DPTPW_StartTrackingFileIo
  1231. #undef DPF_MODNAME
  1232. #define DPF_MODNAME "DPTPW_StopTrackingFileIo"
  1233. //=============================================================================
  1234. // DPTPW_StopTrackingFileIo
  1235. //-----------------------------------------------------------------------------
  1236. //
  1237. // Description: Stops tracking overlapped I/O for a given file handle on
  1238. // the specified CPU (or all CPUs).
  1239. //
  1240. // This method is not available on Windows CE because it does
  1241. // not support overlapped I/O.
  1242. //
  1243. // Arguments:
  1244. // xxx pInterface - Pointer to interface.
  1245. // DWORD dwCPU - CPU with which I/O was tracked, or -1 for all.
  1246. // HANDLE hFile - Handle of file to stop tracking.
  1247. // DWORD dwFlags - Flags to use when no turning off file I/O tracking.
  1248. //
  1249. // Returns: HRESULT
  1250. // DPN_OK - Stopping tracking for the file was successful.
  1251. // DPNERR_INVALIDHANDLE - File handle was not being tracked.
  1252. //=============================================================================
  1253. STDMETHODIMP DPTPW_StopTrackingFileIo(IDirectPlay8ThreadPoolWork * pInterface,
  1254. const DWORD dwCPU,
  1255. const HANDLE hFile,
  1256. const DWORD dwFlags)
  1257. {
  1258. #ifdef WINCE
  1259. DPFX(DPFPREP, 0, "Overlapped I/O not supported on Windows CE!", 0);
  1260. return DPNERR_UNSUPPORTED;
  1261. #else // ! WINCE
  1262. HRESULT hr;
  1263. DPTHREADPOOLOBJECT * pDPTPObject;
  1264. DWORD dwTemp;
  1265. DPTPWORKQUEUE * pWorkQueue;
  1266. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %i, 0x%p, 0x%x)",
  1267. pInterface, dwCPU, hFile, dwFlags);
  1268. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1269. DNASSERT(pDPTPObject != NULL);
  1270. //
  1271. // Call the implementation function for all relevant CPUs.
  1272. //
  1273. if (dwCPU == -1)
  1274. {
  1275. for(dwTemp = 0; dwTemp < NUM_CPUS(pDPTPObject); dwTemp++)
  1276. {
  1277. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp);
  1278. hr = StopTrackingFileIo(pWorkQueue, hFile);
  1279. if (hr != DPN_OK)
  1280. {
  1281. break;
  1282. }
  1283. }
  1284. }
  1285. else
  1286. {
  1287. DNASSERT(dwCPU < NUM_CPUS(pDPTPObject));
  1288. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU);
  1289. hr = StopTrackingFileIo(pWorkQueue, hFile);
  1290. }
  1291. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  1292. return hr;
  1293. #endif // ! WINCE
  1294. } // DPTPW_StopTrackingFileIo
  1295. #undef DPF_MODNAME
  1296. #define DPF_MODNAME "DPTPW_CreateOverlapped"
  1297. //=============================================================================
  1298. // DPTPW_CreateOverlapped
  1299. //-----------------------------------------------------------------------------
  1300. //
  1301. // Description: Creates an overlapped structure for an asynchronous I/O
  1302. // operation so it can be monitored for completion.
  1303. //
  1304. // If this implementation is using I/O completion ports, the
  1305. // caller should be prepared for the work callback function to be
  1306. // invoked as soon as he or she calls the intended asynchronous
  1307. // file function. Otherwise, he or she must call
  1308. // IDirectPlay8ThreadPoolWork::SubmitIoOperation.
  1309. //
  1310. // If the intended asynchronous file function fails immediately
  1311. // and the overlapped structure will never be completed
  1312. // asynchronously, the caller must return the unused overlapped
  1313. // structure with IDirectPlay8ThreadPoolWork::ReleaseOverlapped.
  1314. //
  1315. // This method is not available on Windows CE because it does
  1316. // not support overlapped I/O.
  1317. //
  1318. // Arguments:
  1319. // xxx pInterface - Pointer to interface.
  1320. // DWORD dwCPU - CPU with which I/O is to be
  1321. // monitored, or -1 for any.
  1322. // PFNDPTNWORKCALLBACK pfnWorkCallback - Callback to execute when operation
  1323. // completes.
  1324. // PVOID pvCallbackContext - User specified context to pass to
  1325. // callback.
  1326. // OVERLAPPED * pOverlapped - Pointer to overlapped structure used
  1327. // by OS.
  1328. // DWORD dwFlags - Flags to use when submitting I/O.
  1329. //
  1330. // Returns: HRESULT
  1331. // DPN_OK - Creating the structure was successful.
  1332. // DPNERR_OUTOFMEMORY - Not enough memory to create the structure.
  1333. //=============================================================================
  1334. STDMETHODIMP DPTPW_CreateOverlapped(IDirectPlay8ThreadPoolWork * pInterface,
  1335. const DWORD dwCPU,
  1336. const PFNDPTNWORKCALLBACK pfnWorkCallback,
  1337. PVOID const pvCallbackContext,
  1338. OVERLAPPED ** const ppOverlapped,
  1339. const DWORD dwFlags)
  1340. {
  1341. #ifdef WINCE
  1342. DPFX(DPFPREP, 0, "Overlapped I/O not supported on Windows CE!", 0);
  1343. return DPNERR_UNSUPPORTED;
  1344. #else // ! WINCE
  1345. HRESULT hr;
  1346. DPTHREADPOOLOBJECT * pDPTPObject;
  1347. DPTPWORKQUEUE * pWorkQueue;
  1348. CWorkItem * pWorkItem;
  1349. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %i, 0x%p, 0x%p, 0x%p, 0x%x)",
  1350. pInterface, dwCPU, pfnWorkCallback, pvCallbackContext, ppOverlapped, dwFlags);
  1351. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1352. DNASSERT(pDPTPObject != NULL);
  1353. //
  1354. // Figure out which CPU queue to use.
  1355. //
  1356. DNASSERT((dwCPU == -1) || (dwCPU < NUM_CPUS(pDPTPObject)));
  1357. pWorkQueue = GET_OR_CHOOSE_WORKQUEUE(pDPTPObject, dwCPU);
  1358. //
  1359. // Call the implementation function.
  1360. //
  1361. pWorkItem = CreateOverlappedIoWorkItem(pWorkQueue,
  1362. pfnWorkCallback,
  1363. pvCallbackContext);
  1364. if (pWorkItem == NULL)
  1365. {
  1366. hr = DPNERR_OUTOFMEMORY;
  1367. }
  1368. else
  1369. {
  1370. DNASSERT(ppOverlapped != NULL);
  1371. *ppOverlapped = &pWorkItem->m_Overlapped;
  1372. hr = DPN_OK;
  1373. }
  1374. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  1375. return hr;
  1376. #endif // ! WINCE
  1377. } // DPTPW_CreateOverlapped
  1378. #undef DPF_MODNAME
  1379. #define DPF_MODNAME "DPTPW_SubmitIoOperation"
  1380. //=============================================================================
  1381. // DPTPW_SubmitIoOperation
  1382. //-----------------------------------------------------------------------------
  1383. //
  1384. // Description: Submits an overlapped structure for an asynchronous I/O
  1385. // operation so it can be monitored for completion.
  1386. //
  1387. // If this implementation is using I/O completion ports, this
  1388. // method does not need to be used. Otherwise, the caller should
  1389. // be prepared for the work callback function to be invoked even
  1390. // before this method returns.
  1391. //
  1392. // The caller must pass a valid OVERLAPPED structure that was
  1393. // allocated using IDirectPlay8ThreadPoolWork::CreateOverlapped.
  1394. //
  1395. // This method is not available on Windows CE because it does
  1396. // not support overlapped I/O.
  1397. //
  1398. // Arguments:
  1399. // xxx pInterface - Pointer to interface.
  1400. // OVERLAPPED * pOverlapped - Pointer to overlapped structure to monitor.
  1401. // DWORD dwFlags - Flags to use when submitting I/O.
  1402. //
  1403. // Returns: HRESULT
  1404. // DPN_OK - Submitting the I/O operation was successful.
  1405. //=============================================================================
  1406. STDMETHODIMP DPTPW_SubmitIoOperation(IDirectPlay8ThreadPoolWork * pInterface,
  1407. OVERLAPPED * const pOverlapped,
  1408. const DWORD dwFlags)
  1409. {
  1410. #ifdef WINCE
  1411. DPFX(DPFPREP, 0, "Overlapped I/O not supported on Windows CE!", 0);
  1412. return DPNERR_UNSUPPORTED;
  1413. #else // ! WINCE
  1414. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  1415. DPFX(DPFPREP, 0, "Implementation using I/O completion ports, SubmitIoOperation should not be used!", 0);
  1416. return DPNERR_INVALIDVERSION;
  1417. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  1418. DPTHREADPOOLOBJECT * pDPTPObject;
  1419. CWorkItem * pWorkItem;
  1420. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%p, 0x%x)",
  1421. pInterface, pOverlapped, dwFlags);
  1422. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1423. DNASSERT(pDPTPObject != NULL);
  1424. pWorkItem = CONTAINING_OBJECT(pOverlapped, CWorkItem, m_Overlapped);
  1425. DNASSERT(pWorkItem->IsValid());
  1426. //
  1427. // Call the implementation function.
  1428. //
  1429. SubmitIoOperation(pWorkItem->m_pWorkQueue, pWorkItem);
  1430. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [DPN_OK]");
  1431. return DPN_OK;
  1432. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  1433. #endif // ! WINCE
  1434. } // DPTPW_SubmitIoOperation
  1435. #undef DPF_MODNAME
  1436. #define DPF_MODNAME "DPTPW_ReleaseOverlapped"
  1437. //=============================================================================
  1438. // DPTPW_ReleaseOverlapped
  1439. //-----------------------------------------------------------------------------
  1440. //
  1441. // Description: Returns an unused overlapped structure previously created by
  1442. // IDirectPlay8ThreadPoolWork::CreateOverlapped. This should only
  1443. // be called if the overlapped I/O will never complete
  1444. // asynchronously.
  1445. //
  1446. // This method is not available on Windows CE because it does
  1447. // not support overlapped I/O.
  1448. //
  1449. // Arguments:
  1450. // xxx pInterface - Pointer to interface.
  1451. // OVERLAPPED * pOverlapped - Pointer to overlapped structure to release.
  1452. // DWORD dwFlags - Flags to use when releasing structure.
  1453. //
  1454. // Returns: HRESULT
  1455. // DPN_OK - Releasing the I/O operation was successful.
  1456. //=============================================================================
  1457. STDMETHODIMP DPTPW_ReleaseOverlapped(IDirectPlay8ThreadPoolWork * pInterface,
  1458. OVERLAPPED * const pOverlapped,
  1459. const DWORD dwFlags)
  1460. {
  1461. #ifdef WINCE
  1462. DPFX(DPFPREP, 0, "Overlapped I/O not supported on Windows CE!", 0);
  1463. return DPNERR_UNSUPPORTED;
  1464. #else // ! WINCE
  1465. HRESULT hr;
  1466. DPTHREADPOOLOBJECT * pDPTPObject;
  1467. CWorkItem * pWorkItem;
  1468. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%p, 0x%x)",
  1469. pInterface, pOverlapped, dwFlags);
  1470. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1471. DNASSERT(pDPTPObject != NULL);
  1472. pWorkItem = CONTAINING_OBJECT(pOverlapped, CWorkItem, m_Overlapped);
  1473. DNASSERT(pWorkItem->IsValid());
  1474. //
  1475. // Call the implementation function.
  1476. //
  1477. ReleaseOverlappedIoWorkItem(pWorkItem->m_pWorkQueue, pWorkItem);
  1478. hr = DPN_OK;
  1479. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  1480. return hr;
  1481. #endif // ! WINCE
  1482. } // DPTPW_ReleaseOverlapped
  1483. #undef DPF_MODNAME
  1484. #define DPF_MODNAME "DPTPW_CancelTimer"
  1485. //=============================================================================
  1486. // DPTPW_CancelTimer
  1487. //-----------------------------------------------------------------------------
  1488. //
  1489. // Description: Attempts to cancel a timed work item. If the item is
  1490. // already in the process of completing, DPNERR_CANNOTCANCEL is
  1491. // returned, and the callback will still be (or is being) called.
  1492. // If the item could be cancelled, DPN_OK is returned and the
  1493. // callback will not be executed.
  1494. //
  1495. // Arguments:
  1496. // xxx pInterface - Pointer to interface.
  1497. // void * pvTimerData - Pointer to data for timer being cancelled.
  1498. // UINT uiTimerUnique - Uniqueness value for timer being cancelled.
  1499. // DWORD dwFlags - Flags to use when cancelling timer.
  1500. //
  1501. // Returns: HRESULT
  1502. // DPN_OK - Cancelling the timer was successful.
  1503. // DPNERR_CANNOTCANCEL - The timer could not be cancelled.
  1504. //=============================================================================
  1505. STDMETHODIMP DPTPW_CancelTimer(IDirectPlay8ThreadPoolWork * pInterface,
  1506. void * const pvTimerData,
  1507. const UINT uiTimerUnique,
  1508. const DWORD dwFlags)
  1509. {
  1510. HRESULT hr;
  1511. DPTHREADPOOLOBJECT * pDPTPObject;
  1512. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%p, %u, 0x%x)",
  1513. pInterface, pvTimerData, uiTimerUnique, dwFlags);
  1514. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1515. DNASSERT(pDPTPObject != NULL);
  1516. //
  1517. // Call the implementation function.
  1518. //
  1519. hr = CancelTimer(pvTimerData, uiTimerUnique);
  1520. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  1521. return hr;
  1522. } // DPTPW_CancelTimer
  1523. #undef DPF_MODNAME
  1524. #define DPF_MODNAME "DPTPW_ResetCompletingTimer"
  1525. //=============================================================================
  1526. // DPTPW_ResetCompletingTimer
  1527. //-----------------------------------------------------------------------------
  1528. //
  1529. // Description: Reschedules a timed work item whose callback is currently
  1530. // being called. Resetting timers that have not expired yet,
  1531. // timers that have been cancelled, or timers whose callback has
  1532. // already returned is not allowed.
  1533. //
  1534. // Using this method will never fail, since no new memory is
  1535. // allocated.
  1536. //
  1537. // Arguments:
  1538. // xxx pInterface - Pointer to interface.
  1539. // void * pvTimerData - Pointer to data for timer being
  1540. // reset.
  1541. // DWORD dwNewDelay - How much time should elapsed
  1542. // before executing the work item
  1543. // again, in ms.
  1544. // PFNDPTNWORKCALLBACK pfnNewWorkCallback - Callback to execute when timer
  1545. // elapses.
  1546. // PVOID pvNewCallbackContext - User specified context to pass to
  1547. // callback.
  1548. // UINT * puiNewTimerUnique - Place to store new uniqueness
  1549. // value for timer so that it can
  1550. // be cancelled.
  1551. // DWORD dwFlags - Flags to use when resetting
  1552. // timer.
  1553. //
  1554. // Returns: HRESULT
  1555. // DPN_OK - Resetting the timer was successful.
  1556. //=============================================================================
  1557. STDMETHODIMP DPTPW_ResetCompletingTimer(IDirectPlay8ThreadPoolWork * pInterface,
  1558. void * const pvTimerData,
  1559. const DWORD dwNewDelay,
  1560. const PFNDPTNWORKCALLBACK pfnNewWorkCallback,
  1561. PVOID const pvNewCallbackContext,
  1562. UINT * const puiNewTimerUnique,
  1563. const DWORD dwFlags)
  1564. {
  1565. DPTHREADPOOLOBJECT * pDPTPObject;
  1566. DPFX(DPFPREP, 8, "Parameters: (0x%p, 0x%p, %u, 0x%p, 0x%p, 0x%p, 0x%x)",
  1567. pInterface, pvTimerData, dwNewDelay, pfnNewWorkCallback,
  1568. pvNewCallbackContext, puiNewTimerUnique, dwFlags);
  1569. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1570. DNASSERT(pDPTPObject != NULL);
  1571. #ifdef DBG
  1572. //
  1573. // We should be in a timer callback, and therefore at least in a threadpool
  1574. // thread or doing work.
  1575. //
  1576. #ifndef DPNBUILD_ONLYONETHREAD
  1577. if (TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex) == NULL)
  1578. #endif // ! DPNBUILD_ONLYONETHREAD
  1579. {
  1580. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK);
  1581. }
  1582. #endif // DBG
  1583. //
  1584. // Call the implementation function.
  1585. //
  1586. ResetCompletingTimer(pvTimerData,
  1587. dwNewDelay,
  1588. pfnNewWorkCallback,
  1589. pvNewCallbackContext,
  1590. puiNewTimerUnique);
  1591. DPFX(DPFPREP, 8, "Returning: [DPN_OK]");
  1592. return DPN_OK;
  1593. } // DPTPW_ResetCompletingTimer
  1594. #undef DPF_MODNAME
  1595. #define DPF_MODNAME "DPTPW_WaitWhileWorking"
  1596. //=============================================================================
  1597. // DPTPW_WaitWhileWorking
  1598. //-----------------------------------------------------------------------------
  1599. //
  1600. // Description: Waits for the specified kernel object to become signalled,
  1601. // but allows thread pool work to be performed while waiting. No
  1602. // timeout can be requested, this method will wait on the handle
  1603. // forever.
  1604. //
  1605. // If this thread does not belong to the thread pool or is not
  1606. // currently within a DoWork call, no work is performed. In this
  1607. // case it behaves exactly the same as WaitForSingleObject with a
  1608. // timeout of INFINITE.
  1609. //
  1610. // Arguments:
  1611. // xxx pInterface - Pointer to interface.
  1612. // HANDLE hWaitObject - Handle on which to wait.
  1613. // DWORD dwFlags - Flags to use when waiting.
  1614. //
  1615. // Returns: HRESULT
  1616. // DPN_OK - The object became signalled.
  1617. //=============================================================================
  1618. STDMETHODIMP DPTPW_WaitWhileWorking(IDirectPlay8ThreadPoolWork * pInterface,
  1619. const HANDLE hWaitObject,
  1620. const DWORD dwFlags)
  1621. {
  1622. HRESULT hr;
  1623. DPTHREADPOOLOBJECT * pDPTPObject;
  1624. #ifndef DPNBUILD_ONLYONEPROCESSOR
  1625. DWORD dwCPU = 0;
  1626. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  1627. #ifndef DPNBUILD_ONLYONETHREAD
  1628. DPTPWORKERTHREAD * pWorkerThread;
  1629. #ifndef DPNBUILD_ONLYONEPROCESSOR
  1630. DPTPWORKQUEUE * pWorkQueue;
  1631. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  1632. #endif // ! DPNBUILD_ONLYONETHREAD
  1633. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%p, 0x%x)",
  1634. pInterface, hWaitObject, dwFlags);
  1635. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1636. DNASSERT(pDPTPObject != NULL);
  1637. //
  1638. // Don't call this method while holding locks!
  1639. //
  1640. AssertNoCriticalSectionsTakenByThisThread();
  1641. //
  1642. // Determine if this is a thread pool owned thread or one that is inside a
  1643. // DoWork call. If it is either, go ahead and start waiting & working.
  1644. // Otherwise, just perform a normal WaitForSingleObject.
  1645. // Since all CPU queues share the same TLS index, just use the one from CPU
  1646. // 0 as representative of all of them.
  1647. //
  1648. #ifndef DPNBUILD_ONLYONETHREAD
  1649. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  1650. DPFX(DPFPREP, 7, "Worker thread = 0x%p, doing work = 0x%x.",
  1651. pWorkerThread, (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK));
  1652. if (pWorkerThread != NULL)
  1653. {
  1654. pWorkerThread->dwRecursionCount++;
  1655. #ifdef DBG
  1656. if (pWorkerThread->dwRecursionCount > pWorkerThread->dwMaxRecursionCount)
  1657. {
  1658. pWorkerThread->dwMaxRecursionCount = pWorkerThread->dwRecursionCount;
  1659. }
  1660. #endif // DBG
  1661. //
  1662. // Keep looping until the object is ready.
  1663. //
  1664. while (WaitForSingleObject(hWaitObject, TIMER_BUCKET_GRANULARITY(pWorkerThread->pWorkQueue)) == WAIT_TIMEOUT)
  1665. {
  1666. //
  1667. // The object is not ready, so process some work.
  1668. //
  1669. DoWork(pWorkerThread->pWorkQueue, INFINITE);
  1670. #ifndef DPNBUILD_ONLYONEPROCESSOR
  1671. //
  1672. // It's possible to have 0 threads on a subset of processors. To
  1673. // prevent deadlocks caused by items getting scheduled to a CPU
  1674. // which then has all its threads removed, we need to make an
  1675. // attempt at servicing its items.
  1676. // We won't service every CPU each timeout, just one per loop. We
  1677. // also won't take the lock while check the thread count, we can
  1678. // stand a little error. The worst that could happen is that we
  1679. // check the queue unnecessarily or a little late. Better than
  1680. // hanging...
  1681. //
  1682. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU);
  1683. if (pWorkQueue->dwNumRunningThreads == 0)
  1684. {
  1685. DNASSERT(pWorkQueue != pWorkerThread->pWorkQueue);
  1686. DoWork(pWorkQueue, INFINITE);
  1687. }
  1688. dwCPU++;
  1689. if (dwCPU >= NUM_CPUS(pDPTPObject))
  1690. {
  1691. dwCPU = 0;
  1692. }
  1693. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  1694. }
  1695. DNASSERT(pWorkerThread->dwRecursionCount > 0);
  1696. pWorkerThread->dwRecursionCount--;
  1697. }
  1698. else
  1699. #endif // ! DPNBUILD_ONLYONETHREAD
  1700. {
  1701. BOOL fPseudoDoWork;
  1702. //
  1703. // Lock the object to prevent multiple threads from trying to change
  1704. // the settings while we check and change them.
  1705. //
  1706. DNEnterCriticalSection(&pDPTPObject->csLock);
  1707. //
  1708. // If we're in no-threaded DoWork mode, but we're not in a DoWork call
  1709. // at this moment, pretend that we are.
  1710. //
  1711. #ifdef DPNBUILD_ONLYONETHREAD
  1712. if (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK))
  1713. #else // ! DPNBUILD_ONLYONETHREAD
  1714. if ((pDPTPObject->dwTotalUserThreadCount == 0) &&
  1715. (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK)))
  1716. #endif // ! DPNBUILD_ONLYONETHREAD
  1717. {
  1718. pDPTPObject->dwFlags |= DPTPOBJECTFLAG_USER_DOINGWORK;
  1719. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  1720. pDPTPObject->dwCurrentDoWorkThreadID = GetCurrentThreadId();
  1721. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  1722. fPseudoDoWork = TRUE;
  1723. }
  1724. else
  1725. {
  1726. fPseudoDoWork = FALSE;
  1727. }
  1728. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK)
  1729. {
  1730. #ifndef DPNBUILD_ONLYONETHREAD
  1731. DWORD dwRecursionDepth;
  1732. #endif // ! DPNBUILD_ONLYONETHREAD
  1733. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  1734. DNASSERT(pDPTPObject->dwCurrentDoWorkThreadID == GetCurrentThreadId());
  1735. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  1736. //
  1737. // We can leave the lock because nobody else should be touching the
  1738. // work queue while we're doing work.
  1739. //
  1740. DNLeaveCriticalSection(&pDPTPObject->csLock);
  1741. //
  1742. // Increment the recursion depth.
  1743. //
  1744. #ifdef DPNBUILD_ONLYONETHREAD
  1745. pDPTPObject->dwWorkRecursionCount++;
  1746. #else // ! DPNBUILD_ONLYONETHREAD
  1747. dwRecursionDepth = (DWORD) ((DWORD_PTR) TlsGetValue(pDPTPObject->dwWorkRecursionCountTlsIndex));
  1748. dwRecursionDepth++;
  1749. TlsSetValue(pDPTPObject->dwWorkRecursionCountTlsIndex,
  1750. (PVOID) ((DWORD_PTR) dwRecursionDepth));
  1751. #endif // ! DPNBUILD_ONLYONETHREAD
  1752. //
  1753. // Keep looping until the object is ready.
  1754. //
  1755. while (WaitForSingleObject(hWaitObject, TIMER_BUCKET_GRANULARITY(WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU))) == WAIT_TIMEOUT)
  1756. {
  1757. //
  1758. // The object is not ready, so process some work. Note that
  1759. // timers can be missed by an amount proportional to the number
  1760. // of CPUs since we only check a single queue each interval.
  1761. //
  1762. DoWork(WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU), INFINITE);
  1763. #ifndef DPNBUILD_ONLYONEPROCESSOR
  1764. //
  1765. // Try the next CPU queue (wrapping appropriately).
  1766. //
  1767. dwCPU++;
  1768. if (dwCPU == NUM_CPUS(pDPTPObject))
  1769. {
  1770. dwCPU = 0;
  1771. }
  1772. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  1773. }
  1774. //
  1775. // Decrement the recursion depth.
  1776. //
  1777. #ifdef DPNBUILD_ONLYONETHREAD
  1778. pDPTPObject->dwWorkRecursionCount--;
  1779. #else // ! DPNBUILD_ONLYONETHREAD
  1780. DNASSERT((DWORD) ((DWORD_PTR) TlsGetValue(pDPTPObject->dwWorkRecursionCountTlsIndex)) == dwRecursionDepth);
  1781. dwRecursionDepth--;
  1782. TlsSetValue(pDPTPObject->dwWorkRecursionCountTlsIndex,
  1783. (PVOID) ((DWORD_PTR) dwRecursionDepth));
  1784. #endif // ! DPNBUILD_ONLYONETHREAD
  1785. //
  1786. // Clear the pseudo-DoWork mode flag if necessary.
  1787. //
  1788. if (fPseudoDoWork)
  1789. {
  1790. DNEnterCriticalSection(&pDPTPObject->csLock);
  1791. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK);
  1792. pDPTPObject->dwFlags &= ~DPTPOBJECTFLAG_USER_DOINGWORK;
  1793. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  1794. DNASSERT(pDPTPObject->dwCurrentDoWorkThreadID == GetCurrentThreadId());
  1795. pDPTPObject->dwCurrentDoWorkThreadID = 0;
  1796. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  1797. #ifdef DPNBUILD_ONLYONETHREAD
  1798. DNASSERT(pDPTPObject->dwWorkRecursionCount == 0);
  1799. #else // ! DPNBUILD_ONLYONETHREAD
  1800. DNASSERT(dwRecursionDepth == 0);
  1801. #endif // ! DPNBUILD_ONLYONETHREAD
  1802. DNLeaveCriticalSection(&pDPTPObject->csLock);
  1803. }
  1804. }
  1805. else
  1806. {
  1807. DNLeaveCriticalSection(&pDPTPObject->csLock);
  1808. DNASSERT(! fPseudoDoWork);
  1809. WaitForSingleObject(hWaitObject, INFINITE);
  1810. }
  1811. }
  1812. hr = DPN_OK;
  1813. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  1814. return hr;
  1815. } // DPTPW_WaitWhileWorking
  1816. #undef DPF_MODNAME
  1817. #define DPF_MODNAME "DPTPW_SleepWhileWorking"
  1818. //=============================================================================
  1819. // DPTPW_SleepWhileWorking
  1820. //-----------------------------------------------------------------------------
  1821. //
  1822. // Description: Does not return for a specified number of milliseconds, but
  1823. // allows thread pool work to be performed during that time.
  1824. //
  1825. // If this thread does not belong to the thread pool or is not
  1826. // currently within a DoWork call, no work is performed. In this
  1827. // case it behaves exactly the same as Sleep with the specified
  1828. // timeout.
  1829. //
  1830. // Arguments:
  1831. // xxx pInterface - Pointer to interface.
  1832. // DWORD dwTimeout - Timeout for the sleep operation.
  1833. // DWORD dwFlags - Flags to use when sleeping.
  1834. //
  1835. // Returns: HRESULT
  1836. // DPN_OK - The sleep occurred successfully.
  1837. //=============================================================================
  1838. STDMETHODIMP DPTPW_SleepWhileWorking(IDirectPlay8ThreadPoolWork * pInterface,
  1839. const DWORD dwTimeout,
  1840. const DWORD dwFlags)
  1841. {
  1842. HRESULT hr;
  1843. DPTHREADPOOLOBJECT * pDPTPObject;
  1844. DWORD dwCPU = 0;
  1845. #ifndef DPNBUILD_ONLYONETHREAD
  1846. DPTPWORKERTHREAD * pWorkerThread;
  1847. #ifndef DPNBUILD_ONLYONEPROCESSOR
  1848. DPTPWORKQUEUE * pWorkQueue;
  1849. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  1850. #if ((defined(DPNBUILD_THREADPOOLSTATISTICS)) && (! defined(WINCE)))
  1851. DWORD dwStartTime;
  1852. #endif // DPNBUILD_THREADPOOLSTATISTICS and ! WINCE
  1853. #endif // ! DPNBUILD_ONLYONETHREAD
  1854. DWORD dwStopTime;
  1855. DWORD dwInterval;
  1856. DWORD dwTimeLeft;
  1857. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %u, 0x%x)",
  1858. pInterface, dwTimeout, dwFlags);
  1859. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  1860. DNASSERT(pDPTPObject != NULL);
  1861. //
  1862. // Don't call this method while holding locks!
  1863. //
  1864. AssertNoCriticalSectionsTakenByThisThread();
  1865. //
  1866. // We shouldn't sleep for a really long time, it causes bad results in our
  1867. // calculations, and it doesn't make much sense in our case anyway (having
  1868. // a thread be unusable for 24 days?)
  1869. //
  1870. DNASSERT(dwTimeout < 0x80000000);
  1871. //
  1872. // Determine if this is a thread pool owned thread or one that is inside a
  1873. // DoWork call. If it is either, go ahead and start waiting & working.
  1874. // Otherwise, just perform a normal WaitForSingleObject.
  1875. // Since all CPU queues share the same TLS index, just use the one from CPU
  1876. // 0 as representative of all of them.
  1877. //
  1878. #ifndef DPNBUILD_ONLYONETHREAD
  1879. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  1880. DPFX(DPFPREP, 7, "Worker thread = 0x%p, doing work = 0x%x.",
  1881. pWorkerThread, (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK));
  1882. if (pWorkerThread != NULL)
  1883. {
  1884. pWorkerThread->dwRecursionCount++;
  1885. #ifdef DBG
  1886. if (pWorkerThread->dwRecursionCount > pWorkerThread->dwMaxRecursionCount)
  1887. {
  1888. pWorkerThread->dwMaxRecursionCount = pWorkerThread->dwRecursionCount;
  1889. }
  1890. #endif // DBG
  1891. //
  1892. // Keep looping until the timeout expires. We can be jolted awake
  1893. // earlier if the alert event gets set.
  1894. //
  1895. dwStopTime = GETTIMESTAMP() + dwTimeout;
  1896. dwInterval = TIMER_BUCKET_GRANULARITY(pWorkerThread->pWorkQueue);
  1897. //
  1898. // Give up at least one time slice.
  1899. //
  1900. Sleep(0);
  1901. do
  1902. {
  1903. //
  1904. // Process some work.
  1905. //
  1906. DoWork(pWorkerThread->pWorkQueue, dwStopTime);
  1907. #ifndef DPNBUILD_ONLYONEPROCESSOR
  1908. //
  1909. // It's possible to have 0 threads on a subset of processors. To
  1910. // prevent deadlocks caused by items getting scheduled to a CPU
  1911. // which then has all its threads removed, we need to make an
  1912. // attempt at servicing its items.
  1913. // We won't service every CPU each timeout, just one per loop. We
  1914. // also won't take the lock while check the thread count, we can
  1915. // stand a little error. The worst that could happen is that we
  1916. // check the queue unnecessarily or a little late. Better than
  1917. // hanging...
  1918. //
  1919. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU);
  1920. if (pWorkQueue->dwNumRunningThreads == 0)
  1921. {
  1922. DNASSERT(pWorkQueue != pWorkerThread->pWorkQueue);
  1923. DoWork(pWorkQueue, dwStopTime);
  1924. }
  1925. dwCPU++;
  1926. if (dwCPU >= NUM_CPUS(pDPTPObject))
  1927. {
  1928. dwCPU = 0;
  1929. }
  1930. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  1931. //
  1932. // If it's past time to stop sleeping, bail.
  1933. //
  1934. dwTimeLeft = dwStopTime - GETTIMESTAMP();
  1935. if ((int) dwTimeLeft <= 0)
  1936. {
  1937. break;
  1938. }
  1939. //
  1940. // If the time left is less than the current interval, use that
  1941. // instead for more accurate results.
  1942. //
  1943. if (dwTimeLeft < dwInterval)
  1944. {
  1945. dwInterval = dwTimeLeft;
  1946. }
  1947. #if ((defined(DPNBUILD_THREADPOOLSTATISTICS)) && (! defined(WINCE)))
  1948. dwStartTime = GETTIMESTAMP();
  1949. #endif // DPNBUILD_THREADPOOLSTATISTICS and ! WINCE
  1950. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  1951. #pragma BUGBUG(vanceo, "Sleep alertably")
  1952. Sleep(dwInterval);
  1953. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  1954. //
  1955. // Ignore the return code, we want to start working on the queue
  1956. // regardless of timeout or alert event.
  1957. //
  1958. DNWaitForSingleObject(pWorkerThread->pWorkQueue->hAlertEvent, dwInterval);
  1959. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  1960. #if ((defined(DPNBUILD_THREADPOOLSTATISTICS)) && (! defined(WINCE)))
  1961. DNInterlockedExchangeAdd((LPLONG) (&pWorkerThread->pWorkQueue->dwTotalTimeSpentUnsignalled),
  1962. (GETTIMESTAMP() - dwStartTime));
  1963. #endif // DPNBUILD_THREADPOOLSTATISTICS and ! WINCE
  1964. }
  1965. while (TRUE);
  1966. DNASSERT(pWorkerThread->dwRecursionCount > 0);
  1967. pWorkerThread->dwRecursionCount--;
  1968. }
  1969. else
  1970. #endif // ! DPNBUILD_ONLYONETHREAD
  1971. {
  1972. BOOL fPseudoDoWork;
  1973. //
  1974. // Lock the object to prevent multiple threads from trying to change
  1975. // the settings while we check and change them.
  1976. //
  1977. DNEnterCriticalSection(&pDPTPObject->csLock);
  1978. //
  1979. // If we're in no-threaded DoWork mode, but we're not in a DoWork call
  1980. // at this moment, pretend that we are.
  1981. //
  1982. #ifdef DPNBUILD_ONLYONETHREAD
  1983. if (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK))
  1984. #else // ! DPNBUILD_ONLYONETHREAD
  1985. if ((pDPTPObject->dwTotalUserThreadCount == 0) &&
  1986. (! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK)))
  1987. #endif // ! DPNBUILD_ONLYONETHREAD
  1988. {
  1989. pDPTPObject->dwFlags |= DPTPOBJECTFLAG_USER_DOINGWORK;
  1990. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  1991. pDPTPObject->dwCurrentDoWorkThreadID = GetCurrentThreadId();
  1992. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  1993. fPseudoDoWork = TRUE;
  1994. }
  1995. else
  1996. {
  1997. fPseudoDoWork = FALSE;
  1998. }
  1999. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK)
  2000. {
  2001. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  2002. DNHANDLE ahWaitObjects[64];
  2003. DWORD dwNumWaitObjects;
  2004. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2005. #ifndef DPNBUILD_ONLYONETHREAD
  2006. DWORD dwRecursionDepth;
  2007. #endif // ! DPNBUILD_ONLYONETHREAD
  2008. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  2009. DNASSERT(pDPTPObject->dwCurrentDoWorkThreadID == GetCurrentThreadId());
  2010. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  2011. //
  2012. // We can leave the lock because nobody else should be touching the
  2013. // work queue while we're doing work.
  2014. //
  2015. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2016. //
  2017. // Increment the recursion depth.
  2018. //
  2019. #ifdef DPNBUILD_ONLYONETHREAD
  2020. pDPTPObject->dwWorkRecursionCount++;
  2021. #else // ! DPNBUILD_ONLYONETHREAD
  2022. dwRecursionDepth = (DWORD) ((DWORD_PTR) TlsGetValue(pDPTPObject->dwWorkRecursionCountTlsIndex));
  2023. dwRecursionDepth++;
  2024. TlsSetValue(pDPTPObject->dwWorkRecursionCountTlsIndex,
  2025. (PVOID) ((DWORD_PTR) dwRecursionDepth));
  2026. #endif // ! DPNBUILD_ONLYONETHREAD
  2027. #ifndef DPNBUILD_USEIOCOMPLETIONPORTS
  2028. //
  2029. // Keep looping until the timeout expires. We can be jolted awake
  2030. // earlier if one of the alert events gets set. We can only wait
  2031. // on 64 objects, so we must cap the number of alert events to 64.
  2032. //
  2033. dwNumWaitObjects = NUM_CPUS(pDPTPObject);
  2034. if (dwNumWaitObjects > 64)
  2035. {
  2036. DPFX(DPFPREP, 3, "Capping number of alert events to 64 (num CPUs = %u).",
  2037. dwNumWaitObjects);
  2038. dwNumWaitObjects = 64;
  2039. }
  2040. for(dwCPU = 0; dwCPU < dwNumWaitObjects; dwCPU++)
  2041. {
  2042. ahWaitObjects[dwCPU] = (WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU))->hAlertEvent;
  2043. }
  2044. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2045. dwStopTime = GETTIMESTAMP() + dwTimeout;
  2046. dwInterval = TIMER_BUCKET_GRANULARITY(WORKQUEUE_FOR_CPU(pDPTPObject, 0));
  2047. //
  2048. // Give up at least one time slice.
  2049. //
  2050. Sleep(0);
  2051. do
  2052. {
  2053. //
  2054. // Process all CPU queues.
  2055. //
  2056. for(dwCPU = 0; dwCPU < NUM_CPUS(pDPTPObject); dwCPU++)
  2057. {
  2058. DoWork(WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU), dwStopTime);
  2059. }
  2060. //
  2061. // If it's past time to stop sleeping, bail.
  2062. //
  2063. dwTimeLeft = dwStopTime - GETTIMESTAMP();
  2064. if ((int) dwTimeLeft <= 0)
  2065. {
  2066. break;
  2067. }
  2068. //
  2069. // If the time left is less than the current interval, use that
  2070. // instead for more accurate results.
  2071. //
  2072. if (dwTimeLeft < dwInterval)
  2073. {
  2074. dwInterval = dwTimeLeft;
  2075. }
  2076. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  2077. #pragma BUGBUG(vanceo, "Sleep alertably")
  2078. Sleep(dwInterval);
  2079. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2080. //
  2081. // Ignore return code, we want to start working on all CPUs
  2082. // regardless of timeout or alert event.
  2083. //
  2084. DNWaitForMultipleObjects(dwNumWaitObjects, ahWaitObjects, FALSE, dwInterval);
  2085. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2086. }
  2087. while (TRUE);
  2088. //
  2089. // Decrement the recursion depth.
  2090. //
  2091. #ifdef DPNBUILD_ONLYONETHREAD
  2092. pDPTPObject->dwWorkRecursionCount--;
  2093. #else // ! DPNBUILD_ONLYONETHREAD
  2094. DNASSERT((DWORD) ((DWORD_PTR) TlsGetValue(pDPTPObject->dwWorkRecursionCountTlsIndex)) == dwRecursionDepth);
  2095. dwRecursionDepth--;
  2096. TlsSetValue(pDPTPObject->dwWorkRecursionCountTlsIndex,
  2097. (PVOID) ((DWORD_PTR) dwRecursionDepth));
  2098. #endif // ! DPNBUILD_ONLYONETHREAD
  2099. //
  2100. // Clear the pseudo-DoWork mode flag if necessary.
  2101. //
  2102. if (fPseudoDoWork)
  2103. {
  2104. DNEnterCriticalSection(&pDPTPObject->csLock);
  2105. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK);
  2106. pDPTPObject->dwFlags &= ~DPTPOBJECTFLAG_USER_DOINGWORK;
  2107. #if ((! defined(DPNBUILD_ONLYONETHREAD)) || (! defined(DPNBUILD_NOPARAMVAL)))
  2108. DNASSERT(pDPTPObject->dwCurrentDoWorkThreadID == GetCurrentThreadId());
  2109. pDPTPObject->dwCurrentDoWorkThreadID = 0;
  2110. #endif // ! DPNBUILD_ONLYONETHREAD or ! DPNBUILD_NOPARAMVAL
  2111. #ifdef DPNBUILD_ONLYONETHREAD
  2112. DNASSERT(pDPTPObject->dwWorkRecursionCount == 0);
  2113. #else // ! DPNBUILD_ONLYONETHREAD
  2114. DNASSERT(dwRecursionDepth == 0);
  2115. #endif // ! DPNBUILD_ONLYONETHREAD
  2116. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2117. }
  2118. }
  2119. else
  2120. {
  2121. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2122. DNASSERT(! fPseudoDoWork);
  2123. Sleep(dwTimeout);
  2124. }
  2125. }
  2126. hr = DPN_OK;
  2127. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  2128. return hr;
  2129. } // DPTPW_SleepWhileWorking
  2130. #undef DPF_MODNAME
  2131. #define DPF_MODNAME "DPTPW_RequestTotalThreadCount"
  2132. //=============================================================================
  2133. // DPTPW_RequestTotalThreadCount
  2134. //-----------------------------------------------------------------------------
  2135. //
  2136. // Description: Requests a minimum number of threads for all processors.
  2137. //
  2138. // Arguments:
  2139. // xxx pInterface - Pointer to interface.
  2140. // DWORD dwNumThreads - Desired number of threads.
  2141. // DWORD dwFlags - Flags to use when setting the thread count.
  2142. //
  2143. // Returns: HRESULT
  2144. // DPN_OK - Requesting the number of threads was
  2145. // successful.
  2146. // DPNERR_ALREADYINITIALIZED - The user has already set an incompatible
  2147. // number of threads.
  2148. //=============================================================================
  2149. STDMETHODIMP DPTPW_RequestTotalThreadCount(IDirectPlay8ThreadPoolWork * pInterface,
  2150. const DWORD dwNumThreads,
  2151. const DWORD dwFlags)
  2152. {
  2153. #ifdef DPNBUILD_ONLYONETHREAD
  2154. DPFX(DPFPREP, 0, "Requesting threads is unsupported!");
  2155. DNASSERT(!"Requesting threads is unsupported!");
  2156. return DPNERR_UNSUPPORTED;
  2157. #else // ! DPNBUILD_ONLYONETHREAD
  2158. HRESULT hr;
  2159. DPTHREADPOOLOBJECT * pDPTPObject;
  2160. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %u, 0x%x)",
  2161. pInterface, dwNumThreads, dwFlags);
  2162. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  2163. DNASSERT(pDPTPObject != NULL);
  2164. #pragma TODO(vanceo, "Possibly prevent calling on last thread pool thread or while DoWork in progress")
  2165. //
  2166. // Lock the object to prevent multiple threads from trying to change the
  2167. // thread count simultaneously.
  2168. //
  2169. DNEnterCriticalSection(&pDPTPObject->csLock);
  2170. //
  2171. // This is a minimum request, so if a Work interface has already requested
  2172. // more threads, we're fine. But if the user has already set a specific
  2173. // number of threads then this Work interface can't override that.
  2174. //
  2175. if (pDPTPObject->dwTotalUserThreadCount == -1)
  2176. {
  2177. if ((pDPTPObject->dwTotalDesiredWorkThreadCount == -1) ||
  2178. (pDPTPObject->dwTotalDesiredWorkThreadCount < dwNumThreads))
  2179. {
  2180. hr = SetTotalNumberOfThreads(pDPTPObject, dwNumThreads);
  2181. if (hr != DPN_OK)
  2182. {
  2183. DPFX(DPFPREP, 0, "Couldn't set new minimum number of threads!");
  2184. //
  2185. // Drop through...
  2186. //
  2187. }
  2188. else
  2189. {
  2190. pDPTPObject->dwTotalDesiredWorkThreadCount = dwNumThreads;
  2191. }
  2192. }
  2193. else
  2194. {
  2195. DPFX(DPFPREP, 1, "Work interface has already requested %u threads, succeeding.",
  2196. pDPTPObject->dwTotalDesiredWorkThreadCount);
  2197. hr = DPN_OK;
  2198. }
  2199. }
  2200. else
  2201. {
  2202. if (pDPTPObject->dwTotalUserThreadCount < dwNumThreads)
  2203. {
  2204. DPFX(DPFPREP, 1, "User has already requested a lower number of threads (%u).",
  2205. pDPTPObject->dwTotalUserThreadCount);
  2206. hr = DPNERR_ALREADYINITIALIZED;
  2207. //
  2208. // Drop through...
  2209. //
  2210. }
  2211. else
  2212. {
  2213. DPFX(DPFPREP, 1, "User has already requested %u threads, succeeding.",
  2214. pDPTPObject->dwTotalUserThreadCount);
  2215. hr = DPN_OK;
  2216. }
  2217. }
  2218. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2219. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  2220. return hr;
  2221. #endif // ! DPNBUILD_ONLYONETHREAD
  2222. } // DPTPW_RequestTotalThreadCount
  2223. #undef DPF_MODNAME
  2224. #define DPF_MODNAME "DPTPW_GetTotalThreadCount"
  2225. //=============================================================================
  2226. // DPTPW_GetTotalThreadCount
  2227. //-----------------------------------------------------------------------------
  2228. //
  2229. // Description: Retrieves the current number of threads on the specified
  2230. // processor(s) requested by the main user interface. If the user
  2231. // interface has not specified a thread count, but a work
  2232. // interface has, then pdwNumThreads is set to the requested
  2233. // thread count and DPNSUCCESS_PENDING is returned. If neither
  2234. // the main user interface nor a work interface have set the
  2235. // number of threads, then pdwNumThreads is set to 0 and
  2236. // DPNERR_NOTREADY is returned.
  2237. //
  2238. // Arguments:
  2239. // xxx pInterface - Pointer to interface.
  2240. // DWORD dwCPU - CPU whose thread count is to be retrieved, or -1
  2241. // for total thread count.
  2242. // DWORD * pdwNumThreads - Pointer to DWORD in which to store the current
  2243. // number of threads per processor.
  2244. // DWORD dwFlags - Flags to use when retrieving thread count.
  2245. //
  2246. // Returns: HRESULT
  2247. // DPN_OK - Retrieving the number of threads specified by user
  2248. // was successful.
  2249. // DPNSUCCESS_PENDING - The user hasn't specified a thread count, but the
  2250. // number requested by work interfaces is available.
  2251. // DPNERR_NOTREADY - No thread count has been specified yet.
  2252. //=============================================================================
  2253. STDMETHODIMP DPTPW_GetThreadCount(IDirectPlay8ThreadPoolWork * pInterface,
  2254. const DWORD dwCPU,
  2255. DWORD * const pdwNumThreads,
  2256. const DWORD dwFlags)
  2257. {
  2258. #ifdef DPNBUILD_ONLYONETHREAD
  2259. DPFX(DPFPREP, 0, "Retrieving thread count is unsupported!");
  2260. DNASSERT(!"Retrieving thread count is unsupported!");
  2261. return DPNERR_UNSUPPORTED;
  2262. #else // ! DPNBUILD_ONLYONETHREAD
  2263. HRESULT hr;
  2264. DPTHREADPOOLOBJECT * pDPTPObject;
  2265. DPTPWORKQUEUE * pWorkQueue;
  2266. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %i, 0x%p, 0x%x)",
  2267. pInterface, dwCPU, pdwNumThreads, dwFlags);
  2268. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  2269. DNASSERT(pDPTPObject != NULL);
  2270. DNASSERT((dwCPU == -1) || (dwCPU < NUM_CPUS(pDPTPObject)));
  2271. //
  2272. // Lock the object while we retrieve the thread counts.
  2273. //
  2274. DNEnterCriticalSection(&pDPTPObject->csLock);
  2275. if (dwCPU == -1)
  2276. {
  2277. //
  2278. // Get the total thread count.
  2279. //
  2280. if (pDPTPObject->dwTotalUserThreadCount != -1)
  2281. {
  2282. *pdwNumThreads = pDPTPObject->dwTotalUserThreadCount;
  2283. hr = DPN_OK;
  2284. }
  2285. else if (pDPTPObject->dwTotalDesiredWorkThreadCount != -1)
  2286. {
  2287. *pdwNumThreads = pDPTPObject->dwTotalDesiredWorkThreadCount;
  2288. hr = DPNSUCCESS_PENDING;
  2289. }
  2290. else
  2291. {
  2292. *pdwNumThreads = 0;
  2293. hr = DPNERR_NOTREADY;
  2294. }
  2295. }
  2296. else
  2297. {
  2298. //
  2299. // Get the thread count for the specific CPU.
  2300. //
  2301. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwCPU);
  2302. *pdwNumThreads = pWorkQueue->dwNumRunningThreads;
  2303. if (pDPTPObject->dwTotalUserThreadCount != -1)
  2304. {
  2305. hr = DPN_OK;
  2306. }
  2307. else if (pDPTPObject->dwTotalDesiredWorkThreadCount != -1)
  2308. {
  2309. hr = DPNSUCCESS_PENDING;
  2310. }
  2311. else
  2312. {
  2313. hr = DPNERR_NOTREADY;
  2314. }
  2315. }
  2316. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2317. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  2318. return hr;
  2319. #endif // ! DPNBUILD_ONLYONETHREAD
  2320. } // DPTPW_GetTotalThreadCount
  2321. #undef DPF_MODNAME
  2322. #define DPF_MODNAME "DPTPW_GetWorkRecursionDepth"
  2323. //=============================================================================
  2324. // DPTPW_GetWorkRecursionDepth
  2325. //-----------------------------------------------------------------------------
  2326. //
  2327. // Description: Stores the Work recursion depth of the current thread in the
  2328. // value pointed to by pdwDepth. The recursion depth is the
  2329. // number of times the thread has called DoWork, WaitWhileWorking,
  2330. // or SleepWhileWorking. If the thread is not currently in any
  2331. // of those functions, then the depth returned is 0.
  2332. //
  2333. // Arguments:
  2334. // xxx pInterface - Pointer to interface.
  2335. // DWORD * pdwDepth - Place to store recursion depth of current thread.
  2336. // DWORD dwFlags - Flags to use when retrieving recursion depth.
  2337. //
  2338. // Returns: HRESULT
  2339. // DPN_OK - The recursion depth was retrieved successfully.
  2340. //=============================================================================
  2341. STDMETHODIMP DPTPW_GetWorkRecursionDepth(IDirectPlay8ThreadPoolWork * pInterface,
  2342. DWORD * const pdwDepth,
  2343. const DWORD dwFlags)
  2344. {
  2345. HRESULT hr;
  2346. DPTHREADPOOLOBJECT * pDPTPObject;
  2347. #ifndef DPNBUILD_ONLYONETHREAD
  2348. DPTPWORKERTHREAD * pWorkerThread;
  2349. #endif // ! DPNBUILD_ONLYONETHREAD
  2350. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%p, 0x%x)",
  2351. pInterface, pdwDepth, dwFlags);
  2352. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  2353. DNASSERT(pDPTPObject != NULL);
  2354. #ifdef DPNBUILD_ONLYONETHREAD
  2355. //
  2356. // Retrieve the recursion count for the only thread..
  2357. //
  2358. DNEnterCriticalSection(&pDPTPObject->csLock);
  2359. #ifdef DBG
  2360. if (pDPTPObject->dwWorkRecursionCount > 0)
  2361. {
  2362. DPFX(DPFPREP, 5, "Thread is in a DoWork call with recursion depth %u.",
  2363. pDPTPObject->dwWorkRecursionCount);
  2364. DNASSERT(pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK);
  2365. #ifndef DPNBUILD_NOPARAMVAL
  2366. DNASSERT(pDPTPObject->dwCurrentDoWorkThreadID == GetCurrentThreadId());
  2367. #endif // ! DPNBUILD_NOPARAMVAL
  2368. }
  2369. else
  2370. {
  2371. DPFX(DPFPREP, 5, "Thread is not in a DoWork call.");
  2372. DNASSERT(! (pDPTPObject->dwFlags & DPTPOBJECTFLAG_USER_DOINGWORK));
  2373. #ifndef DPNBUILD_NOPARAMVAL
  2374. DNASSERT(pDPTPObject->dwCurrentDoWorkThreadID == 0);
  2375. #endif // ! DPNBUILD_NOPARAMVAL
  2376. }
  2377. #endif // DBG
  2378. *pdwDepth = pDPTPObject->dwWorkRecursionCount;
  2379. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2380. #else // ! DPNBUILD_ONLYONETHREAD
  2381. //
  2382. // Retrieve the worker thread state. Since all CPU queues share the same
  2383. // TLS index, just use the one from CPU 0 as representative of all of them.
  2384. //
  2385. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  2386. if (pWorkerThread != NULL)
  2387. {
  2388. DPFX(DPFPREP, 5, "Worker thread 0x%p has recursion count of %u.",
  2389. pWorkerThread, pWorkerThread->dwRecursionCount);
  2390. *pdwDepth = pWorkerThread->dwRecursionCount;
  2391. }
  2392. else
  2393. {
  2394. //
  2395. // It's an app thread. Retrieve the recursion count from the TLS slot
  2396. // dedicated to that purpose.
  2397. //
  2398. *pdwDepth = (DWORD) ((DWORD_PTR) TlsGetValue(pDPTPObject->dwWorkRecursionCountTlsIndex));
  2399. DPFX(DPFPREP, 5, "App thread has recursion count of %u.", *pdwDepth);
  2400. }
  2401. #endif // ! DPNBUILD_ONLYONETHREAD
  2402. hr = DPN_OK;
  2403. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  2404. return hr;
  2405. } // DPTPW_GetWorkRecursionDepth
  2406. #undef DPF_MODNAME
  2407. #define DPF_MODNAME "DPTPW_Preallocate"
  2408. //=============================================================================
  2409. // DPTPW_Preallocate
  2410. //-----------------------------------------------------------------------------
  2411. //
  2412. // Description: Pre-allocates per-CPU pooled resources for the given object.
  2413. //
  2414. // Arguments:
  2415. // xxx pInterface - Pointer to interface.
  2416. // DWORD dwNumWorkItems - Number of work items to pre-allocate per CPU.
  2417. // DWORD dwNumTimers - Number of timers to pre-allocate per CPU.
  2418. // DWORD dwNumIoOperations - Number of I/O operations to pre-allocate per
  2419. // CPU.
  2420. // DWORD dwFlags - Flags to use when pre-allocating.
  2421. //
  2422. // Returns: HRESULT
  2423. // DPN_OK - The recursion depth was retrieved successfully.
  2424. //=============================================================================
  2425. STDMETHODIMP DPTPW_Preallocate(IDirectPlay8ThreadPoolWork * pInterface,
  2426. const DWORD dwNumWorkItems,
  2427. const DWORD dwNumTimers,
  2428. const DWORD dwNumIoOperations,
  2429. const DWORD dwFlags)
  2430. {
  2431. #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
  2432. HRESULT hr = DPN_OK;
  2433. DPTHREADPOOLOBJECT * pDPTPObject;
  2434. DWORD dwNumToAllocate;
  2435. DWORD dwTemp;
  2436. DPTPWORKQUEUE * pWorkQueue;
  2437. DWORD dwAllocated;
  2438. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, %u, %u, %u, 0x%x)",
  2439. pInterface, dwNumWorkItems, dwNumTimers, dwNumIoOperations, dwFlags);
  2440. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  2441. DNASSERT(pDPTPObject != NULL);
  2442. //
  2443. // Work items, timers, and I/O operations all come from the same pool.
  2444. //
  2445. dwNumToAllocate = dwNumWorkItems + dwNumTimers + dwNumIoOperations;
  2446. //
  2447. // Populate the pools for each CPU.
  2448. //
  2449. for(dwTemp = 0; dwTemp < NUM_CPUS(pDPTPObject); dwTemp++)
  2450. {
  2451. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp);
  2452. dwAllocated = pWorkQueue->pWorkItemPool->Preallocate(dwNumToAllocate,
  2453. pWorkQueue;
  2454. if (dwAllocated < dwNumToAllocate)
  2455. {
  2456. DPFX(DPFPREP, 0, "Only preallocated %u of %u address elements!",
  2457. dwAllocated, dwNumToAllocate);
  2458. hr = DPNERR_OUTOFMEMORY;
  2459. break;
  2460. }
  2461. }
  2462. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  2463. return hr;
  2464. #else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
  2465. DPFX(DPFPREP, 0, "Preallocation is unsupported!");
  2466. DNASSERT(!"Preallocation is unsupported!");
  2467. return DPNERR_UNSUPPORTED;
  2468. #endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
  2469. } // DPTPW_PreallocateItems
  2470. #ifdef DPNBUILD_MANDATORYTHREADS
  2471. #undef DPF_MODNAME
  2472. #define DPF_MODNAME "DPTPW_CreateMandatoryThread"
  2473. //=============================================================================
  2474. // DPTPW_CreateMandatoryThread
  2475. //-----------------------------------------------------------------------------
  2476. //
  2477. // Description: Creates a mandatory thread that is aware of the thread pool
  2478. // but not directly controllable through the thread pool.
  2479. //
  2480. // This is largely a wrapper for the OS' CreateThread function.
  2481. // The lpThreadAttributes, dwStackSize, lpStartAddress,
  2482. // lpParameter and lpThreadId parameters are described in more
  2483. // detail in the documentation for that function. The dwFlags
  2484. // parameter is also passed straight through to the OS. However
  2485. // the CREATE_SUSPENDED flag is not supported.
  2486. //
  2487. // The thread routine must simply return when finished. It
  2488. // must not call ExitThread, endthread, or TerminateThread.
  2489. //
  2490. // Threads cannot be created when the user has put the
  2491. // threadpool in "DoWork" mode. Similarly, "DoWork" mode cannot
  2492. // be enabled when mandatory threads exist (see
  2493. // IDirectPlay8ThreadPool::SetThreadCount).
  2494. //
  2495. // Arguments:
  2496. // xxx pInterface - Pointer to interface.
  2497. // LPSECURITY_ATTRIBUTES lpThreadAttributes - Attributes for thread.
  2498. // SIZE_T dwStackSize - Stack size for thread.
  2499. // LPTHREAD_START_ROUTINE lpStartAddress - Entry point for thread.
  2500. // LPVOID lpParameter - Entry parameter for thread.
  2501. // LPDWORD lpThreadId - Place to store ID of new
  2502. // thread.
  2503. // HANDLE * phThread - Place to store handle of
  2504. // new thread.
  2505. // DWORD dwFlags - Flags to use when creating
  2506. // thread.
  2507. //
  2508. // Returns: HRESULT
  2509. // DPN_OK - Creating the thread was successful.
  2510. // DPNERR_OUTOFMEMORY - Not enough memory to create the thread.
  2511. // DPNERR_NOTALLOWED - The user is in DoWork mode, threads cannot be
  2512. // created.
  2513. //=============================================================================
  2514. STDMETHODIMP DPTPW_CreateMandatoryThread(IDirectPlay8ThreadPoolWork * pInterface,
  2515. LPSECURITY_ATTRIBUTES lpThreadAttributes,
  2516. SIZE_T dwStackSize,
  2517. LPTHREAD_START_ROUTINE lpStartAddress,
  2518. LPVOID lpParameter,
  2519. LPDWORD lpThreadId,
  2520. HANDLE *const phThread,
  2521. const DWORD dwFlags)
  2522. {
  2523. #ifdef DPNBUILD_ONLYONETHREAD
  2524. DPFX(DPFPREP, 0, "Thread creation is not supported!");
  2525. DNASSERT(!"Thread creation is not supported!");
  2526. return DPNERR_UNSUPPORTED;
  2527. #else // ! DPNBUILD_ONLYONETHREAD
  2528. HRESULT hr;
  2529. DPTHREADPOOLOBJECT * pDPTPObject;
  2530. DNHANDLE hThread = NULL;
  2531. DNHANDLE hStartedEvent = NULL;
  2532. DPTPMANDATORYTHREAD * pMandatoryThread = NULL;
  2533. DWORD dwThreadID;
  2534. DNHANDLE ahWaitObjects[2];
  2535. DWORD dwResult;
  2536. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Parameters: (0x%p, 0x%p, %u, 0x%p, 0x%p, 0x%p, 0x%p, 0x%x)",
  2537. pInterface, lpThreadAttributes, dwStackSize, lpStartAddress,
  2538. lpParameter, lpThreadId, phThread, dwFlags);
  2539. pDPTPObject = (DPTHREADPOOLOBJECT*) GET_OBJECT_FROM_INTERFACE(pInterface);
  2540. DNASSERT(pDPTPObject != NULL);
  2541. DNASSERT(lpStartAddress != NULL);
  2542. DNASSERT(lpThreadId != NULL);
  2543. DNASSERT(phThread != NULL);
  2544. DNASSERT(! (dwFlags & CREATE_SUSPENDED));
  2545. //
  2546. // We could check to see if we're in DoWork mode, but we don't want to hold
  2547. // the lock while creating the thread. We would end up checking twice,
  2548. // once here, and again in the thread when it was about to increment the
  2549. // mandatory thread count, so we'll just use the one in the thread. See
  2550. // DPTPMandatoryThreadProc
  2551. //
  2552. //
  2553. // Create event so we can be notified when the thread starts.
  2554. //
  2555. hStartedEvent = DNCreateEvent(NULL, FALSE, FALSE, NULL);
  2556. if (hStartedEvent == NULL)
  2557. {
  2558. #ifdef DBG
  2559. dwResult = GetLastError();
  2560. DPFX(DPFPREP, 0, "Couldn't create start event (err = %u)!", dwResult);
  2561. #endif // DBG
  2562. hr = DPNERR_GENERIC;
  2563. goto Failure;
  2564. }
  2565. //
  2566. // Allocate a tracking structure for the thread.
  2567. //
  2568. pMandatoryThread = (DPTPMANDATORYTHREAD*) DNMalloc(sizeof(DPTPMANDATORYTHREAD));
  2569. if (pMandatoryThread == NULL)
  2570. {
  2571. DPFX(DPFPREP, 0, "Couldn't allocate memory for tracking mandatory thread!");
  2572. hr = DPNERR_OUTOFMEMORY;
  2573. goto Failure;
  2574. }
  2575. pMandatoryThread->Sig[0] = 'M';
  2576. pMandatoryThread->Sig[1] = 'N';
  2577. pMandatoryThread->Sig[2] = 'D';
  2578. pMandatoryThread->Sig[3] = 'T';
  2579. pMandatoryThread->pDPTPObject = pDPTPObject;
  2580. pMandatoryThread->hStartedEvent = hStartedEvent;
  2581. pMandatoryThread->pfnMsgHandler = (WORKQUEUE_FOR_CPU(pDPTPObject, 0))->pfnMsgHandler;
  2582. pMandatoryThread->pvMsgHandlerContext = (WORKQUEUE_FOR_CPU(pDPTPObject, 0))->pvMsgHandlerContext;
  2583. pMandatoryThread->lpStartAddress = lpStartAddress;
  2584. pMandatoryThread->lpParameter = lpParameter;
  2585. #ifdef DBG
  2586. pMandatoryThread->dwThreadID = 0;
  2587. pMandatoryThread->blList.Initialize();
  2588. #endif // DBG
  2589. hThread = DNCreateThread(lpThreadAttributes,
  2590. dwStackSize,
  2591. DPTPMandatoryThreadProc,
  2592. pMandatoryThread,
  2593. dwFlags,
  2594. &dwThreadID);
  2595. if (hThread == NULL)
  2596. {
  2597. #ifdef DBG
  2598. dwResult = GetLastError();
  2599. DPFX(DPFPREP, 0, "Couldn't create thread (err = %u)!", dwResult);
  2600. #endif // DBG
  2601. hr = DPNERR_GENERIC;
  2602. goto Failure;
  2603. }
  2604. ahWaitObjects[0] = hStartedEvent;
  2605. ahWaitObjects[1] = hThread;
  2606. dwResult = DNWaitForMultipleObjects(2, ahWaitObjects, FALSE, INFINITE);
  2607. switch (dwResult)
  2608. {
  2609. case WAIT_OBJECT_0:
  2610. {
  2611. //
  2612. // The thread started successfully. Drop through.
  2613. //
  2614. break;
  2615. }
  2616. case WAIT_OBJECT_0 + 1:
  2617. {
  2618. //
  2619. // The thread shut down prematurely.
  2620. //
  2621. GetExitCodeThread(HANDLE_FROM_DNHANDLE(hThread), &dwResult);
  2622. if ((HRESULT) dwResult == DPNERR_NOTALLOWED)
  2623. {
  2624. DPFX(DPFPREP, 0, "Thread started while in DoWork mode!");
  2625. hr = DPNERR_NOTALLOWED;
  2626. }
  2627. else
  2628. {
  2629. DPFX(DPFPREP, 0, "Thread shutdown prematurely (exit code = %u)!", dwResult);
  2630. hr = DPNERR_GENERIC;
  2631. }
  2632. goto Failure;
  2633. break;
  2634. }
  2635. default:
  2636. {
  2637. DPFX(DPFPREP, 0, "Thread failed waiting (result = %u)!", dwResult);
  2638. hr = DPNERR_GENERIC;
  2639. goto Failure;
  2640. break;
  2641. }
  2642. }
  2643. //
  2644. // At this point, the thread owns the pMandatoryThread object, and could
  2645. // delete it at any time. We must not reference it again.
  2646. //
  2647. //
  2648. // Return the thread ID and handle to the caller.
  2649. //
  2650. *lpThreadId = dwThreadID;
  2651. *phThread = HANDLE_FROM_DNHANDLE(hThread);
  2652. hr = DPN_OK;
  2653. Exit:
  2654. //
  2655. // Close the started event, we no longer need it.
  2656. //
  2657. if (hStartedEvent != NULL)
  2658. {
  2659. DNCloseHandle(hStartedEvent);
  2660. hStartedEvent = NULL;
  2661. }
  2662. DPFX(DPFPREP, DPF_ENTRYLEVEL, "Returning: [0x%lx]", hr);
  2663. return hr;
  2664. Failure:
  2665. if (hThread != NULL)
  2666. {
  2667. DNCloseHandle(hThread);
  2668. hThread = NULL;
  2669. }
  2670. if (pMandatoryThread != NULL)
  2671. {
  2672. DNFree(pMandatoryThread);
  2673. pMandatoryThread = NULL;
  2674. }
  2675. goto Exit;
  2676. #endif // ! DPNBUILD_ONLYONETHREAD
  2677. } // DPTPW_CreateMandatoryThread
  2678. #endif // DPNBUILD_MANDATORYTHREADS
  2679. #ifndef DPNBUILD_ONLYONEPROCESSOR
  2680. #undef DPF_MODNAME
  2681. #define DPF_MODNAME "ChooseWorkQueue"
  2682. //=============================================================================
  2683. // ChooseWorkQueue
  2684. //-----------------------------------------------------------------------------
  2685. //
  2686. // Description: Selects the best CPU for a given operation, and returns a
  2687. // pointer to its work queue object.
  2688. //
  2689. // Arguments:
  2690. // DPTHREADPOOLOBJECT * pDPTPObject - Pointer to interface object.
  2691. //
  2692. // Returns: Pointer to work queue selected.
  2693. //=============================================================================
  2694. DPTPWORKQUEUE * ChooseWorkQueue(DPTHREADPOOLOBJECT * const pDPTPObject)
  2695. {
  2696. DPTPWORKQUEUE * pWorkQueue;
  2697. DPTPWORKERTHREAD * pWorkerThread;
  2698. //
  2699. // If this is a thread pool thread, choose the CPU associated with this
  2700. // thread.
  2701. //
  2702. pWorkerThread = (DPTPWORKERTHREAD*) TlsGetValue((WORKQUEUE_FOR_CPU(pDPTPObject, 0))->dwWorkerThreadTlsIndex);
  2703. if (pWorkerThread != NULL)
  2704. {
  2705. pWorkQueue = pWorkerThread->pWorkQueue;
  2706. goto Exit;
  2707. }
  2708. DNEnterCriticalSection(&pDPTPObject->csLock);
  2709. //
  2710. // If we are in DoWork mode, or no threads have been started, just use
  2711. // processor 0's work queue.
  2712. //
  2713. if ((pDPTPObject->dwTotalUserThreadCount == 0) ||
  2714. ((pDPTPObject->dwTotalUserThreadCount == -1) && (pDPTPObject->dwTotalDesiredWorkThreadCount == -1)))
  2715. {
  2716. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2717. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, 0);
  2718. goto Exit;
  2719. }
  2720. //
  2721. // Otherwise keep cycling through each CPU to distribute items equally
  2722. // round-robin style. Don't queue items for CPUs that don't have any
  2723. // running threads, though.
  2724. //
  2725. do
  2726. {
  2727. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, pDPTPObject->dwCurrentCPUSelection);
  2728. pDPTPObject->dwCurrentCPUSelection++;
  2729. if (pDPTPObject->dwCurrentCPUSelection >= NUM_CPUS(pDPTPObject))
  2730. {
  2731. pDPTPObject->dwCurrentCPUSelection = 0;
  2732. }
  2733. }
  2734. while (pWorkQueue->dwNumRunningThreads == 0);
  2735. DNLeaveCriticalSection(&pDPTPObject->csLock);
  2736. Exit:
  2737. return pWorkQueue;
  2738. } // ChooseWorkQueue
  2739. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  2740. #ifndef DPNBUILD_ONLYONETHREAD
  2741. #undef DPF_MODNAME
  2742. #define DPF_MODNAME "SetTotalNumberOfThreads"
  2743. //=============================================================================
  2744. // SetTotalNumberOfThreads
  2745. //-----------------------------------------------------------------------------
  2746. //
  2747. // Description: Modifies the total number of threads for all processors.
  2748. //
  2749. // The DPTHREADPOOLOBJECT lock is assumed to be held.
  2750. //
  2751. // Arguments:
  2752. // DPTHREADPOOLOBJECT * pDPTPObject - Pointer to interface object.
  2753. // DWORD dwNumThreads - New desired totla number of threads.
  2754. //
  2755. // Returns: HRESULT
  2756. // DPN_OK - Setting the number of threads was successful.
  2757. // DPNERR_OUTOFMEMORY - Not enough memory to alter the number of threads.
  2758. //=============================================================================
  2759. HRESULT SetTotalNumberOfThreads(DPTHREADPOOLOBJECT * const pDPTPObject,
  2760. const DWORD dwNumThreads)
  2761. {
  2762. HRESULT hr = DPN_OK;
  2763. DWORD dwNumThreadsPerProcessor;
  2764. DWORD dwExtraThreads;
  2765. DWORD dwTemp;
  2766. DPTPWORKQUEUE * pWorkQueue;
  2767. DWORD dwDelta;
  2768. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  2769. dwNumThreadsPerProcessor = dwNumThreads;
  2770. dwExtraThreads = 0;
  2771. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2772. dwNumThreadsPerProcessor = dwNumThreads / NUM_CPUS(pDPTPObject);
  2773. dwExtraThreads = dwNumThreads % NUM_CPUS(pDPTPObject);
  2774. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2775. if (pDPTPObject->dwFlags & DPTPOBJECTFLAG_THREADCOUNTCHANGING)
  2776. {
  2777. AssertCriticalSectionIsTakenByThisThread(&pDPTPObject->csLock, FALSE);
  2778. }
  2779. else
  2780. {
  2781. AssertCriticalSectionIsTakenByThisThread(&pDPTPObject->csLock, TRUE);
  2782. }
  2783. //
  2784. // Loop through each of the CPU specific work queues and adjust their
  2785. // thread counts.
  2786. //
  2787. #ifdef DPNBUILD_USEIOCOMPLETIONPORTS
  2788. dwTemp = 0;
  2789. #else // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2790. for(dwTemp = 0; dwTemp < NUM_CPUS(pDPTPObject); dwTemp++)
  2791. #endif // ! DPNBUILD_USEIOCOMPLETIONPORTS
  2792. {
  2793. pWorkQueue = WORKQUEUE_FOR_CPU(pDPTPObject, dwTemp);
  2794. dwDelta = dwNumThreadsPerProcessor - pWorkQueue->dwNumRunningThreads;
  2795. if (dwTemp < dwExtraThreads)
  2796. {
  2797. dwDelta++;
  2798. }
  2799. if ((int) dwDelta > 0)
  2800. {
  2801. //
  2802. // We need to add threads.
  2803. //
  2804. hr = StartThreads(pWorkQueue, dwDelta);
  2805. if (hr != DPN_OK)
  2806. {
  2807. DPFX(DPFPREP, 0, "Couldn't start %u threads!", dwDelta);
  2808. goto Exit;
  2809. }
  2810. }
  2811. else if ((int) dwDelta < 0)
  2812. {
  2813. //
  2814. // We need to remove threads.
  2815. //
  2816. dwDelta = (int) dwDelta * -1; // get absolute value
  2817. hr = StopThreads(pWorkQueue, dwDelta);
  2818. if (hr != DPN_OK)
  2819. {
  2820. DPFX(DPFPREP, 0, "Couldn't stop %u threads!", dwDelta);
  2821. goto Exit;
  2822. }
  2823. }
  2824. else
  2825. {
  2826. //
  2827. // The thread count is already correct.
  2828. //
  2829. }
  2830. if (dwTemp < dwExtraThreads)
  2831. {
  2832. DNASSERT(pWorkQueue->dwNumRunningThreads == (dwNumThreadsPerProcessor + 1));
  2833. }
  2834. else
  2835. {
  2836. DNASSERT(pWorkQueue->dwNumRunningThreads == dwNumThreadsPerProcessor);
  2837. }
  2838. }
  2839. Exit:
  2840. return hr;
  2841. } // SetTotalNumberOfThreads
  2842. #endif // ! DPNBUILD_ONLYONETHREAD