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.

3358 lines
88 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1998-2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: ThreadPool.cpp
  6. * Content: main job thread pool
  7. *
  8. *
  9. * History:
  10. * Date By Reason
  11. * ==== == ======
  12. * 11/25/1998 jtk Created
  13. ***************************************************************************/
  14. #include "dnwsocki.h"
  15. //**********************************************************************
  16. // Constant definitions
  17. //**********************************************************************
  18. //
  19. // select polling period and duration for I/O (milliseconds)
  20. //
  21. static const DWORD g_dwSelectTimePeriod = 4; // wait for jobs for 4 ms, then wait for I/O
  22. static const DWORD g_dwSelectTimeSlice = 0; // wait for I/O for 0 ms, then wait for jobs
  23. //**********************************************************************
  24. // Macro definitions
  25. //**********************************************************************
  26. //**********************************************************************
  27. // Structure definitions
  28. //**********************************************************************
  29. #ifndef DPNBUILD_NOSPUI
  30. //
  31. // structure passed to dialog threads
  32. //
  33. typedef struct _DIALOG_THREAD_PARAM
  34. {
  35. DIALOG_FUNCTION *pDialogFunction;
  36. void *pContext;
  37. CThreadPool *pThisThreadPool;
  38. } DIALOG_THREAD_PARAM;
  39. #endif // !DPNBUILD_NOSPUI
  40. //**********************************************************************
  41. // Variable definitions
  42. //**********************************************************************
  43. //**********************************************************************
  44. // Function prototypes
  45. //**********************************************************************
  46. //**********************************************************************
  47. // Function definitions
  48. //**********************************************************************
  49. DWORD WINAPI DPNBlockingJobThreadProc(PVOID pvParameter);
  50. //**********************************************************************
  51. // ------------------------------
  52. // CThreadPool::PoolAllocFunction
  53. //
  54. // Entry: Nothing
  55. //
  56. // Exit: Nothing
  57. // ------------------------------
  58. #undef DPF_MODNAME
  59. #define DPF_MODNAME "CThreadPool::PoolAllocFunction"
  60. BOOL CThreadPool::PoolAllocFunction( void* pvItem, void* pvContext )
  61. {
  62. CThreadPool* pThreadPool = (CThreadPool*)pvItem;
  63. pThreadPool->m_iRefCount = 0;
  64. pThreadPool->m_fAllowThreadCountReduction = FALSE;
  65. #ifndef DPNBUILD_ONLYONETHREAD
  66. pThreadPool->m_iIntendedThreadCount = 0;
  67. #endif // ! DPNBUILD_ONLYONETHREAD
  68. #ifndef DPNBUILD_ONLYWINSOCK2
  69. pThreadPool->m_uReservedSocketCount = 0;
  70. memset( &pThreadPool->m_SocketSet, 0x00, sizeof( pThreadPool->m_SocketSet ) );
  71. memset( &pThreadPool->m_pSocketPorts, 0x00, sizeof( pThreadPool->m_pSocketPorts ) );
  72. pThreadPool->m_pvTimerDataWinsock1IO = NULL;
  73. pThreadPool->m_uiTimerUniqueWinsock1IO = 0;
  74. pThreadPool->m_fCancelWinsock1IO = FALSE;
  75. #endif // ! DPNBUILD_ONLYWINSOCK2
  76. #ifndef DPNBUILD_NONATHELP
  77. pThreadPool->m_fNATHelpLoaded = FALSE;
  78. pThreadPool->m_fNATHelpTimerJobSubmitted = FALSE;
  79. pThreadPool->m_dwNATHelpUpdateThreadID = 0;
  80. #endif // DPNBUILD_NONATHELP
  81. #if ((defined(WINNT)) && (! defined(DPNBUILD_NOMULTICAST)))
  82. pThreadPool->m_fMadcapLoaded = FALSE;
  83. #endif // WINNT and not DPNBUILD_NOMULTICAST
  84. pThreadPool->m_Sig[0] = 'T';
  85. pThreadPool->m_Sig[1] = 'H';
  86. pThreadPool->m_Sig[2] = 'P';
  87. pThreadPool->m_Sig[3] = 'L';
  88. pThreadPool->m_TimerJobList.Initialize();
  89. #ifndef DPNBUILD_ONLYONETHREAD
  90. pThreadPool->m_blBlockingJobQueue.Initialize();
  91. pThreadPool->m_hBlockingJobThread = NULL;
  92. pThreadPool->m_hBlockingJobEvent = NULL;
  93. #endif // ! DPNBUILD_ONLYONETHREAD
  94. return TRUE;
  95. }
  96. //**********************************************************************
  97. //**********************************************************************
  98. // ------------------------------
  99. // CThreadPool::PoolDellocFunction
  100. //
  101. // Entry: Nothing
  102. //
  103. // Exit: Nothing
  104. // ------------------------------
  105. #undef DPF_MODNAME
  106. #define DPF_MODNAME "CThreadPool::PoolDeallocFunction"
  107. void CThreadPool::PoolDeallocFunction( void* pvItem )
  108. {
  109. #ifdef DBG
  110. const CThreadPool* pThreadPool = (CThreadPool*)pvItem;
  111. #ifndef DPNBUILD_ONLYWINSOCK2
  112. DNASSERT( pThreadPool->m_uReservedSocketCount == 0 );
  113. DNASSERT( pThreadPool->m_pvTimerDataWinsock1IO == NULL );
  114. DNASSERT( ! pThreadPool->m_fCancelWinsock1IO );
  115. #endif // ! DPNBUILD_ONLYWINSOCK2
  116. DNASSERT( pThreadPool->m_iRefCount == 0 );
  117. #ifndef DPNBUILD_NONATHELP
  118. DNASSERT( pThreadPool->m_fNATHelpLoaded == FALSE );
  119. DNASSERT( pThreadPool->m_fNATHelpTimerJobSubmitted == FALSE );
  120. DNASSERT( pThreadPool->m_dwNATHelpUpdateThreadID == 0 );
  121. #endif // DPNBUILD_NONATHELP
  122. #if ((defined(WINNT)) && (! defined(DPNBUILD_NOMULTICAST)))
  123. DNASSERT( pThreadPool->m_fMadcapLoaded == FALSE );
  124. #endif // WINNT and not DPNBUILD_NOMULTICAST
  125. DNASSERT( pThreadPool->m_TimerJobList.IsEmpty() != FALSE );
  126. #ifndef DPNBUILD_ONLYONETHREAD
  127. DNASSERT( pThreadPool->m_blBlockingJobQueue.IsEmpty() );
  128. DNASSERT( pThreadPool->m_hBlockingJobThread == NULL );
  129. DNASSERT( pThreadPool->m_hBlockingJobEvent == NULL );
  130. #endif // ! DPNBUILD_ONLYONETHREAD
  131. #endif // DBG
  132. }
  133. //**********************************************************************
  134. //**********************************************************************
  135. // ------------------------------
  136. // CThreadPool::Initialize - initialize work threads
  137. //
  138. // Entry: Nothing
  139. //
  140. // Exit: Error Code
  141. // ------------------------------
  142. #undef DPF_MODNAME
  143. #define DPF_MODNAME "CThreadPool::Initialize"
  144. HRESULT CThreadPool::Initialize( void )
  145. {
  146. HRESULT hr;
  147. BOOL fInittedLock = FALSE;
  148. BOOL fInittedTimerDataLock = FALSE;
  149. #ifndef DPNBUILD_ONLYONETHREAD
  150. BOOL fInittedBlockingJobLock = FALSE;
  151. #endif // ! DPNBUILD_ONLYONETHREAD
  152. DPFX(DPFPREP, 4, "(0x%p) Enter", this);
  153. //
  154. // initialize
  155. //
  156. hr = DPN_OK;
  157. //
  158. // initialize critical sections
  159. //
  160. if ( DNInitializeCriticalSection( &m_Lock ) == FALSE )
  161. {
  162. hr = DPNERR_OUTOFMEMORY;
  163. goto Failure;
  164. }
  165. DebugSetCriticalSectionRecursionCount( &m_Lock, 0 );
  166. DebugSetCriticalSectionGroup( &m_Lock, &g_blDPNWSockCritSecsHeld ); // separate dpnwsock CSes from the rest of DPlay's CSes
  167. fInittedLock = TRUE;
  168. //
  169. // Create the thread pool work interface
  170. //
  171. #ifdef DPNBUILD_LIBINTERFACE
  172. #if ((defined(DPNBUILD_ONLYONETHREAD)) && (! defined(DPNBUILD_MULTIPLETHREADPOOLS)))
  173. DPTPCF_GetObject(reinterpret_cast<void**>(&m_pDPThreadPoolWork));
  174. hr = S_OK;
  175. #else // ! DPNBUILD_ONLYONETHREAD or DPNBUILD_MULTIPLETHREADPOOLS
  176. hr = DPTPCF_CreateObject(reinterpret_cast<void**>(&m_pDPThreadPoolWork));
  177. #endif // ! DPNBUILD_ONLYONETHREAD or DPNBUILD_MULTIPLETHREADPOOLS
  178. #else // ! DPNBUILD_LIBINTERFACE
  179. hr = COM_CoCreateInstance( CLSID_DirectPlay8ThreadPool,
  180. NULL,
  181. CLSCTX_INPROC_SERVER,
  182. IID_IDirectPlay8ThreadPoolWork,
  183. reinterpret_cast<void**>( &m_pDPThreadPoolWork ),
  184. FALSE );
  185. #endif // ! DPNBUILD_LIBINTERFACE
  186. if ( hr != S_OK )
  187. {
  188. DPFX(DPFPREP, 0, " Failed to create thread pool work interface (err = 0x%lx)!", hr);
  189. goto Failure;
  190. }
  191. if ( DNInitializeCriticalSection( &m_TimerDataLock ) == FALSE )
  192. {
  193. hr = DPNERR_OUTOFMEMORY;
  194. goto Failure;
  195. }
  196. DebugSetCriticalSectionRecursionCount( &m_TimerDataLock, 1 );
  197. DebugSetCriticalSectionGroup( &m_TimerDataLock, &g_blDPNWSockCritSecsHeld ); // separate dpnwsock CSes from the rest of DPlay's CSes
  198. fInittedTimerDataLock = TRUE;
  199. DNASSERT( m_fAllowThreadCountReduction == FALSE );
  200. m_fAllowThreadCountReduction = TRUE;
  201. #ifndef DPNBUILD_ONLYONETHREAD
  202. if ( DNInitializeCriticalSection( &m_csBlockingJobLock ) == FALSE )
  203. {
  204. hr = DPNERR_OUTOFMEMORY;
  205. goto Failure;
  206. }
  207. DebugSetCriticalSectionRecursionCount( &m_csBlockingJobLock, 0 );
  208. DebugSetCriticalSectionGroup( &m_csBlockingJobLock, &g_blDPNWSockCritSecsHeld ); // separate dpnwsock CSes from the rest of DPlay's CSes
  209. fInittedBlockingJobLock = TRUE;
  210. DPFX(DPFPREP, 7, "SetIntendedThreadCount %i", g_iThreadCount);
  211. SetIntendedThreadCount( g_iThreadCount );
  212. #endif // ! DPNBUILD_ONLYONETHREAD
  213. Exit:
  214. DPFX(DPFPREP, 4, "(0x%p) Return [0x%lx]", this, hr);
  215. return hr;
  216. Failure:
  217. #ifndef DPNBUILD_ONLYONETHREAD
  218. if (fInittedBlockingJobLock)
  219. {
  220. DNDeleteCriticalSection(&m_csBlockingJobLock);
  221. fInittedBlockingJobLock = FALSE;
  222. }
  223. #endif // ! DPNBUILD_ONLYONETHREAD
  224. if (fInittedTimerDataLock)
  225. {
  226. DNDeleteCriticalSection(&m_TimerDataLock);
  227. fInittedTimerDataLock = FALSE;
  228. }
  229. if (m_pDPThreadPoolWork != NULL)
  230. {
  231. IDirectPlay8ThreadPoolWork_Release(m_pDPThreadPoolWork);
  232. m_pDPThreadPoolWork = NULL;
  233. }
  234. if (fInittedLock)
  235. {
  236. DNDeleteCriticalSection(&m_Lock);
  237. fInittedLock = FALSE;
  238. }
  239. goto Exit;
  240. }
  241. //**********************************************************************
  242. //**********************************************************************
  243. // ------------------------------
  244. // CThreadPool::Deinitialize - destroy work threads
  245. //
  246. // Entry: Nothing
  247. //
  248. // Exit: Nothing
  249. // ------------------------------
  250. #undef DPF_MODNAME
  251. #define DPF_MODNAME "CThreadPool::Deinitialize"
  252. void CThreadPool::Deinitialize( void )
  253. {
  254. DPFX(DPFPREP, 4, "(0x%p) Enter", this );
  255. #ifndef DPNBUILD_ONLYWINSOCK2
  256. //
  257. // Keep trying to cancel the Winsock1 timer. It may fail because it's in the
  258. // process of actively executing, but eventually we will catch it while only
  259. // the timer is active.
  260. //
  261. Lock();
  262. if (m_pvTimerDataWinsock1IO != NULL)
  263. {
  264. HRESULT hr;
  265. DWORD dwInterval;
  266. DPFX(DPFPREP, 1, "Cancelling Winsock 1 I/O timer.");
  267. dwInterval = 10;
  268. do
  269. {
  270. hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
  271. m_pvTimerDataWinsock1IO,
  272. m_uiTimerUniqueWinsock1IO,
  273. 0);
  274. if (hr != DPN_OK)
  275. {
  276. Unlock();
  277. IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork,
  278. dwInterval,
  279. 0);
  280. dwInterval += 5; // next time wait a bit longer
  281. DNASSERT(dwInterval < 600);
  282. Lock();
  283. }
  284. else
  285. {
  286. m_pvTimerDataWinsock1IO = NULL;
  287. }
  288. }
  289. while (m_pvTimerDataWinsock1IO != NULL);
  290. }
  291. Unlock();
  292. #endif // ! DPNBUILD_ONLYWINSOCK2
  293. #ifndef DPNBUILD_NONATHELP
  294. //
  295. // Stop submitting new NAT help refresh jobs.
  296. //
  297. if ( IsNATHelpTimerJobSubmitted() )
  298. {
  299. //
  300. // Try to cancel the job. It could fail if the timer is in the
  301. // process of firing right now. If it is firing, keep looping
  302. // until we see that the timer has noticed cancellation.
  303. //
  304. DPFX(DPFPREP, 5, "Cancelling NAT Help refresh timer job.");
  305. if (! StopTimerJob( this, DPNERR_USERCANCEL ))
  306. {
  307. DWORD dwInterval;
  308. DPFX(DPFPREP, 4, "Couldn't cancel NAT Help refresh timer job, waiting for completion.");
  309. dwInterval = 10;
  310. while (*((volatile BOOL *) (&m_fNATHelpTimerJobSubmitted)))
  311. {
  312. IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork,
  313. dwInterval,
  314. 0);
  315. dwInterval += 5; // next time wait a bit longer
  316. if (dwInterval > 500)
  317. {
  318. dwInterval = 500;
  319. }
  320. }
  321. }
  322. else
  323. {
  324. DNASSERT(! m_fNATHelpTimerJobSubmitted);
  325. }
  326. }
  327. #endif // DPNBUILD_NONATHELP
  328. #ifndef DPNBUILD_ONLYONETHREAD
  329. //
  330. // Stop the blocking job thread, if it was running.
  331. //
  332. if (m_hBlockingJobThread != NULL)
  333. {
  334. HRESULT hr;
  335. //
  336. // Queue an item with a NULL callback to signal that the thread
  337. // should quit.
  338. //
  339. hr = SubmitBlockingJob(NULL, NULL);
  340. DNASSERT(hr == DPN_OK);
  341. //
  342. // Wait for the thread to complete.
  343. //
  344. hr = IDirectPlay8ThreadPoolWork_WaitWhileWorking(m_pDPThreadPoolWork,
  345. HANDLE_FROM_DNHANDLE(m_hBlockingJobThread),
  346. 0);
  347. DNASSERT(hr == DPN_OK);
  348. DNASSERT(m_blBlockingJobQueue.IsEmpty());
  349. DNCloseHandle(m_hBlockingJobThread);
  350. m_hBlockingJobThread = NULL;
  351. DNCloseHandle(m_hBlockingJobEvent);
  352. m_hBlockingJobEvent = NULL;
  353. }
  354. #endif // ! DPNBUILD_ONLYONETHREAD
  355. #ifndef DPNBUILD_NONATHELP
  356. //
  357. // The refresh timer and blocking jobs should have finished earlier,
  358. // it should be safe to unload NAT help now (if we even loaded it).
  359. //
  360. if ( IsNATHelpLoaded() )
  361. {
  362. UnloadNATHelp();
  363. m_fNATHelpLoaded = FALSE;
  364. }
  365. #endif // DPNBUILD_NONATHELP
  366. #if ((defined(WINNT)) && (! defined(DPNBUILD_NOMULTICAST)))
  367. //
  368. // Unload MADCAP, if we had loaded it.
  369. //
  370. if ( IsMadcapLoaded() )
  371. {
  372. UnloadMadcap();
  373. m_fMadcapLoaded = FALSE;
  374. }
  375. #endif // WINNT and not DPNBUILD_NOMULTICAST
  376. if ( m_pDPThreadPoolWork != NULL )
  377. {
  378. IDirectPlay8ThreadPoolWork_Release(m_pDPThreadPoolWork);
  379. m_pDPThreadPoolWork = NULL;
  380. }
  381. m_fAllowThreadCountReduction = FALSE;
  382. DNDeleteCriticalSection( &m_TimerDataLock );
  383. DNDeleteCriticalSection( &m_Lock );
  384. #ifndef DPNBUILD_ONLYONETHREAD
  385. DNDeleteCriticalSection(&m_csBlockingJobLock);
  386. #endif // ! DPNBUILD_ONLYONETHREAD
  387. DPFX(DPFPREP, 4, "(0x%p) Leave", this );
  388. }
  389. //**********************************************************************
  390. //**********************************************************************
  391. // ------------------------------
  392. // CThreadPool::GetNewReadIOData - get new read request from pool
  393. //
  394. // Entry: Pointer to context
  395. //
  396. // Exit: Pointer to new read request
  397. // NULL = out of memory
  398. // ------------------------------
  399. #undef DPF_MODNAME
  400. #define DPF_MODNAME "CThreadPool::GetNewReadIOData"
  401. #if ((! defined(DPNBUILD_NOWINSOCK2)) && (! defined(DPNBUILD_ONLYWINSOCK2)))
  402. CReadIOData *CThreadPool::GetNewReadIOData( READ_IO_DATA_POOL_CONTEXT *const pContext, const BOOL fNeedOverlapped )
  403. #else // DPNBUILD_NOWINSOCK2 or DPNBUILD_ONLYWINSOCK2
  404. CReadIOData *CThreadPool::GetNewReadIOData( READ_IO_DATA_POOL_CONTEXT *const pContext )
  405. #endif // DPNBUILD_NOWINSOCK2 or DPNBUILD_ONLYWINSOCK2
  406. {
  407. CReadIOData * pTempReadData;
  408. #ifndef DPNBUILD_NOWINSOCK2
  409. OVERLAPPED * pOverlapped;
  410. #endif // ! DPNBUILD_NOWINSOCK2
  411. DNASSERT( pContext != NULL );
  412. //
  413. // initialize
  414. //
  415. pTempReadData = NULL;
  416. pContext->pThreadPool = this;
  417. pTempReadData = (CReadIOData*)g_ReadIODataPool.Get( pContext );
  418. if ( pTempReadData == NULL )
  419. {
  420. DPFX(DPFPREP, 0, "Failed to get new ReadIOData from pool!" );
  421. goto Failure;
  422. }
  423. //
  424. // we have data, immediately add a reference to it
  425. //
  426. pTempReadData->AddRef();
  427. DNASSERT( pTempReadData->m_pSourceSocketAddress != NULL );
  428. #ifndef DPNBUILD_NOWINSOCK2
  429. #ifndef DPNBUILD_ONLYWINSOCK2
  430. if (! fNeedOverlapped)
  431. {
  432. DNASSERT( pTempReadData->GetOverlapped() == NULL );
  433. }
  434. else
  435. #endif // ! DPNBUILD_ONLYWINSOCK2
  436. {
  437. HRESULT hr;
  438. #ifdef DPNBUILD_ONLYONEPROCESSOR
  439. hr = IDirectPlay8ThreadPoolWork_CreateOverlapped( m_pDPThreadPoolWork,
  440. 0,
  441. CSocketPort::Winsock2ReceiveComplete,
  442. pTempReadData,
  443. &pOverlapped,
  444. 0 );
  445. if (hr != DPN_OK)
  446. {
  447. DPFX(DPFPREP, 0, "Couldn't create overlapped structure (err = 0x%lx)!", hr);
  448. goto Failure;
  449. }
  450. #else // ! DPNBUILD_ONLYONEPROCESSOR
  451. hr = IDirectPlay8ThreadPoolWork_CreateOverlapped( m_pDPThreadPoolWork,
  452. pContext->dwCPU,
  453. CSocketPort::Winsock2ReceiveComplete,
  454. pTempReadData,
  455. &pOverlapped,
  456. 0 );
  457. if (hr != DPN_OK)
  458. {
  459. DPFX(DPFPREP, 0, "Couldn't create overlapped structure for CPU %i (err = 0x%lx)!",
  460. pContext->dwCPU, hr);
  461. goto Failure;
  462. }
  463. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  464. pTempReadData->SetOverlapped( pOverlapped );
  465. }
  466. #endif // ! DPNBUILD_NOWINSOCK2
  467. Exit:
  468. return pTempReadData;
  469. Failure:
  470. if ( pTempReadData != NULL )
  471. {
  472. pTempReadData->DecRef();
  473. pTempReadData = NULL;
  474. }
  475. goto Exit;
  476. }
  477. //**********************************************************************
  478. //**********************************************************************
  479. // ------------------------------
  480. // CThreadPool::ReturnReadIOData - return read data item to pool
  481. //
  482. // Entry: Pointer to read data
  483. //
  484. // Exit: Nothing
  485. // ------------------------------
  486. #undef DPF_MODNAME
  487. #define DPF_MODNAME "CThreadPool::ReturnReadIOData"
  488. void CThreadPool::ReturnReadIOData( CReadIOData *const pReadData )
  489. {
  490. DNASSERT( pReadData != NULL );
  491. DNASSERT( pReadData->m_pSourceSocketAddress != NULL );
  492. g_ReadIODataPool.Release( pReadData );
  493. }
  494. //**********************************************************************
  495. //**********************************************************************
  496. // ------------------------------
  497. // CThreadPool::SubmitTimerJob - add a timer job to the timer list
  498. //
  499. // Entry: Whether to perform immediately or not
  500. // Retry count
  501. // Boolean indicating that we retry forever
  502. // Retry interval
  503. // Boolean indicating that we wait forever
  504. // Idle wait interval
  505. // Pointer to callback when event fires
  506. // Pointer to callback when event complete
  507. // User context
  508. //
  509. // Exit: Error code
  510. // ------------------------------
  511. #undef DPF_MODNAME
  512. #define DPF_MODNAME "CThreadPool::SubmitTimerJob"
  513. #ifdef DPNBUILD_ONLYONEPROCESSOR
  514. HRESULT CThreadPool::SubmitTimerJob( const BOOL fPerformImmediately,
  515. const UINT_PTR uRetryCount,
  516. const BOOL fRetryForever,
  517. const DWORD dwRetryInterval,
  518. const BOOL fIdleWaitForever,
  519. const DWORD dwIdleTimeout,
  520. TIMER_EVENT_CALLBACK *const pTimerCallbackFunction,
  521. TIMER_EVENT_COMPLETE *const pTimerCompleteFunction,
  522. void *const pContext )
  523. #else // ! DPNBUILD_ONLYONEPROCESSOR
  524. HRESULT CThreadPool::SubmitTimerJob( const DWORD dwCPU,
  525. const BOOL fPerformImmediately,
  526. const UINT_PTR uRetryCount,
  527. const BOOL fRetryForever,
  528. const DWORD dwRetryInterval,
  529. const BOOL fIdleWaitForever,
  530. const DWORD dwIdleTimeout,
  531. TIMER_EVENT_CALLBACK *const pTimerCallbackFunction,
  532. TIMER_EVENT_COMPLETE *const pTimerCompleteFunction,
  533. void *const pContext )
  534. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  535. {
  536. HRESULT hr = DPN_OK;
  537. TIMER_OPERATION_ENTRY *pEntry = NULL;
  538. BOOL fTimerDataLocked = FALSE;
  539. #ifdef DPNBUILD_ONLYONEPROCESSOR
  540. DWORD dwCPU = -1;
  541. #endif // DPNBUILD_ONLYONEPROCESSOR
  542. DNASSERT( uRetryCount != 0 );
  543. DNASSERT( pTimerCallbackFunction != NULL );
  544. DNASSERT( pTimerCompleteFunction != NULL );
  545. DNASSERT( pContext != NULL ); // must be non-NULL because it's the lookup key to remove job
  546. //
  547. // allocate new enum entry
  548. //
  549. pEntry = static_cast<TIMER_OPERATION_ENTRY*>( g_TimerEntryPool.Get() );
  550. if ( pEntry == NULL )
  551. {
  552. hr = DPNERR_OUTOFMEMORY;
  553. DPFX(DPFPREP, 0, "Cannot allocate memory to add to timer list!" );
  554. goto Failure;
  555. }
  556. DNASSERT( pEntry->pContext == NULL );
  557. DPFX(DPFPREP, 7, "Created timer entry 0x%p (CPU %i, context 0x%p, immed. %i, tries %u, forever %i, interval %u, timeout %u, forever = %i).",
  558. pEntry, dwCPU, pContext, fPerformImmediately, uRetryCount, fRetryForever, dwRetryInterval, dwIdleTimeout, fIdleWaitForever);
  559. //
  560. // build timer entry block
  561. //
  562. pEntry->pContext = pContext;
  563. pEntry->uRetryCount = uRetryCount;
  564. pEntry->fRetryForever = fRetryForever;
  565. pEntry->dwRetryInterval = dwRetryInterval;
  566. pEntry->dwIdleTimeout = dwIdleTimeout;
  567. pEntry->fIdleWaitForever = fIdleWaitForever;
  568. pEntry->pTimerCallback = pTimerCallbackFunction;
  569. pEntry->pTimerComplete = pTimerCompleteFunction;
  570. pEntry->pThreadPool = this;
  571. pEntry->fCancelling = FALSE;
  572. #ifndef DPNBUILD_ONLYONEPROCESSOR
  573. pEntry->dwCPU = dwCPU;
  574. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  575. pEntry->dwNextRetryTime = GETTIMESTAMP();
  576. if (fPerformImmediately)
  577. {
  578. pEntry->dwNextRetryTime -= 1; // longest possible time, so that the time has already passed
  579. }
  580. else
  581. {
  582. pEntry->dwNextRetryTime += dwRetryInterval;
  583. }
  584. LockTimerData();
  585. fTimerDataLocked = TRUE;
  586. pEntry->pvTimerData = NULL;
  587. pEntry->uiTimerUnique = 0;
  588. if (fPerformImmediately)
  589. {
  590. hr = IDirectPlay8ThreadPoolWork_QueueWorkItem(m_pDPThreadPoolWork,
  591. dwCPU, // CPU
  592. CThreadPool::GenericTimerCallback, // callback
  593. pEntry, // user context
  594. 0); // flags
  595. if ( hr != DPN_OK )
  596. {
  597. DPFX(DPFPREP, 0, "Problem queueing immediate work item!" );
  598. DisplayDNError( 0, hr );
  599. goto Failure;
  600. }
  601. }
  602. else
  603. {
  604. hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
  605. dwCPU, // CPU
  606. dwRetryInterval, // delay
  607. CThreadPool::GenericTimerCallback, // callback
  608. pEntry, // user context
  609. &pEntry->pvTimerData, // timer data (returned)
  610. &pEntry->uiTimerUnique, // timer unique(returned)
  611. 0); // flags
  612. if ( hr != DPN_OK )
  613. {
  614. DPFX(DPFPREP, 0, "Problem scheduling timer!" );
  615. DisplayDNError( 0, hr );
  616. goto Failure;
  617. }
  618. }
  619. //
  620. // debug block to check for duplicate contexts
  621. //
  622. DEBUG_ONLY(
  623. {
  624. CBilink *pTempLink;
  625. pTempLink = m_TimerJobList.GetNext();
  626. while ( pTempLink != &m_TimerJobList )
  627. {
  628. TIMER_OPERATION_ENTRY *pTempTimerEntry;
  629. pTempTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempLink );
  630. DNASSERT( pTempTimerEntry->pContext != pContext );
  631. pTempLink = pTempLink->GetNext();
  632. }
  633. }
  634. );
  635. //
  636. // link to rest of list
  637. //
  638. pEntry->Linkage.InsertAfter( &m_TimerJobList );
  639. UnlockTimerData();
  640. fTimerDataLocked = FALSE;
  641. Exit:
  642. if ( hr != DPN_OK )
  643. {
  644. DPFX(DPFPREP, 0, "Problem with SubmitTimerJob" );
  645. DisplayDNError( 0, hr );
  646. }
  647. return hr;
  648. Failure:
  649. if ( pEntry != NULL )
  650. {
  651. g_TimerEntryPool.Release( pEntry );
  652. DEBUG_ONLY( pEntry = NULL );
  653. }
  654. if ( fTimerDataLocked != FALSE )
  655. {
  656. UnlockTimerData();
  657. fTimerDataLocked = FALSE;
  658. }
  659. goto Exit;
  660. }
  661. //**********************************************************************
  662. //**********************************************************************
  663. // ------------------------------
  664. // CThreadPool::StopTimerJob - remove timer job from list
  665. //
  666. // Entry: Pointer to job context (these MUST be unique for jobs)
  667. // Command result
  668. //
  669. // Exit: Boolean indicating whether a job was stopped or not
  670. //
  671. // Note: This function is for the forced removal of a job from the timed job
  672. // list. It is assumed that the caller of this function will clean
  673. // up any messes.
  674. // ------------------------------
  675. #undef DPF_MODNAME
  676. #define DPF_MODNAME "CThreadPool::StopTimerJob"
  677. BOOL CThreadPool::StopTimerJob( void *const pContext, const HRESULT hCommandResult )
  678. {
  679. BOOL fComplete = FALSE;
  680. CBilink * pTempEntry;
  681. TIMER_OPERATION_ENTRY * pTimerEntry = NULL;
  682. DNASSERT( pContext != NULL );
  683. DPFX(DPFPREP, 8, "Parameters (0x%p, 0x%lx)", pContext, hCommandResult);
  684. //
  685. // initialize
  686. //
  687. LockTimerData();
  688. pTempEntry = m_TimerJobList.GetNext();
  689. while ( pTempEntry != &m_TimerJobList )
  690. {
  691. pTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempEntry );
  692. if ( pTimerEntry->pContext == pContext )
  693. {
  694. HRESULT hr;
  695. //
  696. // Mark the entry as cancelling.
  697. //
  698. pTimerEntry->fCancelling = TRUE;
  699. //
  700. // Make sure an actual timer has been submitted.
  701. //
  702. if (pTimerEntry->pvTimerData != NULL)
  703. {
  704. //
  705. // Attempt to cancel the timer. If it succeeds, we're cool.
  706. //
  707. hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
  708. pTimerEntry->pvTimerData,
  709. pTimerEntry->uiTimerUnique,
  710. 0);
  711. }
  712. else
  713. {
  714. if ((! pTimerEntry->fIdleWaitForever) ||
  715. (pTimerEntry->uRetryCount > 0))
  716. {
  717. //
  718. // Timer hasn't been submitted yet, the completion function should
  719. // notice that it is now cancelling.
  720. //
  721. DPFX(DPFPREP, 1, "Timer for entry 0x%p not submitted yet, reporting that timer will still fire.",
  722. pTimerEntry);
  723. hr = DPNERR_CANNOTCANCEL;
  724. }
  725. else
  726. {
  727. //
  728. // No other timer will ever be submitted because the job is now
  729. // waiting forever.
  730. //
  731. DPFX(DPFPREP, 1, "Entry 0x%p was idling forever, cancelling.",
  732. pTimerEntry);
  733. hr = DPN_OK;
  734. }
  735. }
  736. if (hr != DPN_OK)
  737. {
  738. //
  739. // The processing function is still going to fire. It should notice
  740. // that it needs to complete.
  741. //
  742. }
  743. else
  744. {
  745. //
  746. // remove this link from the list
  747. //
  748. pTimerEntry->Linkage.RemoveFromList();
  749. fComplete = TRUE;
  750. }
  751. //
  752. // terminate loop
  753. //
  754. break;
  755. }
  756. pTempEntry = pTempEntry->GetNext();
  757. }
  758. UnlockTimerData();
  759. //
  760. // tell owner that the job is complete and return the job to the pool
  761. // outside of the lock
  762. //
  763. if (fComplete)
  764. {
  765. DPFX(DPFPREP, 8, "Found cancellable timer entry 0x%p (context 0x%p), completing with result 0x%lx.",
  766. pTimerEntry, pTimerEntry->pContext, hCommandResult);
  767. pTimerEntry->pTimerComplete( hCommandResult, pTimerEntry->pContext );
  768. //
  769. // Relock the timer list so we can safely put items back in the pool,
  770. //
  771. LockTimerData();
  772. g_TimerEntryPool.Release( pTimerEntry );
  773. UnlockTimerData();
  774. }
  775. DPFX(DPFPREP, 8, "Returning [%i]", fComplete);
  776. return fComplete;
  777. }
  778. //**********************************************************************
  779. //**********************************************************************
  780. // ------------------------------
  781. // CThreadPool::ModifyTimerJobNextRetryTime - update a timer job's next retry time
  782. //
  783. // Entry: Pointer to job context (these MUST be unique for jobs)
  784. // New time for next retry
  785. //
  786. // Exit: Boolean indicating whether a job was found & updated or not
  787. // ------------------------------
  788. #undef DPF_MODNAME
  789. #define DPF_MODNAME "CThreadPool::ModifyTimerJobNextRetryTime"
  790. BOOL CThreadPool::ModifyTimerJobNextRetryTime( void *const pContext,
  791. DWORD const dwNextRetryTime)
  792. {
  793. BOOL fFound;
  794. CBilink * pTempEntry;
  795. TIMER_OPERATION_ENTRY * pTimerEntry;
  796. INT_PTR iResult;
  797. DNASSERT( pContext != NULL );
  798. DPFX(DPFPREP, 7, "Parameters (0x%p, %u)", pContext, dwNextRetryTime);
  799. //
  800. // initialize
  801. //
  802. fFound = FALSE;
  803. LockTimerData();
  804. pTempEntry = m_TimerJobList.GetNext();
  805. while ( pTempEntry != &m_TimerJobList )
  806. {
  807. pTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempEntry );
  808. if ( pTimerEntry->pContext == pContext )
  809. {
  810. iResult = (int) (pTimerEntry->dwNextRetryTime - dwNextRetryTime);
  811. if (iResult != 0)
  812. {
  813. HRESULT hr;
  814. DWORD dwNextRetryTimeDifference;
  815. DWORD dwNewRetryInterval;
  816. if (iResult < 0)
  817. {
  818. //
  819. // Next time to fire is now later.
  820. //
  821. dwNextRetryTimeDifference = dwNextRetryTime - pTimerEntry->dwNextRetryTime;
  822. dwNewRetryInterval = pTimerEntry->dwRetryInterval + dwNextRetryTimeDifference;
  823. DPFX(DPFPREP, 7, "Timer 0x%p next retry time delayed by %u ms from offset %u to offset %u, modifying interval from %u to %u.",
  824. pTimerEntry,
  825. dwNextRetryTimeDifference,
  826. pTimerEntry->dwNextRetryTime,
  827. dwNextRetryTime,
  828. pTimerEntry->dwRetryInterval,
  829. dwNewRetryInterval);
  830. }
  831. else
  832. {
  833. //
  834. // Next time to fire is now earlier.
  835. //
  836. dwNextRetryTimeDifference = pTimerEntry->dwNextRetryTime - dwNextRetryTime;
  837. dwNewRetryInterval = pTimerEntry->dwRetryInterval - dwNextRetryTimeDifference;
  838. DPFX(DPFPREP, 7, "Timer 0x%p next retry time moved up by %u ms from offset %u to offset %u, modifying interval from %u to %u.",
  839. pTimerEntry,
  840. dwNextRetryTimeDifference,
  841. pTimerEntry->dwNextRetryTime,
  842. dwNextRetryTime,
  843. pTimerEntry->dwRetryInterval,
  844. dwNewRetryInterval);
  845. }
  846. //
  847. // Force the timer to expire right away if the calculations returned a really
  848. // long delay.
  849. //
  850. if (dwNewRetryInterval > 0x80000000)
  851. {
  852. DPFX(DPFPREP, 1, "Timer 0x%p delay 0x%x/%u (next retry time %u) being set to 0.",
  853. pTimerEntry, dwNewRetryInterval, dwNewRetryInterval, dwNextRetryTime);
  854. pTimerEntry->dwRetryInterval = 0;
  855. pTimerEntry->dwNextRetryTime = GETTIMESTAMP();
  856. }
  857. else
  858. {
  859. pTimerEntry->dwRetryInterval = dwNewRetryInterval;
  860. pTimerEntry->dwNextRetryTime = dwNextRetryTime;
  861. }
  862. //
  863. // Attempt to cancel the existing timer.
  864. //
  865. hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
  866. pTimerEntry->pvTimerData,
  867. pTimerEntry->uiTimerUnique,
  868. 0);
  869. if (hr != DPN_OK)
  870. {
  871. DPFX(DPFPREP, 1, "Couldn't cancel existing timer for entry 0x%p (err = 0x%lx), modifying retry timer only.",
  872. pTimerEntry, hr);
  873. }
  874. else
  875. {
  876. //
  877. // Restart the timer.
  878. //
  879. #ifdef DPNBUILD_ONLYONEPROCESSOR
  880. hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
  881. -1, // pick any CPU
  882. dwNewRetryInterval, // delay
  883. CThreadPool::GenericTimerCallback, // callback
  884. pTimerEntry, // user context
  885. &pTimerEntry->pvTimerData, // timer data (returned)
  886. &pTimerEntry->uiTimerUnique, // timer unique (returned)
  887. 0); // flags
  888. #else // ! DPNBUILD_ONLYONEPROCESSOR
  889. hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
  890. pTimerEntry->dwCPU, // use same CPU as before
  891. dwNewRetryInterval, // delay
  892. CThreadPool::GenericTimerCallback, // callback
  893. pTimerEntry, // user context
  894. &pTimerEntry->pvTimerData, // timer data (returned)
  895. &pTimerEntry->uiTimerUnique, // timer unique (returned)
  896. 0); // flags
  897. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  898. if (hr != DPN_OK)
  899. {
  900. DPFX(DPFPREP, 0, "Couldn't reschedule timer for entry 0x%p (err = 0x%lx)!",
  901. pTimerEntry, hr);
  902. pTimerEntry->pvTimerData = NULL;
  903. //
  904. // Drop lock while we complete timer.
  905. //
  906. UnlockTimerData();
  907. pTimerEntry->pTimerComplete(hr, pTimerEntry->pContext);
  908. g_TimerEntryPool.Release(pTimerEntry);
  909. LockTimerData();
  910. //
  911. // Drop through...
  912. //
  913. }
  914. }
  915. }
  916. else
  917. {
  918. //
  919. // The intervals are the same, no change necessary.
  920. //
  921. DPFX(DPFPREP, 7, "Timer 0x%p next retry time was unchanged (offset %u), not changing interval from %u.",
  922. pTimerEntry,
  923. pTimerEntry->dwNextRetryTime,
  924. pTimerEntry->dwRetryInterval);
  925. }
  926. fFound = TRUE;
  927. //
  928. // Terminate loop
  929. //
  930. break;
  931. }
  932. pTempEntry = pTempEntry->GetNext();
  933. }
  934. UnlockTimerData();
  935. DPFX(DPFPREP, 7, "Returning [%i]", fFound);
  936. return fFound;
  937. }
  938. //**********************************************************************
  939. #ifndef DPNBUILD_ONLYONETHREAD
  940. //**********************************************************************
  941. // ------------------------------
  942. // CThreadPool::SubmitBlockingJob - submits a blocking job to the be processed by the blockable thread
  943. // duplicate commands (matching callback and context) are disallowed
  944. //
  945. // Entry: Pointer to callback that executes blocking job
  946. // User context
  947. //
  948. // Exit: Error code
  949. // ------------------------------
  950. #undef DPF_MODNAME
  951. #define DPF_MODNAME "CThreadPool::SubmitBlockingJob"
  952. HRESULT CThreadPool::SubmitBlockingJob( BLOCKING_JOB_CALLBACK *const pfnBlockingJobCallback,
  953. void *const pvContext )
  954. {
  955. HRESULT hr;
  956. BLOCKING_JOB * pJob = NULL;
  957. DWORD dwTemp;
  958. BOOL fQueueLocked = FALSE;
  959. CBilink * pBilink;
  960. BLOCKING_JOB * pExistingJob;
  961. //
  962. // allocate new enum entry
  963. //
  964. pJob = (BLOCKING_JOB*) g_BlockingJobPool.Get();
  965. if (pJob == NULL)
  966. {
  967. DPFX(DPFPREP, 0, "Cannot allocate memory for blocking job!" );
  968. hr = DPNERR_OUTOFMEMORY;
  969. goto Failure;
  970. }
  971. DPFX(DPFPREP, 6, "Created blocking job 0x%p (callback 0x%p, context 0x%p).",
  972. pJob, pfnBlockingJobCallback, pvContext);
  973. pJob->Linkage.Initialize();
  974. pJob->pfnBlockingJobCallback = pfnBlockingJobCallback;
  975. pJob->pvContext = pvContext;
  976. DNEnterCriticalSection(&m_csBlockingJobLock);
  977. fQueueLocked = TRUE;
  978. //
  979. // Start the blocking job thread, if we haven't already.
  980. //
  981. if (m_hBlockingJobThread == NULL)
  982. {
  983. m_hBlockingJobEvent = DNCreateEvent(NULL, FALSE, FALSE, NULL);
  984. if (m_hBlockingJobEvent == NULL)
  985. {
  986. #ifdef DBG
  987. dwTemp = GetLastError();
  988. DPFX(DPFPREP, 0, "Couldn't create blocking job event (err = %u)!", dwTemp);
  989. #endif // DBG
  990. hr = DPNERR_OUTOFMEMORY;
  991. goto Failure;
  992. }
  993. m_hBlockingJobThread = DNCreateThread(NULL,
  994. 0,
  995. DPNBlockingJobThreadProc,
  996. this,
  997. 0,
  998. &dwTemp);
  999. if (m_hBlockingJobThread == NULL)
  1000. {
  1001. #ifdef DBG
  1002. dwTemp = GetLastError();
  1003. DPFX(DPFPREP, 0, "Couldn't create blocking job thread (err = %u)!", dwTemp);
  1004. #endif // DBG
  1005. DNCloseHandle(m_hBlockingJobEvent);
  1006. m_hBlockingJobEvent = NULL;
  1007. hr = DPNERR_OUTOFMEMORY;
  1008. goto Failure;
  1009. }
  1010. DNASSERT(m_blBlockingJobQueue.IsEmpty());
  1011. }
  1012. else
  1013. {
  1014. pBilink = m_blBlockingJobQueue.GetNext();
  1015. while (pBilink != &m_blBlockingJobQueue)
  1016. {
  1017. pExistingJob = CONTAINING_OBJECT(pBilink, BLOCKING_JOB, Linkage);
  1018. if ((pExistingJob->pfnBlockingJobCallback == pfnBlockingJobCallback) &&
  1019. (pExistingJob->pvContext == pvContext))
  1020. {
  1021. DPFX(DPFPREP, 1, "Existing blocking job 0x%p matches new job 0x%p, not submitting.",
  1022. pExistingJob, pJob);
  1023. hr = DPNERR_DUPLICATECOMMAND;
  1024. goto Failure;
  1025. }
  1026. pBilink = pBilink->GetNext();
  1027. }
  1028. }
  1029. //
  1030. // Add job to queue.
  1031. //
  1032. pJob->Linkage.InsertBefore(&m_blBlockingJobQueue);
  1033. DNLeaveCriticalSection(&m_csBlockingJobLock);
  1034. fQueueLocked = FALSE;
  1035. //
  1036. // Alert the thread.
  1037. //
  1038. DNSetEvent( m_hBlockingJobEvent );
  1039. hr = DPN_OK;
  1040. Exit:
  1041. return hr;
  1042. Failure:
  1043. if (fQueueLocked)
  1044. {
  1045. DNLeaveCriticalSection(&m_csBlockingJobLock);
  1046. fQueueLocked = FALSE;
  1047. }
  1048. if (pJob != NULL)
  1049. {
  1050. g_BlockingJobPool.Release(pJob);
  1051. pJob = NULL;
  1052. }
  1053. goto Exit;
  1054. }
  1055. //**********************************************************************
  1056. //**********************************************************************
  1057. // ------------------------------
  1058. // CThreadPool::DoBlockingJobs - processes all blocking jobs.
  1059. //
  1060. // Entry: Nothing
  1061. //
  1062. // Exit: Nothing
  1063. // ------------------------------
  1064. #undef DPF_MODNAME
  1065. #define DPF_MODNAME "CThreadPool::DoBlockingJobs"
  1066. void CThreadPool::DoBlockingJobs( void )
  1067. {
  1068. BOOL fRunning = TRUE;
  1069. CBilink * pBilink;
  1070. BLOCKING_JOB * pJob;
  1071. //
  1072. // Keep looping until it's time to shutdown.
  1073. //
  1074. while (fRunning)
  1075. {
  1076. //
  1077. // Wait for more work.
  1078. //
  1079. DNWaitForSingleObject( m_hBlockingJobEvent, INFINITE );
  1080. //
  1081. // Keep looping while we have work.
  1082. //
  1083. do
  1084. {
  1085. DNEnterCriticalSection(&m_csBlockingJobLock);
  1086. pBilink = m_blBlockingJobQueue.GetNext();
  1087. if (pBilink == &m_blBlockingJobQueue)
  1088. {
  1089. //
  1090. // Bail out of the inner loop.
  1091. //
  1092. DNLeaveCriticalSection(&m_csBlockingJobLock);
  1093. break;
  1094. }
  1095. pJob = CONTAINING_OBJECT(pBilink, BLOCKING_JOB, Linkage);
  1096. pJob->Linkage.RemoveFromList();
  1097. DNLeaveCriticalSection(&m_csBlockingJobLock);
  1098. //
  1099. // Bail out of both loops if it's the quit job.
  1100. //
  1101. if (pJob->pfnBlockingJobCallback == NULL)
  1102. {
  1103. DPFX(DPFPREP, 5, "Recognized quit job 0x%p.", pJob);
  1104. g_BlockingJobPool.Release(pJob);
  1105. fRunning = FALSE;
  1106. break;
  1107. }
  1108. DPFX(DPFPREP, 6, "Processing blocking job 0x%p (callback 0x%p, context 0x%p).",
  1109. pJob, pJob->pfnBlockingJobCallback, pJob->pvContext);
  1110. pJob->pfnBlockingJobCallback(pJob->pvContext);
  1111. DPFX(DPFPREP, 7, "Returning blocking job 0x%p to pool.", pJob);
  1112. g_BlockingJobPool.Release(pJob);
  1113. }
  1114. while (TRUE);
  1115. }
  1116. }
  1117. //**********************************************************************
  1118. #endif // ! DPNBUILD_ONLYONETHREAD
  1119. #ifndef DPNBUILD_NOSPUI
  1120. //**********************************************************************
  1121. // ------------------------------
  1122. // CThreadPool::SpawnDialogThread - start a secondary thread to display service
  1123. // provider UI.
  1124. //
  1125. // Entry: Pointer to dialog function
  1126. // Dialog context
  1127. //
  1128. // Exit: Error code
  1129. // ------------------------------
  1130. #undef DPF_MODNAME
  1131. #define DPF_MODNAME "CThreadPool::SpawnDialogThread"
  1132. HRESULT CThreadPool::SpawnDialogThread( DIALOG_FUNCTION *const pDialogFunction, void *const pDialogContext )
  1133. {
  1134. HRESULT hr;
  1135. DNHANDLE hDialogThread;
  1136. DIALOG_THREAD_PARAM *pThreadParam;
  1137. DWORD dwThreadID;
  1138. DNASSERT( pDialogFunction != NULL );
  1139. DNASSERT( pDialogContext != NULL ); // why would anyone not want a dialog context??
  1140. //
  1141. // initialize
  1142. //
  1143. hr = DPN_OK;
  1144. pThreadParam = NULL;
  1145. //
  1146. // create and initialize thread param
  1147. //
  1148. pThreadParam = static_cast<DIALOG_THREAD_PARAM*>( DNMalloc( sizeof( *pThreadParam ) ) );
  1149. if ( pThreadParam == NULL )
  1150. {
  1151. hr = DPNERR_OUTOFMEMORY;
  1152. DPFX(DPFPREP, 0, "Failed to allocate memory for dialog thread!" );
  1153. goto Failure;
  1154. }
  1155. pThreadParam->pDialogFunction = pDialogFunction;
  1156. pThreadParam->pContext = pDialogContext;
  1157. pThreadParam->pThisThreadPool = this;
  1158. //
  1159. // create thread
  1160. //
  1161. hDialogThread = DNCreateThread( NULL, // pointer to security (none)
  1162. 0, // stack size (default)
  1163. DialogThreadProc, // thread procedure
  1164. pThreadParam, // thread param
  1165. 0, // creation flags (none)
  1166. &dwThreadID ); // pointer to thread ID
  1167. if ( hDialogThread == NULL )
  1168. {
  1169. DWORD dwError;
  1170. dwError = GetLastError();
  1171. DPFX(DPFPREP, 0, "Failed to start dialog thread!" );
  1172. DisplayErrorCode( 0, dwError );
  1173. goto Failure;
  1174. }
  1175. if ( DNCloseHandle( hDialogThread ) == FALSE )
  1176. {
  1177. DWORD dwError;
  1178. dwError = GetLastError();
  1179. DPFX(DPFPREP, 0, "Problem closing handle from create dialog thread!" );
  1180. DisplayErrorCode( 0, dwError );
  1181. }
  1182. Exit:
  1183. return hr;
  1184. Failure:
  1185. if ( pThreadParam != NULL )
  1186. {
  1187. DNFree( pThreadParam );
  1188. pThreadParam = NULL;
  1189. }
  1190. goto Exit;
  1191. }
  1192. //**********************************************************************
  1193. #endif // !DPNBUILD_NOSPUI
  1194. #ifndef DPNBUILD_ONLYONETHREAD
  1195. //**********************************************************************
  1196. // ------------------------------
  1197. // CThreadPool::GetIOThreadCount - get I/O thread count
  1198. //
  1199. // Entry: Pointer to variable to fill
  1200. //
  1201. // Exit: Error code
  1202. // ------------------------------
  1203. #undef DPF_MODNAME
  1204. #define DPF_MODNAME "CThreadPool::GetIOThreadCount"
  1205. HRESULT CThreadPool::GetIOThreadCount( LONG *const piThreadCount )
  1206. {
  1207. HRESULT hr;
  1208. DWORD dwThreadPoolCount;
  1209. DNASSERT( piThreadCount != NULL );
  1210. Lock();
  1211. hr = IDirectPlay8ThreadPoolWork_GetThreadCount(m_pDPThreadPoolWork,
  1212. -1,
  1213. &dwThreadPoolCount,
  1214. 0);
  1215. switch (hr)
  1216. {
  1217. case DPN_OK:
  1218. {
  1219. *piThreadCount = dwThreadPoolCount;
  1220. DPFX(DPFPREP, 6, "User has explicitly started %u threads.", (*piThreadCount));
  1221. break;
  1222. }
  1223. case DPNSUCCESS_PENDING:
  1224. {
  1225. if ((IsThreadCountReductionAllowed()) &&
  1226. (((DWORD) GetIntendedThreadCount()) > dwThreadPoolCount))
  1227. {
  1228. *piThreadCount = GetIntendedThreadCount();
  1229. DPFX(DPFPREP, 6, "Thread pool not locked down and only %u threads currently started, using intended count of %u.",
  1230. dwThreadPoolCount, (*piThreadCount));
  1231. }
  1232. else
  1233. {
  1234. *piThreadCount = dwThreadPoolCount;
  1235. DPFX(DPFPREP, 6, "Thread pool locked down (%i) or more than %u threads already started, using actual count of %u.",
  1236. (! IsThreadCountReductionAllowed()), GetIntendedThreadCount(),
  1237. (*piThreadCount));
  1238. }
  1239. hr = DPN_OK;
  1240. break;
  1241. }
  1242. case DPNERR_NOTREADY:
  1243. {
  1244. DNASSERT(IsThreadCountReductionAllowed());
  1245. *piThreadCount = GetIntendedThreadCount();
  1246. DPFX(DPFPREP, 6, "Thread pool does not have a thread count set, using intended count of %u.",
  1247. (*piThreadCount));
  1248. hr = DPN_OK;
  1249. break;
  1250. }
  1251. default:
  1252. {
  1253. DPFX(DPFPREP, 0, "Failed getting thread count!");
  1254. break;
  1255. }
  1256. }
  1257. Unlock();
  1258. return hr;
  1259. }
  1260. //**********************************************************************
  1261. //**********************************************************************
  1262. // ------------------------------
  1263. // CThreadPool::SetIOThreadCount - set I/O thread count
  1264. //
  1265. // Entry: New thread count
  1266. //
  1267. // Exit: Error code
  1268. // ------------------------------
  1269. #undef DPF_MODNAME
  1270. #define DPF_MODNAME "CThreadPool::SetIOThreadCount"
  1271. HRESULT CThreadPool::SetIOThreadCount( const LONG iThreadCount )
  1272. {
  1273. HRESULT hr;
  1274. DWORD dwThreadCount;
  1275. DNASSERT( iThreadCount > 0 );
  1276. //
  1277. // initialize
  1278. //
  1279. hr = DPN_OK;
  1280. Lock();
  1281. if ( IsThreadCountReductionAllowed() )
  1282. {
  1283. DPFX(DPFPREP, 4, "Thread pool not locked down, setting intended thread count to %i.", iThreadCount );
  1284. SetIntendedThreadCount( iThreadCount );
  1285. }
  1286. else
  1287. {
  1288. hr = IDirectPlay8ThreadPoolWork_GetThreadCount(m_pDPThreadPoolWork,
  1289. -1,
  1290. &dwThreadCount,
  1291. 0);
  1292. switch (hr)
  1293. {
  1294. case DPN_OK:
  1295. case DPNSUCCESS_PENDING:
  1296. case DPNERR_NOTREADY:
  1297. {
  1298. if ( (DWORD) iThreadCount != dwThreadCount )
  1299. {
  1300. if (hr == DPN_OK)
  1301. {
  1302. DPFX(DPFPREP, 1, "Thread count already set to %u by user, not changing to %i total.",
  1303. dwThreadCount, iThreadCount);
  1304. hr = DPNERR_NOTALLOWED;
  1305. }
  1306. else
  1307. {
  1308. //
  1309. // Artificially prevent thread pool reduction after operations have
  1310. // started, to simulate pre-unified-threadpool behavior. Only
  1311. // request a thread count change if the new count is greater
  1312. // than the old count.
  1313. //
  1314. if ( (DWORD) iThreadCount > dwThreadCount )
  1315. {
  1316. hr = IDirectPlay8ThreadPoolWork_RequestTotalThreadCount(m_pDPThreadPoolWork,
  1317. (DWORD) iThreadCount,
  1318. 0);
  1319. }
  1320. else
  1321. {
  1322. DPFX(DPFPREP, 4, "Thread pool locked down and already has %u threads, not adjusting to %i.",
  1323. dwThreadCount, iThreadCount );
  1324. hr = DPN_OK;
  1325. }
  1326. }
  1327. }
  1328. else
  1329. {
  1330. DPFX(DPFPREP, 4, "Thread pool thread count matches (%u).",
  1331. dwThreadCount);
  1332. hr = DPN_OK;
  1333. }
  1334. break;
  1335. }
  1336. default:
  1337. {
  1338. DPFX(DPFPREP, 0, "Getting current thread count failed!");
  1339. break;
  1340. }
  1341. }
  1342. }
  1343. Unlock();
  1344. return hr;
  1345. }
  1346. //**********************************************************************
  1347. #endif // ! DPNBUILD_ONLYONETHREAD
  1348. //**********************************************************************
  1349. // ------------------------------
  1350. // CThreadPool::PreventThreadPoolReduction - prevents the thread pool size from being reduced
  1351. //
  1352. // Entry: None
  1353. //
  1354. // Exit: Error code
  1355. // ------------------------------
  1356. #undef DPF_MODNAME
  1357. #define DPF_MODNAME "CThreadPool::PreventThreadPoolReduction"
  1358. HRESULT CThreadPool::PreventThreadPoolReduction( void )
  1359. {
  1360. HRESULT hr;
  1361. #ifndef DPNBUILD_ONLYONETHREAD
  1362. LONG iDesiredThreads;
  1363. #endif // ! DPNBUILD_ONLYONETHREAD
  1364. #ifdef _XBOX
  1365. DWORD dwStatus;
  1366. DWORD dwInterval;
  1367. XNADDR xnaddr;
  1368. #endif // _XBOX
  1369. #ifdef DBG
  1370. DWORD dwStartTime;
  1371. #endif // DBG
  1372. Lock();
  1373. //
  1374. // If we haven't already clamped down, do so, and spin up the threads.
  1375. //
  1376. if ( IsThreadCountReductionAllowed() )
  1377. {
  1378. m_fAllowThreadCountReduction = FALSE;
  1379. #ifdef DPNBUILD_ONLYONETHREAD
  1380. DPFX(DPFPREP, 3, "Locking down thread pool." );
  1381. #else // ! DPNBUILD_ONLYONETHREAD
  1382. iDesiredThreads = GetIntendedThreadCount();
  1383. DNASSERT( iDesiredThreads > 0 );
  1384. SetIntendedThreadCount( 0 );
  1385. DPFX(DPFPREP, 3, "Locking down thread count at %i.", iDesiredThreads );
  1386. #endif // ! DPNBUILD_ONLYONETHREAD
  1387. #ifndef DPNBUILD_ONLYONETHREAD
  1388. #ifdef DBG
  1389. dwStartTime = GETTIMESTAMP();
  1390. #endif // DBG
  1391. //
  1392. // Have the thread pool object try to start the requested number of threads.
  1393. //
  1394. // We'll ignore failure, because we could still operate in DoWork mode even
  1395. // when starting the threads fails. It most likely failed because the user
  1396. // is in that mode already anyway (DPNERR_ALREADYINITIALIZED).
  1397. //
  1398. hr = IDirectPlay8ThreadPoolWork_RequestTotalThreadCount(m_pDPThreadPoolWork,
  1399. iDesiredThreads,
  1400. 0);
  1401. if (hr != DPN_OK)
  1402. {
  1403. if (hr != DPNERR_ALREADYINITIALIZED)
  1404. {
  1405. DPFX(DPFPREP, 0, "Requesting thread count failed (err = 0x%lx)!", hr);
  1406. }
  1407. //
  1408. // Continue...
  1409. //
  1410. }
  1411. #ifdef DBG
  1412. DPFX(DPFPREP, 8, "Spent %u ms trying to start %i threads.",
  1413. (GETTIMESTAMP() - dwStartTime), iDesiredThreads);
  1414. #endif // DBG
  1415. #endif // ! DPNBUILD_ONLYONETHREAD
  1416. #ifdef _XBOX
  1417. #ifdef DBG
  1418. dwStartTime = GETTIMESTAMP();
  1419. #endif // DBG
  1420. #pragma TODO(vanceo, "Use #defines")
  1421. //
  1422. // Wait until the Ethernet link is active.
  1423. //
  1424. DPFX(DPFPREP, 1, "Ensuring Ethernet link status is active...");
  1425. dwStatus = XNetGetEthernetLinkStatus();
  1426. dwInterval = 5;
  1427. while (! (dwStatus & XNET_ETHERNET_LINK_ACTIVE))
  1428. {
  1429. if (dwInterval > 100)
  1430. {
  1431. DPFX(DPFPREP, 0, "Ethernet link never became ready (status = 0x%x)!",
  1432. dwStatus);
  1433. hr = DPNERR_NOCONNECTION;
  1434. goto Failure;
  1435. }
  1436. DPFX(DPFPREP, 0, "Ethernet link is not ready (0x%x).", dwStatus);
  1437. IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork, dwInterval, 0);
  1438. dwInterval += 5;
  1439. dwStatus = XNetGetEthernetLinkStatus();
  1440. }
  1441. //
  1442. // Wait until a DHCP address has been acquired or we give up trying.
  1443. // If we're not using DHCP, this should return something other than
  1444. // XNET_GET_XNADDR_PENDING right away.
  1445. //
  1446. DPFX(DPFPREP, 1, "Waiting for a valid address...");
  1447. dwStatus = XNetGetTitleXnAddr(&xnaddr);
  1448. dwInterval = 5;
  1449. while (dwStatus == XNET_GET_XNADDR_PENDING)
  1450. {
  1451. if (dwInterval > 225)
  1452. {
  1453. DPFX(DPFPREP, 0, "Never acquired an address (status = 0x%x)!",
  1454. dwStatus);
  1455. hr = DPNERR_NOTREADY;
  1456. goto Failure;
  1457. }
  1458. IDirectPlay8ThreadPoolWork_SleepWhileWorking(m_pDPThreadPoolWork, dwInterval, 0);
  1459. dwInterval += 5;
  1460. dwStatus = XNetGetTitleXnAddr(&xnaddr);
  1461. }
  1462. if (dwStatus == XNET_GET_XNADDR_NONE)
  1463. {
  1464. DPFX(DPFPREP, 0, "Couldn't get an address!");
  1465. hr = DPNERR_NOCONNECTION;
  1466. goto Failure;
  1467. }
  1468. DPFX(DPFPREP, 1, "Network ready.");
  1469. #pragma TODO(vanceo, "Ethernet link status timer?")
  1470. #ifdef DBG
  1471. DPFX(DPFPREP, 8, "Spent %u ms waiting for network.",
  1472. (GETTIMESTAMP() - dwStartTime));
  1473. #endif // DBG
  1474. #endif // _XBOX
  1475. }
  1476. else
  1477. {
  1478. DPFX(DPFPREP, 3, "Thread count already locked down." );
  1479. }
  1480. //
  1481. // If we're here, everything is successful.
  1482. //
  1483. hr = DPN_OK;
  1484. #ifdef _XBOX
  1485. Exit:
  1486. #endif // _XBOX
  1487. Unlock();
  1488. return hr;
  1489. #ifdef _XBOX
  1490. Failure:
  1491. //
  1492. // The only time we can fail in this function is if we disabled thread
  1493. // count reduction. In order to return to the previous state, re-enable
  1494. // reduction.
  1495. //
  1496. m_fAllowThreadCountReduction = TRUE;
  1497. goto Exit;
  1498. #endif // _XBOX
  1499. }
  1500. //**********************************************************************
  1501. #if ((! defined(DPNBUILD_NOMULTICAST)) && (defined(WINNT)))
  1502. //**********************************************************************
  1503. // ------------------------------
  1504. // CThreadPool::EnsureMadcapLoaded - Load the MADCAP API if it hasn't been
  1505. // already, and it can be loaded.
  1506. //
  1507. // Entry: Nothing
  1508. //
  1509. // Exit: TRUE if MADCAP is loaded, FALSE otherwise
  1510. // ------------------------------
  1511. #undef DPF_MODNAME
  1512. #define DPF_MODNAME "CThreadPool::EnsureMadcapLoaded"
  1513. BOOL CThreadPool::EnsureMadcapLoaded( void )
  1514. {
  1515. BOOL fReturn;
  1516. DPFX(DPFPREP, 7, "Enter");
  1517. #ifndef DPNBUILD_NOREGISTRY
  1518. if (! g_fDisableMadcapSupport)
  1519. #endif // ! DPNBUILD_NOREGISTRY
  1520. {
  1521. Lock();
  1522. if (! IsMadcapLoaded())
  1523. {
  1524. if ( LoadMadcap() == FALSE )
  1525. {
  1526. DPFX(DPFPREP, 0, "Failed to load MADCAP API, continuing." );
  1527. fReturn = FALSE;
  1528. }
  1529. else
  1530. {
  1531. m_fMadcapLoaded = TRUE;
  1532. fReturn = TRUE;
  1533. }
  1534. }
  1535. else
  1536. {
  1537. DPFX(DPFPREP, 4, "MADCAP already loaded." );
  1538. fReturn = TRUE;
  1539. }
  1540. Unlock();
  1541. }
  1542. #ifndef DPNBUILD_NOREGISTRY
  1543. else
  1544. {
  1545. DPFX(DPFPREP, 0, "Not loading MADCAP API." );
  1546. fReturn = FALSE;
  1547. }
  1548. #endif // ! DPNBUILD_NOREGISTRY
  1549. DPFX(DPFPREP, 7, "Return [%i]", fReturn);
  1550. return fReturn;
  1551. }
  1552. #endif // ! DPNBUILD_NOMULTICAST and WINNT
  1553. #ifndef DPNBUILD_NONATHELP
  1554. //**********************************************************************
  1555. // ------------------------------
  1556. // CThreadPool::EnsureNATHelpLoaded - Load NAT Help if it hasn't been already.
  1557. // This has no return values, so if NAT
  1558. // traversal is explicitly disabled, or some
  1559. // error occurs, NAT Help will not actually
  1560. // get loaded.
  1561. //
  1562. // Entry: Nothing
  1563. //
  1564. // Exit: Nothing
  1565. // ------------------------------
  1566. #undef DPF_MODNAME
  1567. #define DPF_MODNAME "CThreadPool::EnsureNATHelpLoaded"
  1568. void CThreadPool::EnsureNATHelpLoaded( void )
  1569. {
  1570. HRESULT hr;
  1571. DWORD dwTemp;
  1572. DPNHCAPS dpnhcaps;
  1573. DWORD dwNATHelpRetryTime;
  1574. #ifdef DBG
  1575. DWORD dwStartTime;
  1576. #endif // DBG
  1577. DPFX(DPFPREP, 7, "Enter");
  1578. #ifndef DPNBUILD_NOREGISTRY
  1579. if ((! g_fDisableDPNHGatewaySupport) || (! g_fDisableDPNHFirewallSupport))
  1580. #endif // ! DPNBUILD_NOREGISTRY
  1581. {
  1582. Lock();
  1583. if ( ! IsNATHelpLoaded() )
  1584. {
  1585. //
  1586. // Attempt to load the NAT helper(s).
  1587. //
  1588. if ( LoadNATHelp() )
  1589. {
  1590. m_fNATHelpLoaded = TRUE;
  1591. #ifdef DBG
  1592. dwStartTime = GETTIMESTAMP();
  1593. #endif // DBG
  1594. //
  1595. // Initialize the timer values.
  1596. //
  1597. dwNATHelpRetryTime = -1;
  1598. //
  1599. // Loop through each NAT help object.
  1600. //
  1601. for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
  1602. {
  1603. if (g_papNATHelpObjects[dwTemp] != NULL)
  1604. {
  1605. //
  1606. // Determine how often to refresh the NAT help caps in the future.
  1607. //
  1608. // We're going to force server detection now. This will increase the time
  1609. // it takes to startup up this Enum/Connect/Listen operation, but is
  1610. // necessary since the IDirectPlayNATHelp::GetRegisteredAddresses call in
  1611. // CSocketPort::BindToInternetGateway and possibly the
  1612. // IDirectPlayNATHelp::QueryAddress call in CSocketPort::MungePublicAddress
  1613. // could occur before the first NATHelp GetCaps timer fires.
  1614. // In the vast majority of NAT cases, the gateway is already available.
  1615. // If we hadn't detected that yet (because we hadn't called
  1616. // IDirectPlayNATHelp::GetCaps with DPNHGETCAPS_UPDATESERVERSTATUS)
  1617. // then we would get an incorrect result from GetRegisteredAddresses or
  1618. // QueryAddress.
  1619. //
  1620. ZeroMemory(&dpnhcaps, sizeof(dpnhcaps));
  1621. dpnhcaps.dwSize = sizeof(dpnhcaps);
  1622. hr = IDirectPlayNATHelp_GetCaps(g_papNATHelpObjects[dwTemp],
  1623. &dpnhcaps,
  1624. DPNHGETCAPS_UPDATESERVERSTATUS);
  1625. if (FAILED(hr))
  1626. {
  1627. DPFX(DPFPREP, 0, "Failed getting NAT Help capabilities (error = 0x%lx), continuing.",
  1628. hr);
  1629. //
  1630. // NAT Help will probably not work correctly, but that won't prevent
  1631. // local connections from working. Consider it non-fatal.
  1632. //
  1633. }
  1634. else
  1635. {
  1636. //
  1637. // See if this is the shortest interval.
  1638. //
  1639. if (dpnhcaps.dwRecommendedGetCapsInterval < dwNATHelpRetryTime)
  1640. {
  1641. dwNATHelpRetryTime = dpnhcaps.dwRecommendedGetCapsInterval;
  1642. }
  1643. #ifndef DPNBUILD_NOLOCALNAT
  1644. //
  1645. // Remember if there's a local NAT.
  1646. //
  1647. if ((dpnhcaps.dwFlags & DPNHCAPSFLAG_GATEWAYPRESENT) &&
  1648. (dpnhcaps.dwFlags & DPNHCAPSFLAG_GATEWAYISLOCAL))
  1649. {
  1650. g_fLocalNATDetectedAtStartup = TRUE;
  1651. }
  1652. else
  1653. {
  1654. g_fLocalNATDetectedAtStartup = FALSE;
  1655. }
  1656. #endif // ! DPNBUILD_NOLOCALNAT
  1657. }
  1658. }
  1659. else
  1660. {
  1661. //
  1662. // No object loaded in this slot.
  1663. //
  1664. }
  1665. }
  1666. //
  1667. // If there's a retry interval, submit a timer job.
  1668. //
  1669. if (dwNATHelpRetryTime != -1)
  1670. {
  1671. //
  1672. // Attempt to add timer job that will refresh the lease and server
  1673. // status.
  1674. // Although we're submitting it as a periodic timer, it's actually
  1675. // not going to be called at regular intervals.
  1676. // There is a race condition where the alert event/IOCP may have
  1677. // been fired already, and another thread tried to cancel this timer
  1678. // which hasn't been submitted yet. The logic to handle this race
  1679. // is placed there (HandleNATHelpUpdate); here we can assume we
  1680. // are the first person to submit the refresh timer.
  1681. //
  1682. DPFX(DPFPREP, 7, "Submitting NAT Help refresh timer (for every %u ms) for thread pool 0x%p.",
  1683. dwNATHelpRetryTime, this);
  1684. DNASSERT(! m_fNATHelpTimerJobSubmitted );
  1685. m_fNATHelpTimerJobSubmitted = TRUE;
  1686. #ifdef DPNBUILD_ONLYONEPROCESSOR
  1687. hr = SubmitTimerJob(FALSE, // don't perform immediately
  1688. 1, // retry count
  1689. TRUE, // retry forever
  1690. dwNATHelpRetryTime, // retry timeout
  1691. TRUE, // wait forever
  1692. 0, // idle timeout
  1693. CThreadPool::NATHelpTimerFunction, // periodic callback function
  1694. CThreadPool::NATHelpTimerComplete, // completion function
  1695. this); // context
  1696. #else // ! DPNBUILD_ONLYONEPROCESSOR
  1697. hr = SubmitTimerJob(-1, // pick any CPU
  1698. FALSE, // don't perform immediately
  1699. 1, // retry count
  1700. TRUE, // retry forever
  1701. dwNATHelpRetryTime, // retry timeout
  1702. TRUE, // wait forever
  1703. 0, // idle timeout
  1704. CThreadPool::NATHelpTimerFunction, // periodic callback function
  1705. CThreadPool::NATHelpTimerComplete, // completion function
  1706. this); // context
  1707. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  1708. if (hr != DPN_OK)
  1709. {
  1710. m_fNATHelpTimerJobSubmitted = FALSE;
  1711. DPFX(DPFPREP, 0, "Failed to submit timer job to watch over NAT Help (err = 0x%lx)!", hr );
  1712. //
  1713. // NAT Help will probably not work correctly, but that won't
  1714. // prevent local connections from working. Consider it
  1715. // non-fatal.
  1716. //
  1717. }
  1718. }
  1719. #ifdef DBG
  1720. DPFX(DPFPREP, 8, "Spent %u ms preparing NAT Help.",
  1721. (GETTIMESTAMP() - dwStartTime));
  1722. #endif // DBG
  1723. }
  1724. else
  1725. {
  1726. DPFX(DPFPREP, 0, "Failed to load NAT Help, continuing." );
  1727. }
  1728. }
  1729. else
  1730. {
  1731. DPFX(DPFPREP, 4, "NAT Help already loaded." );
  1732. }
  1733. Unlock();
  1734. }
  1735. #ifndef DPNBUILD_NOREGISTRY
  1736. else
  1737. {
  1738. DPFX(DPFPREP, 0, "Not loading NAT Help." );
  1739. }
  1740. #endif // ! DPNBUILD_NOREGISTRY
  1741. DPFX(DPFPREP, 7, "Leave");
  1742. }
  1743. //**********************************************************************
  1744. // ------------------------------
  1745. // CThreadPool::PerformSubsequentNATHelpGetCaps - blocking function to get NAT Help caps again
  1746. //
  1747. // Entry: Pointer to job information
  1748. //
  1749. // Exit: Nothing
  1750. // ------------------------------
  1751. #undef DPF_MODNAME
  1752. #define DPF_MODNAME "CEndpoint::EnumQueryJobCallback"
  1753. void CThreadPool::PerformSubsequentNATHelpGetCaps( void * const pvContext )
  1754. {
  1755. CThreadPool * pThisThreadPool;
  1756. DNASSERT( pvContext != NULL );
  1757. pThisThreadPool = (CThreadPool*) pvContext;
  1758. pThisThreadPool->HandleNATHelpUpdate(NULL);
  1759. }
  1760. //**********************************************************************
  1761. //**********************************************************************
  1762. // ------------------------------
  1763. // CThreadPool::NATHelpTimerComplete - NAT Help timer job has completed
  1764. //
  1765. // Entry: Timer result code
  1766. // Context
  1767. //
  1768. // Exit: Nothing
  1769. // ------------------------------
  1770. #undef DPF_MODNAME
  1771. #define DPF_MODNAME "CThreadPool::NATHelpTimerComplete"
  1772. void CThreadPool::NATHelpTimerComplete( const HRESULT hResult, void * const pContext )
  1773. {
  1774. CThreadPool * pThisThreadPool;
  1775. DNASSERT( pContext != NULL );
  1776. pThisThreadPool = (CThreadPool*) pContext;
  1777. DPFX(DPFPREP, 5, "Threadpool 0x%p NAT Help Timer complete.", pThisThreadPool);
  1778. pThisThreadPool->m_fNATHelpTimerJobSubmitted = FALSE;
  1779. }
  1780. //**********************************************************************
  1781. //**********************************************************************
  1782. // ------------------------------
  1783. // CThreadPool::NATHelpTimerFunction - NAT Help timer job needs service
  1784. //
  1785. // Entry: Pointer to context
  1786. //
  1787. // Exit: Nothing
  1788. // ------------------------------
  1789. #undef DPF_MODNAME
  1790. #define DPF_MODNAME "CThreadPool::NATHelpTimerFunction"
  1791. void CThreadPool::NATHelpTimerFunction( void * const pContext )
  1792. {
  1793. CThreadPool * pThisThreadPool;
  1794. DNASSERT( pContext != NULL );
  1795. pThisThreadPool = (CThreadPool*) pContext;
  1796. //
  1797. // Attempt to submit a blocking job to update the NAT capabilites. If it
  1798. // fails, we'll just try again later. It might also fail because a previous
  1799. // blocking job took so long that there's still a job scheduled in the queue
  1800. // already (we disallow duplicates).
  1801. //
  1802. pThisThreadPool->SubmitBlockingJob(CThreadPool::PerformSubsequentNATHelpGetCaps, pThisThreadPool);
  1803. }
  1804. //**********************************************************************
  1805. #endif // DPNBUILD_NONATHELP
  1806. #ifndef DPNBUILD_ONLYWINSOCK2
  1807. //**********************************************************************
  1808. // ------------------------------
  1809. // CThreadPool::AddSocketPort - add a socket to the Win9x watch list
  1810. //
  1811. // Entry: Pointer to SocketPort
  1812. //
  1813. // Exit: Error code
  1814. // ------------------------------
  1815. #undef DPF_MODNAME
  1816. #define DPF_MODNAME "CThreadPool::AddSocketPort"
  1817. HRESULT CThreadPool::AddSocketPort( CSocketPort *const pSocketPort )
  1818. {
  1819. HRESULT hr;
  1820. BOOL fSocketAdded;
  1821. DPFX(DPFPREP, 6, "(0x%p) Parameters: (0x%p)", this, pSocketPort);
  1822. DNASSERT( pSocketPort != NULL );
  1823. //
  1824. // initialize
  1825. //
  1826. hr = DPN_OK;
  1827. fSocketAdded = FALSE;
  1828. Lock();
  1829. //
  1830. // We're capped by the number of sockets we can use for Winsock1. Make
  1831. // sure we don't allocate too many sockets.
  1832. //
  1833. if ( m_uReservedSocketCount == FD_SETSIZE )
  1834. {
  1835. hr = DPNERR_OUTOFMEMORY;
  1836. DPFX(DPFPREP, 0, "There are too many sockets allocated on Winsock1!" );
  1837. goto Failure;
  1838. }
  1839. m_uReservedSocketCount++;
  1840. DNASSERT( m_SocketSet.fd_count < FD_SETSIZE );
  1841. m_pSocketPorts[ m_SocketSet.fd_count ] = pSocketPort;
  1842. m_SocketSet.fd_array[ m_SocketSet.fd_count ] = pSocketPort->GetSocket();
  1843. m_SocketSet.fd_count++;
  1844. fSocketAdded = TRUE;
  1845. //
  1846. // add a reference to note that this socket port is being used by the thread
  1847. // pool
  1848. //
  1849. pSocketPort->AddRef();
  1850. if (m_pvTimerDataWinsock1IO == NULL)
  1851. {
  1852. hr = IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pDPThreadPoolWork,
  1853. 0, // use CPU 0, we shouldn't have multiple CPUs anyway
  1854. g_dwSelectTimePeriod, // delay
  1855. CThreadPool::CheckWinsock1IOCallback, // callback
  1856. this, // user context
  1857. &m_pvTimerDataWinsock1IO, // timer data (returned)
  1858. &m_uiTimerUniqueWinsock1IO, // timer unique (returned)
  1859. 0); // flags
  1860. if (hr != DPN_OK)
  1861. {
  1862. DPFX(DPFPREP, 0, "Couldn't schedule Winsock 1 I/O poll timer!");
  1863. goto Failure;
  1864. }
  1865. }
  1866. else
  1867. {
  1868. //
  1869. // It's possible there's still an outstanding lazy cancellation
  1870. // attempt. If so, don't try to cancel I/O anymore.
  1871. //
  1872. if (m_fCancelWinsock1IO)
  1873. {
  1874. DPFX(DPFPREP, 1, "Retracting lazy cancellation attempt.");
  1875. m_fCancelWinsock1IO = FALSE;
  1876. }
  1877. }
  1878. Exit:
  1879. Unlock();
  1880. DPFX(DPFPREP, 6, "(0x%p) Return: [0x%08x]", this, hr);
  1881. return hr;
  1882. Failure:
  1883. if ( fSocketAdded != FALSE )
  1884. {
  1885. AssertCriticalSectionIsTakenByThisThread( &m_Lock, TRUE );
  1886. m_SocketSet.fd_count--;
  1887. m_pSocketPorts[ m_SocketSet.fd_count ] = NULL;
  1888. m_SocketSet.fd_array[ m_SocketSet.fd_count ] = NULL;
  1889. fSocketAdded = FALSE;
  1890. }
  1891. m_uReservedSocketCount--;
  1892. goto Exit;
  1893. }
  1894. //**********************************************************************
  1895. //**********************************************************************
  1896. // ------------------------------
  1897. // CThreadPool::RemoveSocketPort - remove a socket from the Win9x watch list
  1898. //
  1899. // Entry: Pointer to socket port to remove
  1900. //
  1901. // Exit: Nothing
  1902. // ------------------------------
  1903. #undef DPF_MODNAME
  1904. #define DPF_MODNAME "CThreadPool::RemoveSocketPort"
  1905. void CThreadPool::RemoveSocketPort( CSocketPort *const pSocketPort )
  1906. {
  1907. UINT_PTR uIndex;
  1908. DPFX(DPFPREP, 6, "(0x%p) Parameters: (0x%p)", this, pSocketPort);
  1909. DNASSERT( pSocketPort != NULL );
  1910. Lock();
  1911. uIndex = m_SocketSet.fd_count;
  1912. DNASSERT( uIndex != 0 );
  1913. //
  1914. // If this is the last socket, cancel the I/O timer.
  1915. //
  1916. if ( uIndex == 1 )
  1917. {
  1918. HRESULT hr;
  1919. //
  1920. // Keep trying to cancel the Winsock1 timer. It may fail because it's in the
  1921. // process of actively executing, but we will set the cancel flag so that the
  1922. // timer will eventually notice.
  1923. //
  1924. DPFX(DPFPREP, 5, "Cancelling Winsock 1 I/O timer.");
  1925. DNASSERT(m_pvTimerDataWinsock1IO != NULL);
  1926. hr = IDirectPlay8ThreadPoolWork_CancelTimer(m_pDPThreadPoolWork,
  1927. m_pvTimerDataWinsock1IO,
  1928. m_uiTimerUniqueWinsock1IO,
  1929. 0);
  1930. if (hr != DPN_OK)
  1931. {
  1932. DPFX(DPFPREP, 2, "Couldn't stop Winsock1 I/O timer, marking for lazy cancellation.");
  1933. m_fCancelWinsock1IO = TRUE;
  1934. }
  1935. else
  1936. {
  1937. m_pvTimerDataWinsock1IO = NULL;
  1938. }
  1939. }
  1940. while ( uIndex != 0 )
  1941. {
  1942. uIndex--;
  1943. if ( m_pSocketPorts[ uIndex ] == pSocketPort )
  1944. {
  1945. m_uReservedSocketCount--;
  1946. m_SocketSet.fd_count--;
  1947. memmove( &m_pSocketPorts[ uIndex ],
  1948. &m_pSocketPorts[ uIndex + 1 ],
  1949. ( sizeof( m_pSocketPorts[ uIndex ] ) * ( m_SocketSet.fd_count - uIndex ) ) );
  1950. memmove( &m_SocketSet.fd_array[ uIndex ],
  1951. &m_SocketSet.fd_array[ uIndex + 1 ],
  1952. ( sizeof( m_SocketSet.fd_array[ uIndex ] ) * ( m_SocketSet.fd_count - uIndex ) ) );
  1953. //
  1954. // clear last entry which is now unused
  1955. //
  1956. memset( &m_pSocketPorts[ m_SocketSet.fd_count ], 0x00, sizeof( m_pSocketPorts[ m_SocketSet.fd_count ] ) );
  1957. memset( &m_SocketSet.fd_array[ m_SocketSet.fd_count ], 0x00, sizeof( m_SocketSet.fd_array[ m_SocketSet.fd_count ] ) );
  1958. //
  1959. // end the loop
  1960. //
  1961. uIndex = 0;
  1962. }
  1963. }
  1964. Unlock();
  1965. pSocketPort->DecRef();
  1966. DPFX(DPFPREP, 6, "(0x%p) Leave", this);
  1967. }
  1968. //**********************************************************************
  1969. #endif // ! DPNBUILD_ONLYWINSOCK2
  1970. #ifdef WIN95
  1971. #ifndef DPNBUILD_NOWINSOCK2
  1972. #endif // ! DPNBUILD_NOWINSOCK2
  1973. #endif // WIN95
  1974. #ifndef DPNBUILD_NONATHELP
  1975. //**********************************************************************
  1976. // ------------------------------
  1977. // CThreadPool::HandleNATHelpUpdate - handle a NAT Help update event
  1978. //
  1979. // Entry: Timer interval if update is occurring periodically, or
  1980. // NULL if a triggered event.
  1981. // This function may take a while, because updating NAT Help
  1982. // can block.
  1983. //
  1984. // Exit: Nothing
  1985. // ------------------------------
  1986. #undef DPF_MODNAME
  1987. #define DPF_MODNAME "CThreadPool::HandleNATHelpUpdate"
  1988. void CThreadPool::HandleNATHelpUpdate( DWORD * const pdwTimerInterval )
  1989. {
  1990. HRESULT hr;
  1991. DWORD dwTemp;
  1992. DPNHCAPS dpnhcaps;
  1993. DWORD dwNATHelpRetryTime;
  1994. BOOL fModifiedRetryInterval;
  1995. DWORD dwFirstUpdateTime;
  1996. DWORD dwCurrentTime;
  1997. DWORD dwNumGetCaps = 0;
  1998. DNASSERT(IsNATHelpLoaded());
  1999. Lock();
  2000. //
  2001. // Prevent multiple threads from trying to update NAT Help status at the same
  2002. // time. If we're a duplicate, just bail.
  2003. //
  2004. if (m_dwNATHelpUpdateThreadID != 0)
  2005. {
  2006. DPFX(DPFPREP, 1, "Thread %u/0x%x already handling NAT Help update, not processing again (thread pool = 0x%p, timer = 0x%p).",
  2007. m_dwNATHelpUpdateThreadID, m_dwNATHelpUpdateThreadID, this, pdwTimerInterval);
  2008. Unlock();
  2009. return;
  2010. }
  2011. m_dwNATHelpUpdateThreadID = GetCurrentThreadId();
  2012. if (! m_fNATHelpTimerJobSubmitted)
  2013. {
  2014. DPFX(DPFPREP, 1, "Handling NAT Help update without a NAT refresh timer job submitted (thread pool = 0x%p).",
  2015. this);
  2016. DNASSERT(pdwTimerInterval == NULL);
  2017. }
  2018. Unlock();
  2019. DPFX(DPFPREP, 6, "Beginning thread pool 0x%p NAT Help update.", this);
  2020. //
  2021. // Initialize the timer values.
  2022. //
  2023. dwNATHelpRetryTime = -1;
  2024. dwFirstUpdateTime = GETTIMESTAMP() - 1; // longest possible time
  2025. for(dwTemp = 0; dwTemp < MAX_NUM_DIRECTPLAYNATHELPERS; dwTemp++)
  2026. {
  2027. if (g_papNATHelpObjects[dwTemp] != NULL)
  2028. {
  2029. ZeroMemory(&dpnhcaps, sizeof(dpnhcaps));
  2030. dpnhcaps.dwSize = sizeof(dpnhcaps);
  2031. hr = IDirectPlayNATHelp_GetCaps(g_papNATHelpObjects[dwTemp],
  2032. &dpnhcaps,
  2033. DPNHGETCAPS_UPDATESERVERSTATUS);
  2034. switch (hr)
  2035. {
  2036. case DPNH_OK:
  2037. {
  2038. //
  2039. // See if this is the shortest interval.
  2040. //
  2041. if (dpnhcaps.dwRecommendedGetCapsInterval < dwNATHelpRetryTime)
  2042. {
  2043. dwNATHelpRetryTime = dpnhcaps.dwRecommendedGetCapsInterval;
  2044. }
  2045. break;
  2046. }
  2047. case DPNHSUCCESS_ADDRESSESCHANGED:
  2048. {
  2049. DPFX(DPFPREP, 1, "NAT Help index %u indicated public addresses changed.",
  2050. dwTemp);
  2051. //
  2052. // We don't actually store any public address information,
  2053. // we query it each time. Therefore we don't need to
  2054. // actually do anything with the change notification.
  2055. //
  2056. //
  2057. // See if this is the shortest interval.
  2058. //
  2059. if (dpnhcaps.dwRecommendedGetCapsInterval < dwNATHelpRetryTime)
  2060. {
  2061. dwNATHelpRetryTime = dpnhcaps.dwRecommendedGetCapsInterval;
  2062. }
  2063. break;
  2064. }
  2065. case DPNHERR_OUTOFMEMORY:
  2066. {
  2067. //
  2068. // This should generally only happen in stress. We'll
  2069. // continue on to other NAT help objects, and hope we
  2070. // aren't totally hosed.
  2071. //
  2072. DPFX(DPFPREP, 0, "NAT Help index %u returned out-of-memory error! Continuing.",
  2073. dwTemp);
  2074. break;
  2075. }
  2076. default:
  2077. {
  2078. //
  2079. // Some other unknown error occurred. Ignore it.
  2080. //
  2081. DPFX(DPFPREP, 0, "NAT Help index %u returned unknown error 0x%lx! Continuing.",
  2082. dwTemp, hr);
  2083. break;
  2084. }
  2085. }
  2086. //
  2087. // Save the current time, if this is the first GetCaps.
  2088. //
  2089. if (dwNumGetCaps == 0)
  2090. {
  2091. dwFirstUpdateTime = GETTIMESTAMP();
  2092. }
  2093. dwNumGetCaps++;
  2094. }
  2095. else
  2096. {
  2097. //
  2098. // No DPNATHelp object in that slot.
  2099. //
  2100. }
  2101. }
  2102. //
  2103. // Assert that at least one NAT Help object is loaded.
  2104. //
  2105. DNASSERT(dwNumGetCaps > 0);
  2106. dwCurrentTime = GETTIMESTAMP();
  2107. //
  2108. // We may need to make some adjustments to the timer. Either subtract out time
  2109. // we've spent fiddling around with other NAT Help interfaces, or make sure the
  2110. // time isn't really large because that can screw up time calculations. It's
  2111. // not a big deal to wake up after 24 days even though we wouldn't need to
  2112. // according to the logic above.
  2113. //
  2114. if (dwNATHelpRetryTime & 0x80000000)
  2115. {
  2116. DPFX(DPFPREP, 3, "NAT Help refresh timer for thread pool 0x%p is set to longest possible without going negative.",
  2117. this);
  2118. dwNATHelpRetryTime = 0x7FFFFFFF;
  2119. }
  2120. else
  2121. {
  2122. DWORD dwTimeElapsed;
  2123. //
  2124. // Find out how much time has elapsed since the first GetCaps completed.
  2125. //
  2126. dwTimeElapsed = dwCurrentTime - dwFirstUpdateTime;
  2127. //
  2128. // Remove it from the retry interval, unless it's already overdue.
  2129. //
  2130. if (dwTimeElapsed < dwNATHelpRetryTime)
  2131. {
  2132. dwNATHelpRetryTime -= dwTimeElapsed;
  2133. }
  2134. else
  2135. {
  2136. dwNATHelpRetryTime = 0; // shortest time possible
  2137. }
  2138. }
  2139. //
  2140. // Modify the next time when we should refresh the NAT Help information based
  2141. // on the reported recommendation.
  2142. //
  2143. if (pdwTimerInterval != NULL)
  2144. {
  2145. DPFX(DPFPREP, 6, "Modifying NAT Help refresh timer for thread pool 0x%p in place (was %u ms, changing to %u).",
  2146. this, (*pdwTimerInterval), dwNATHelpRetryTime);
  2147. (*pdwTimerInterval) = dwNATHelpRetryTime;
  2148. }
  2149. else
  2150. {
  2151. //
  2152. // Add the interval to the current time to find the new retry time.
  2153. //
  2154. dwCurrentTime += dwNATHelpRetryTime;
  2155. DPFX(DPFPREP, 6, "Modifying NAT Help refresh timer for thread pool 0x%p to run at offset %u (in %u ms).",
  2156. this, dwCurrentTime, dwNATHelpRetryTime);
  2157. //
  2158. // Try to modify the existing timer job. There are two race conditions:
  2159. // 1) the recurring timer that triggered this refresh (which occurs on a
  2160. // separate non-blockable thread) has not rescheduled itself yet. It
  2161. // should pick up the new timer setting we're about to apply when it
  2162. // eventually does reschedule. ModifyTimerJobNextRetryTime should
  2163. // succeed.
  2164. // 2) the NAT help timer is being killed. ModifyTimerJobNextRetryTime
  2165. // will fail, but there's nothing we can or should do.
  2166. //
  2167. fModifiedRetryInterval = ModifyTimerJobNextRetryTime(this, dwCurrentTime);
  2168. if (! fModifiedRetryInterval)
  2169. {
  2170. DPFX(DPFPREP, 1, "Unable to modify NAT Help refresh timer (thread pool 0x%p), timer should be cancelled.",
  2171. this);
  2172. }
  2173. }
  2174. //
  2175. // Now that we're done handling the update, let other threads do what they
  2176. // want.
  2177. //
  2178. Lock();
  2179. DNASSERT(m_dwNATHelpUpdateThreadID == GetCurrentThreadId());
  2180. m_dwNATHelpUpdateThreadID = 0;
  2181. Unlock();
  2182. }
  2183. //**********************************************************************
  2184. #endif // DPNBUILD_NONATHELP
  2185. #ifndef DPNBUILD_NOSPUI
  2186. //**********************************************************************
  2187. // ------------------------------
  2188. // CThreadPool::DialogThreadProc - thread proc for spawning dialogs
  2189. //
  2190. // Entry: Pointer to startup parameter
  2191. //
  2192. // Exit: Error Code
  2193. // ------------------------------
  2194. #undef DPF_MODNAME
  2195. #define DPF_MODNAME "CThreadPool::DialogThreadProc"
  2196. DWORD WINAPI CThreadPool::DialogThreadProc( void *pParam )
  2197. {
  2198. const DIALOG_THREAD_PARAM *pThreadParam;
  2199. BOOL fComInitialized;
  2200. //
  2201. // Initialize COM. If this fails, we'll have problems later.
  2202. //
  2203. fComInitialized = FALSE;
  2204. switch ( COM_CoInitialize( NULL ) )
  2205. {
  2206. case S_OK:
  2207. {
  2208. fComInitialized = TRUE;
  2209. break;
  2210. }
  2211. case S_FALSE:
  2212. {
  2213. DNASSERT( FALSE );
  2214. fComInitialized = TRUE;
  2215. break;
  2216. }
  2217. //
  2218. // COM init failed!
  2219. //
  2220. default:
  2221. {
  2222. DPFX(DPFPREP, 0, "Failed to initialize COM!" );
  2223. DNASSERT( FALSE );
  2224. break;
  2225. }
  2226. }
  2227. DNASSERT( pParam != NULL );
  2228. pThreadParam = static_cast<DIALOG_THREAD_PARAM*>( pParam );
  2229. pThreadParam->pDialogFunction( pThreadParam->pContext );
  2230. DNFree( pParam );
  2231. if ( fComInitialized != FALSE )
  2232. {
  2233. COM_CoUninitialize();
  2234. fComInitialized = FALSE;
  2235. }
  2236. return 0;
  2237. }
  2238. //**********************************************************************
  2239. #endif // ! DPNBUILD_NOSPUI
  2240. #ifndef DPNBUILD_ONLYWINSOCK2
  2241. //**********************************************************************
  2242. // ------------------------------
  2243. // CThreadPool::CheckWinsock1IO - check the IO status for Winsock1 sockets
  2244. //
  2245. // Entry: Pointer to sockets to watch
  2246. //
  2247. // Exit: Boolean indicating whether I/O was serviced
  2248. // TRUE = I/O serviced
  2249. // FALSE = I/O not serviced
  2250. // ------------------------------
  2251. #undef DPF_MODNAME
  2252. #define DPF_MODNAME "CThreadPool::CheckWinsock1IO"
  2253. BOOL CThreadPool::CheckWinsock1IO( FD_SET *const pWinsock1Sockets )
  2254. {
  2255. #ifdef DPNBUILD_NONEWTHREADPOOL
  2256. static const TIMEVAL SelectTime = { 0, 0 }; // zero, do an instant check
  2257. #else // ! DPNBUILD_NONEWTHREADPOOL
  2258. TIMEVAL SelectTime = { 0, (g_dwSelectTimeSlice * 1000)}; // convert ms into microseconds
  2259. #endif // ! DPNBUILD_NONEWTHREADPOOL
  2260. BOOL fIOServiced;
  2261. INT iSelectReturn;
  2262. FD_SET ReadSocketSet;
  2263. FD_SET ErrorSocketSet;
  2264. //
  2265. // Make a local copy of all of the sockets. This isn't totally
  2266. // efficient, but it works. Multiplying by active socket count will
  2267. // spend half the time in the integer multiply.
  2268. //
  2269. fIOServiced = FALSE;
  2270. Lock();
  2271. if (m_fCancelWinsock1IO)
  2272. {
  2273. DPFX(DPFPREP, 1, "Detected Winsock 1 I/O cancellation, aborting.");
  2274. Unlock();
  2275. return FALSE;
  2276. }
  2277. memcpy( &ReadSocketSet, pWinsock1Sockets, sizeof( ReadSocketSet ) );
  2278. memcpy( &ErrorSocketSet, pWinsock1Sockets, sizeof( ErrorSocketSet ) );
  2279. Unlock();
  2280. //
  2281. // Don't check write sockets here because it's very likely that they're ready
  2282. // for service but have no outgoing data and will thrash
  2283. //
  2284. iSelectReturn = select( 0, // compatibility parameter (ignored)
  2285. &ReadSocketSet, // sockets to check for read
  2286. NULL, // sockets to check for write (none)
  2287. &ErrorSocketSet, // sockets to check for error
  2288. &SelectTime // wait timeout
  2289. );
  2290. switch ( iSelectReturn )
  2291. {
  2292. //
  2293. // timeout
  2294. //
  2295. case 0:
  2296. {
  2297. break;
  2298. }
  2299. //
  2300. // select got pissed
  2301. //
  2302. case SOCKET_ERROR:
  2303. {
  2304. DWORD dwWSAError;
  2305. dwWSAError = WSAGetLastError();
  2306. switch ( dwWSAError )
  2307. {
  2308. //
  2309. // WSAENOTSOCK = This socket was probably closed
  2310. //
  2311. case WSAENOTSOCK:
  2312. {
  2313. DPFX(DPFPREP, 1, "Winsock1 reporting 'Not a socket' when selecting read or error sockets!" );
  2314. break;
  2315. }
  2316. //
  2317. // WSAEINTR = this operation was interrupted
  2318. //
  2319. case WSAEINTR:
  2320. {
  2321. DPFX(DPFPREP, 1, "Winsock1 reporting interrupted operation when selecting read or error sockets!" );
  2322. break;
  2323. }
  2324. //
  2325. // other
  2326. //
  2327. default:
  2328. {
  2329. DPFX(DPFPREP, 0, "Problem selecting read or error sockets for service!" );
  2330. DisplayWinsockError( 0, dwWSAError );
  2331. DNASSERT( FALSE );
  2332. break;
  2333. }
  2334. }
  2335. break;
  2336. }
  2337. //
  2338. // Check for sockets needing read service and error service.
  2339. //
  2340. default:
  2341. {
  2342. fIOServiced |= ServiceWinsock1Sockets( &ReadSocketSet, CSocketPort::Winsock1ReadService );
  2343. fIOServiced |= ServiceWinsock1Sockets( &ErrorSocketSet, CSocketPort::Winsock1ErrorService );
  2344. break;
  2345. }
  2346. }
  2347. return fIOServiced;
  2348. }
  2349. //**********************************************************************
  2350. //**********************************************************************
  2351. // ------------------------------
  2352. // CThreadPool::ServiceWinsock1Sockets - service requests on Winsock1 sockets ports
  2353. //
  2354. // Entry: Pointer to set of sockets
  2355. // Pointer to service function
  2356. //
  2357. // Exit: Boolean indicating whether I/O was serviced
  2358. // TRUE = I/O serviced
  2359. // FALSE = I/O not serviced
  2360. // ------------------------------
  2361. #undef DPF_MODNAME
  2362. #define DPF_MODNAME "CThreadPool::ServiceWinsock1Sockets"
  2363. BOOL CThreadPool::ServiceWinsock1Sockets( FD_SET *pSocketSet, PSOCKET_SERVICE_FUNCTION pServiceFunction )
  2364. {
  2365. BOOL fReturn;
  2366. UINT_PTR uWaitingSocketCount;
  2367. UINT_PTR uSocketPortCount;
  2368. CSocketPort *pSocketPorts[ FD_SETSIZE ];
  2369. //
  2370. // initialize
  2371. //
  2372. fReturn = FALSE;
  2373. uSocketPortCount = 0;
  2374. uWaitingSocketCount = pSocketSet->fd_count;
  2375. Lock();
  2376. while ( uWaitingSocketCount > 0 )
  2377. {
  2378. UINT_PTR uIdx;
  2379. uWaitingSocketCount--;
  2380. uIdx = m_SocketSet.fd_count;
  2381. while ( uIdx != 0 )
  2382. {
  2383. uIdx--;
  2384. if ( __WSAFDIsSet( m_SocketSet.fd_array[ uIdx ], pSocketSet ) != FALSE )
  2385. {
  2386. //
  2387. // this socket is still available, add a reference to the socket
  2388. // port and keep it around to be processed outside of the lock
  2389. //
  2390. pSocketPorts[ uSocketPortCount ] = m_pSocketPorts[ uIdx ];
  2391. pSocketPorts[ uSocketPortCount ]->AddRef();
  2392. uSocketPortCount++;
  2393. uIdx = 0;
  2394. }
  2395. }
  2396. }
  2397. Unlock();
  2398. while ( uSocketPortCount != 0 )
  2399. {
  2400. uSocketPortCount--;
  2401. //
  2402. // call the service function and remove the reference
  2403. //
  2404. fReturn |= (pSocketPorts[ uSocketPortCount ]->*pServiceFunction)();
  2405. pSocketPorts[ uSocketPortCount ]->DecRef();
  2406. }
  2407. return fReturn;
  2408. }
  2409. //**********************************************************************
  2410. #endif // ! DPNBUILD_ONLYWINSOCK2
  2411. //**********************************************************************
  2412. // ------------------------------
  2413. // CThreadPool::SubmitDelayedCommand - submit request to perform work in another thread
  2414. //
  2415. // Entry: CPU index (non DPNBUILD_ONLYONEPROCESSOR builds only)
  2416. // Pointer to callback function
  2417. // Pointer to callback context
  2418. //
  2419. // Exit: Error code
  2420. // ------------------------------
  2421. #undef DPF_MODNAME
  2422. #define DPF_MODNAME "CThreadPool::SubmitDelayedCommand"
  2423. #ifdef DPNBUILD_ONLYONEPROCESSOR
  2424. HRESULT CThreadPool::SubmitDelayedCommand( const PFNDPTNWORKCALLBACK pFunction,
  2425. void *const pContext )
  2426. {
  2427. return IDirectPlay8ThreadPoolWork_QueueWorkItem(m_pDPThreadPoolWork,
  2428. -1,
  2429. pFunction,
  2430. pContext,
  2431. 0);
  2432. }
  2433. #else // ! DPNBUILD_ONLYONEPROCESSOR
  2434. HRESULT CThreadPool::SubmitDelayedCommand( const DWORD dwCPU,
  2435. const PFNDPTNWORKCALLBACK pFunction,
  2436. void *const pContext )
  2437. {
  2438. return IDirectPlay8ThreadPoolWork_QueueWorkItem(m_pDPThreadPoolWork,
  2439. dwCPU,
  2440. pFunction,
  2441. pContext,
  2442. 0);
  2443. }
  2444. #endif // ! DPNBUILD_ONLYONEPROCESSOR
  2445. //**********************************************************************
  2446. //**********************************************************************
  2447. // ------------------------------
  2448. // CThreadPool::GenericTimerCallback - generic timer callback
  2449. //
  2450. // Entry: Pointer to callback context
  2451. // Pointer to timer data
  2452. // Pointer to timer uniqueness value
  2453. //
  2454. // Exit: None
  2455. // ------------------------------
  2456. #undef DPF_MODNAME
  2457. #define DPF_MODNAME "CThreadPool::GenericTimerCallback"
  2458. void WINAPI CThreadPool::GenericTimerCallback( void * const pvContext,
  2459. void * const pvTimerData,
  2460. const UINT uiTimerUnique )
  2461. {
  2462. TIMER_OPERATION_ENTRY * pTimerEntry;
  2463. CThreadPool * pThisThreadPool;
  2464. DWORD dwCurrentTime;
  2465. HRESULT hr;
  2466. DWORD dwNewTimerDelay;
  2467. #ifdef DBG
  2468. DWORD dwNextRetryTime;
  2469. #endif // DBG
  2470. pTimerEntry = (TIMER_OPERATION_ENTRY*) pvContext;
  2471. DNASSERT((pvTimerData == pTimerEntry->pvTimerData) || (pTimerEntry->pvTimerData == NULL));
  2472. DNASSERT((uiTimerUnique == pTimerEntry->uiTimerUnique) || (pTimerEntry->uiTimerUnique == 0));
  2473. pThisThreadPool = pTimerEntry->pThreadPool;
  2474. //
  2475. // Process the timer, unless we just went through the idle timeout.
  2476. //
  2477. if (pTimerEntry->uRetryCount != 0)
  2478. {
  2479. dwCurrentTime = GETTIMESTAMP();
  2480. #ifdef DBG
  2481. dwNextRetryTime = pTimerEntry->dwNextRetryTime; // copy since lock is not held
  2482. if ((int) (dwNextRetryTime - dwCurrentTime) <= 0)
  2483. {
  2484. //
  2485. // Timer expired and has not been rescheduled yet.
  2486. //
  2487. DPFX(DPFPREP, 7, "Threadpool 0x%p performing timed job 0x%p approximately %u ms after intended time of %u.",
  2488. pThisThreadPool, pTimerEntry, (dwCurrentTime - dwNextRetryTime), pTimerEntry->dwNextRetryTime);
  2489. }
  2490. else
  2491. {
  2492. //
  2493. // Another thread may have modified the timer already.
  2494. //
  2495. DPFX(DPFPREP, 7, "Threadpool 0x%p performing timed job 0x%p (next time already modified to be %u).",
  2496. pThisThreadPool, pTimerEntry, dwNextRetryTime);
  2497. }
  2498. #endif // DBG
  2499. //
  2500. // Execute this timed item.
  2501. //
  2502. pTimerEntry->pTimerCallback(pTimerEntry->pContext);
  2503. //
  2504. // Reschedule the job, unless it was *just* cancelled, or it's idling
  2505. // forever.
  2506. //
  2507. pThisThreadPool->LockTimerData();
  2508. if (pTimerEntry->fCancelling)
  2509. {
  2510. DPFX(DPFPREP, 5, "Timer 0x%p was just cancelled, completing.", pTimerEntry);
  2511. pTimerEntry->Linkage.RemoveFromList();
  2512. pThisThreadPool->UnlockTimerData();
  2513. pTimerEntry->pTimerComplete(DPNERR_USERCANCEL, pTimerEntry->pContext);
  2514. g_TimerEntryPool.Release(pTimerEntry);
  2515. }
  2516. else
  2517. {
  2518. //
  2519. // If this job isn't running forever, decrement the retry count.
  2520. // If there are no more retries, set up the idle timer.
  2521. //
  2522. if ( pTimerEntry->fRetryForever == FALSE )
  2523. {
  2524. pTimerEntry->uRetryCount--;
  2525. if ( pTimerEntry->uRetryCount == 0 )
  2526. {
  2527. if ( pTimerEntry->fIdleWaitForever == FALSE )
  2528. {
  2529. //
  2530. // Compute stopping time for this job's 'Timeout' phase.
  2531. //
  2532. dwNewTimerDelay = pTimerEntry->dwIdleTimeout;
  2533. pTimerEntry->dwIdleTimeout += dwCurrentTime;
  2534. }
  2535. else
  2536. {
  2537. //
  2538. // We're waiting forever for enum returns. ASSERT that we
  2539. // have the maximum timeout.
  2540. //
  2541. DNASSERT( pTimerEntry->dwIdleTimeout == -1 );
  2542. //
  2543. // Set this value to avoid a false PREfast warning.
  2544. //
  2545. dwNewTimerDelay = 0;
  2546. }
  2547. goto SkipNextRetryTimeComputation;
  2548. }
  2549. } // end if (don't retry forever)
  2550. dwNewTimerDelay = pTimerEntry->dwRetryInterval;
  2551. pTimerEntry->dwNextRetryTime = dwCurrentTime + pTimerEntry->dwRetryInterval;
  2552. SkipNextRetryTimeComputation:
  2553. if ((! pTimerEntry->fIdleWaitForever) ||
  2554. (pTimerEntry->uRetryCount > 0))
  2555. {
  2556. //
  2557. // Make sure we aren't trying to schedule something too far in
  2558. // the future or backward in time. If we are, we'll just force
  2559. // the timer to expire earlier.
  2560. //
  2561. if ((int) dwNewTimerDelay < 0)
  2562. {
  2563. DNASSERT(! "Job time is unexpectedly long or backward in time!");
  2564. dwNewTimerDelay = 0x7FFFFFFF;
  2565. }
  2566. hr = IDirectPlay8ThreadPoolWork_ResetCompletingTimer(pThisThreadPool->m_pDPThreadPoolWork,
  2567. pvTimerData, // timer data
  2568. dwNewTimerDelay, // delay
  2569. CThreadPool::GenericTimerCallback, // callback
  2570. pTimerEntry, // user context
  2571. &pTimerEntry->uiTimerUnique, // new timer uniqueness value
  2572. 0); // flags
  2573. DNASSERT(hr == DPN_OK);
  2574. pTimerEntry->pvTimerData = pvTimerData; // ensure that we remember the timer handle
  2575. pThisThreadPool->UnlockTimerData();
  2576. }
  2577. else
  2578. {
  2579. DPFX(DPFPREP, 5, "Timer 0x%p now idling forever.", pTimerEntry);
  2580. pTimerEntry->pvTimerData = NULL;
  2581. pThisThreadPool->UnlockTimerData();
  2582. }
  2583. }
  2584. }
  2585. else
  2586. {
  2587. DNASSERT((int) (pTimerEntry->dwIdleTimeout - GETTIMESTAMP()) <= 0);
  2588. //
  2589. // Remove this link from the list, tell owner that the job is
  2590. // complete and return the job to the pool.
  2591. //
  2592. pThisThreadPool->LockTimerData();
  2593. pTimerEntry->Linkage.RemoveFromList();
  2594. pThisThreadPool->UnlockTimerData();
  2595. pTimerEntry->pTimerComplete(DPN_OK, pTimerEntry->pContext);
  2596. g_TimerEntryPool.Release(pTimerEntry);
  2597. }
  2598. }
  2599. //**********************************************************************
  2600. #ifndef DPNBUILD_ONLYWINSOCK2
  2601. //**********************************************************************
  2602. // ------------------------------
  2603. // CThreadPool::CheckWinsock1IOCallback - Winsock1 I/O servicing callback
  2604. //
  2605. // Pointer to timer data
  2606. // Pointer to timer uniqueness value
  2607. // Entry: Pointer to callback context
  2608. //
  2609. // Exit: None
  2610. // ------------------------------
  2611. #undef DPF_MODNAME
  2612. #define DPF_MODNAME "CThreadPool::CheckWinsock1IOCallback"
  2613. void WINAPI CThreadPool::CheckWinsock1IOCallback( void * const pvContext,
  2614. void * const pvTimerData,
  2615. const UINT uiTimerUnique )
  2616. {
  2617. CThreadPool * pThisThreadPool = (CThreadPool*) pvContext;
  2618. BOOL fResult;
  2619. HRESULT hr;
  2620. //
  2621. // Service all Winsock1 I/O possible.
  2622. //
  2623. do
  2624. {
  2625. fResult = pThisThreadPool->CheckWinsock1IO(&pThisThreadPool->m_SocketSet);
  2626. }
  2627. while (fResult);
  2628. //
  2629. // Schedule the timer again, unless we're cancelling it.
  2630. //
  2631. pThisThreadPool->Lock();
  2632. DNASSERT(pvTimerData == pThisThreadPool->m_pvTimerDataWinsock1IO);
  2633. DNASSERT(uiTimerUnique == pThisThreadPool->m_uiTimerUniqueWinsock1IO);
  2634. if (! pThisThreadPool->m_fCancelWinsock1IO)
  2635. {
  2636. hr = IDirectPlay8ThreadPoolWork_ResetCompletingTimer(pThisThreadPool->m_pDPThreadPoolWork,
  2637. pvTimerData, // timer data
  2638. g_dwSelectTimePeriod, // delay
  2639. CThreadPool::CheckWinsock1IOCallback, // callback
  2640. pThisThreadPool, // user context
  2641. &pThisThreadPool->m_uiTimerUniqueWinsock1IO, // updated timer uniqueness value
  2642. 0); // flags
  2643. DNASSERT(hr == DPN_OK);
  2644. }
  2645. else
  2646. {
  2647. DPFX(DPFPREP, 1, "Not resubmitting Winsock1 I/O timer due to cancellation.");
  2648. pThisThreadPool->m_fCancelWinsock1IO = FALSE;
  2649. pThisThreadPool->m_pvTimerDataWinsock1IO = NULL;
  2650. }
  2651. pThisThreadPool->Unlock();
  2652. }
  2653. //**********************************************************************
  2654. #endif // ! DPNBUILD_ONLYWINSOCK2
  2655. #ifndef DPNBUILD_ONLYONETHREAD
  2656. //**********************************************************************
  2657. // ------------------------------
  2658. // DPNBlockingJobThreadProc - thread procedure for executing blocking jobs
  2659. //
  2660. // Entry: Parameter
  2661. //
  2662. // Exit: Result code
  2663. // ------------------------------
  2664. #undef DPF_MODNAME
  2665. #define DPF_MODNAME "DPNBlockingJobThreadProc"
  2666. DWORD WINAPI DPNBlockingJobThreadProc(PVOID pvParameter)
  2667. {
  2668. HRESULT hr;
  2669. CThreadPool * pThisThreadPool;
  2670. BOOL fUninitializeCOM = TRUE;
  2671. DPFX(DPFPREP, 5, "Parameters: (0x%p)", pvParameter);
  2672. pThisThreadPool = (CThreadPool*) pvParameter;
  2673. //
  2674. // Init COM.
  2675. //
  2676. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  2677. if (FAILED(hr))
  2678. {
  2679. DPFX(DPFPREP, 0, "Failed to initialize COM (err = 0x%lx)! Continuing.", hr);
  2680. fUninitializeCOM = FALSE;
  2681. //
  2682. // Continue...
  2683. //
  2684. }
  2685. //
  2686. // Process all jobs until we're told to quit.
  2687. //
  2688. pThisThreadPool->DoBlockingJobs();
  2689. if (fUninitializeCOM)
  2690. {
  2691. CoUninitialize();
  2692. fUninitializeCOM = FALSE;
  2693. }
  2694. DPFX(DPFPREP, 5, "Leave");
  2695. return 0;
  2696. }
  2697. //**********************************************************************
  2698. #endif // ! DPNBUILD_ONLYONETHREAD
  2699. //**********************************************************************
  2700. // ------------------------------
  2701. // TimerEntry_Alloc - allocate a new timer job entry
  2702. //
  2703. // Entry: Pointer to new entry
  2704. //
  2705. // Exit: Boolean indicating success
  2706. // TRUE = initialization successful
  2707. // FALSE = initialization failed
  2708. // ------------------------------
  2709. #undef DPF_MODNAME
  2710. #define DPF_MODNAME "TimerEntry_Alloc"
  2711. BOOL TimerEntry_Alloc( void *pvItem, void* pvContext )
  2712. {
  2713. TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
  2714. DNASSERT( pvItem != NULL );
  2715. DEBUG_ONLY( memset( pTimerEntry, 0x00, sizeof( *pTimerEntry ) ) );
  2716. pTimerEntry->Linkage.Initialize();
  2717. return TRUE;
  2718. }
  2719. //**********************************************************************
  2720. //**********************************************************************
  2721. // ------------------------------
  2722. // TimerEntry_Get - get new timer job entry from pool
  2723. //
  2724. // Entry: Pointer to new entry
  2725. //
  2726. // Exit: Nothing
  2727. // ------------------------------
  2728. #undef DPF_MODNAME
  2729. #define DPF_MODNAME "TimerEntry_Get"
  2730. void TimerEntry_Get( void *pvItem, void* pvContext )
  2731. {
  2732. #ifdef DBG
  2733. const TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
  2734. DNASSERT( pvItem != NULL );
  2735. DNASSERT( pTimerEntry->pContext == NULL );
  2736. DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
  2737. #endif // DBG
  2738. }
  2739. //**********************************************************************
  2740. //**********************************************************************
  2741. // ------------------------------
  2742. // TimerEntry_Release - return timer job entry to pool
  2743. //
  2744. // Entry: Pointer to entry
  2745. //
  2746. // Exit: Nothing
  2747. // ------------------------------
  2748. #undef DPF_MODNAME
  2749. #define DPF_MODNAME "TimerEntry_Release"
  2750. void TimerEntry_Release( void *pvItem )
  2751. {
  2752. TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
  2753. DNASSERT( pvItem != NULL );
  2754. DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
  2755. pTimerEntry->pContext= NULL;
  2756. }
  2757. //**********************************************************************
  2758. //**********************************************************************
  2759. // ------------------------------
  2760. // TimerEntry_Dealloc - deallocate a timer job entry
  2761. //
  2762. // Entry: Pointer to entry
  2763. //
  2764. // Exit: Nothing
  2765. // ------------------------------
  2766. #undef DPF_MODNAME
  2767. #define DPF_MODNAME "TimerEntry_Dealloc"
  2768. void TimerEntry_Dealloc( void *pvItem )
  2769. {
  2770. #ifdef DBG
  2771. const TIMER_OPERATION_ENTRY* pTimerEntry = (TIMER_OPERATION_ENTRY*)pvItem;
  2772. DNASSERT( pvItem != NULL );
  2773. DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
  2774. DNASSERT( pTimerEntry->pContext == NULL );
  2775. #endif // DBG
  2776. }
  2777. //**********************************************************************