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.

4186 lines
106 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1998-2000 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/98 jtk Created
  13. ***************************************************************************/
  14. #include "dnmdmi.h"
  15. #undef DPF_SUBCOMP
  16. #define DPF_SUBCOMP DN_SUBCOMP_MODEM
  17. //**********************************************************************
  18. // Constant definitions
  19. //**********************************************************************
  20. //
  21. // events for threads
  22. //
  23. enum
  24. {
  25. EVENT_INDEX_STOP_ALL_THREADS = 0,
  26. EVENT_INDEX_PENDING_JOB = 1,
  27. EVENT_INDEX_WAKE_NT_TIMER_THREAD = 1,
  28. EVENT_INDEX_SEND_COMPLETE = 2,
  29. EVENT_INDEX_RECEIVE_COMPLETE = 3,
  30. EVENT_INDEX_TAPI_MESSAGE = 4,
  31. EVENT_INDEX_MAX
  32. };
  33. //
  34. // times to wait in milliseconds when polling for work thread shutdown
  35. //
  36. #define WORK_THREAD_CLOSE_WAIT_TIME 3000
  37. #define WORK_THREAD_CLOSE_SLEEP_TIME 100
  38. //**********************************************************************
  39. // Macro definitions
  40. //**********************************************************************
  41. //**********************************************************************
  42. // Structure definitions
  43. //**********************************************************************
  44. //
  45. // structure for common data in Win9x thread
  46. //
  47. typedef struct _WIN9X_CORE_DATA
  48. {
  49. DWORD dwNextTimerJobTime; // time when the next timer job needs service
  50. DNHANDLE hWaitHandles[ EVENT_INDEX_MAX ]; // handles for waiting on
  51. DWORD dwWaitHandleCount; // count of handles to wait on
  52. DWORD dwTimeToNextJob; // time to next job
  53. BOOL fTimerJobsActive; // Boolean indicating that there are active jobs
  54. BOOL fLooping; // Boolean indicating that this thread is still active
  55. } WIN9X_CORE_DATA;
  56. //
  57. // information passed to the Win9x workhorse thread
  58. //
  59. typedef struct _WIN9X_THREAD_DATA
  60. {
  61. CModemThreadPool *pThisThreadPool; // pointer to this object
  62. } WIN9X_THREAD_DATA;
  63. //
  64. // information passed to the IOCompletion thread
  65. //
  66. typedef struct _IOCOMPLETION_THREAD_DATA
  67. {
  68. CModemThreadPool *pThisThreadPool; // pointer to this object
  69. } IOCOMPLETION_THREAD_DATA;
  70. //
  71. // structure passed to dialog threads
  72. //
  73. typedef struct _DIALOG_THREAD_PARAM
  74. {
  75. DIALOG_FUNCTION *pDialogFunction;
  76. void *pContext;
  77. CModemThreadPool *pThisThreadPool;
  78. } DIALOG_THREAD_PARAM;
  79. //**********************************************************************
  80. // Variable definitions
  81. //**********************************************************************
  82. //**********************************************************************
  83. // Function prototypes
  84. //**********************************************************************
  85. //**********************************************************************
  86. // Function definitions
  87. //**********************************************************************
  88. //**********************************************************************
  89. // ------------------------------
  90. // CModemThreadPool::PoolAllocFunction
  91. //
  92. // Entry: Nothing
  93. //
  94. // Exit: Nothing
  95. // ------------------------------
  96. #undef DPF_MODNAME
  97. #define DPF_MODNAME "CModemThreadPool::PoolAllocFunction"
  98. BOOL CModemThreadPool::PoolAllocFunction( void* pvItem, void* pvContext )
  99. {
  100. CModemThreadPool* pThreadPool = (CModemThreadPool*)pvItem;
  101. pThreadPool->m_iTotalThreadCount = 0;
  102. #ifdef WINNT
  103. pThreadPool->m_iNTCompletionThreadCount = 0;
  104. pThreadPool->m_fNTTimerThreadRunning = FALSE;
  105. pThreadPool->m_hIOCompletionPort = NULL;
  106. #endif // WINNT
  107. pThreadPool->m_fAllowThreadCountReduction = FALSE;
  108. pThreadPool->m_iIntendedThreadCount = 0;
  109. pThreadPool->m_hStopAllThreads = NULL;
  110. #ifdef WIN95
  111. pThreadPool->m_hSendComplete = NULL;
  112. pThreadPool->m_hReceiveComplete = NULL;
  113. pThreadPool->m_hTAPIEvent = NULL;
  114. pThreadPool->m_hFakeTAPIEvent = NULL;
  115. #endif // WIN95
  116. pThreadPool->m_fTAPIAvailable = FALSE;
  117. pThreadPool->m_iRefCount = 0;
  118. pThreadPool->m_Sig[0] = 'T';
  119. pThreadPool->m_Sig[1] = 'H';
  120. pThreadPool->m_Sig[2] = 'P';
  121. pThreadPool->m_Sig[3] = 'L';
  122. pThreadPool->m_OutstandingReadList.Initialize();
  123. pThreadPool->m_OutstandingWriteList.Initialize();
  124. memset( &pThreadPool->m_InitFlags, 0x00, sizeof( pThreadPool->m_InitFlags ) );
  125. memset( &pThreadPool->m_TAPIInfo, 0x00, sizeof( pThreadPool->m_TAPIInfo ) );
  126. pThreadPool->m_TimerJobList.Initialize();
  127. return TRUE;
  128. }
  129. //**********************************************************************
  130. //**********************************************************************
  131. // ------------------------------
  132. // CModemThreadPool::PoolInitFunction
  133. //
  134. // Entry: Nothing
  135. //
  136. // Exit: Nothing
  137. // ------------------------------
  138. #undef DPF_MODNAME
  139. #define DPF_MODNAME "CModemThreadPool::PoolInitFunction"
  140. void CModemThreadPool::PoolInitFunction( void* pvItem, void* pvContext )
  141. {
  142. CModemThreadPool* pThreadPool = (CModemThreadPool*)pvItem;
  143. DNASSERT(pThreadPool->m_iRefCount == 0);
  144. pThreadPool->m_iRefCount = 1;
  145. }
  146. //**********************************************************************
  147. //**********************************************************************
  148. // ------------------------------
  149. // CModemThreadPool::PoolDeallocFunction
  150. //
  151. // Entry: Nothing
  152. //
  153. // Exit: Nothing
  154. // ------------------------------
  155. #undef DPF_MODNAME
  156. #define DPF_MODNAME "CModemThreadPool::PoolDeallocFunction"
  157. void CModemThreadPool::PoolDeallocFunction( void* pvItem )
  158. {
  159. const CModemThreadPool* pThreadPool = (CModemThreadPool*)pvItem;
  160. DNASSERT( pThreadPool->m_iTotalThreadCount == 0 );
  161. #ifdef WINNT
  162. DNASSERT( pThreadPool->m_iNTCompletionThreadCount == 0 );
  163. DNASSERT( pThreadPool->m_fNTTimerThreadRunning == FALSE );
  164. DNASSERT( pThreadPool->m_hIOCompletionPort == NULL );
  165. #endif // WINNT
  166. DNASSERT( pThreadPool->m_fAllowThreadCountReduction == FALSE );
  167. DNASSERT( pThreadPool->m_iIntendedThreadCount == 0 );
  168. DNASSERT( pThreadPool->m_hStopAllThreads == NULL );
  169. #ifdef WIN95
  170. DNASSERT( pThreadPool->m_hSendComplete == NULL );
  171. DNASSERT( pThreadPool->m_hReceiveComplete == NULL );
  172. DNASSERT( pThreadPool->m_hTAPIEvent == NULL );
  173. DNASSERT( pThreadPool->m_hFakeTAPIEvent == NULL );
  174. #endif // WIN95
  175. DNASSERT( pThreadPool->m_fTAPIAvailable == FALSE );
  176. DNASSERT( pThreadPool->m_OutstandingReadList.IsEmpty() != FALSE );
  177. DNASSERT( pThreadPool->m_OutstandingReadList.IsEmpty() != FALSE );
  178. DNASSERT( pThreadPool->m_TimerJobList.IsEmpty() != FALSE );
  179. DNASSERT( pThreadPool->m_InitFlags.fTAPILoaded == FALSE );
  180. DNASSERT( pThreadPool->m_InitFlags.fLockInitialized == FALSE );
  181. DNASSERT( pThreadPool->m_InitFlags.fIODataLockInitialized == FALSE );
  182. DNASSERT( pThreadPool->m_InitFlags.fJobDataLockInitialized == FALSE );
  183. DNASSERT( pThreadPool->m_InitFlags.fTimerDataLockInitialized == FALSE );
  184. DNASSERT( pThreadPool->m_InitFlags.fJobQueueInitialized == FALSE );
  185. DNASSERT( pThreadPool->m_iRefCount == 0 );
  186. }
  187. //**********************************************************************
  188. //**********************************************************************
  189. // ------------------------------
  190. // CModemThreadPool::Initialize - initialize work threads
  191. //
  192. // Entry: Nothing
  193. //
  194. // Exit: Boolean indicating success
  195. // TRUE = success
  196. // FALSE = failure
  197. // ------------------------------
  198. #undef DPF_MODNAME
  199. #define DPF_MODNAME "CModemThreadPool::Initialize"
  200. BOOL CModemThreadPool::Initialize( void )
  201. {
  202. HRESULT hTempResult;
  203. BOOL fReturn;
  204. //
  205. // initialize
  206. //
  207. fReturn = TRUE;
  208. DNASSERT( m_InitFlags.fTAPILoaded == FALSE );
  209. DNASSERT( m_InitFlags.fLockInitialized == FALSE );
  210. DNASSERT( m_InitFlags.fIODataLockInitialized == FALSE );
  211. DNASSERT( m_InitFlags.fJobDataLockInitialized == FALSE );
  212. DNASSERT( m_InitFlags.fTimerDataLockInitialized == FALSE );
  213. DNASSERT( m_InitFlags.fDataPortHandleTableInitialized == FALSE );
  214. DNASSERT( m_InitFlags.fJobQueueInitialized == FALSE );
  215. //
  216. // try to load TAPI before anything else
  217. //
  218. hTempResult = LoadTAPILibrary();
  219. if ( hTempResult == DPN_OK )
  220. {
  221. m_InitFlags.fTAPILoaded = TRUE;
  222. }
  223. else
  224. {
  225. DPFX(DPFPREP, 0, "Failed to load TAPI!" );
  226. DisplayDNError( 0, hTempResult );
  227. }
  228. //
  229. // initialize critical sections
  230. //
  231. if ( DNInitializeCriticalSection( &m_Lock ) == FALSE )
  232. {
  233. goto Failure;
  234. }
  235. DebugSetCriticalSectionRecursionCount( &m_Lock, 0 );
  236. DebugSetCriticalSectionGroup( &m_Lock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
  237. m_InitFlags.fLockInitialized = TRUE;
  238. //
  239. // Win9x has poor APC support and as part of the workaround, the read and
  240. // write data locks need to be taken twice. Adjust the recursion counts
  241. // accordingly.
  242. //
  243. if ( DNInitializeCriticalSection( &m_IODataLock ) == FALSE )
  244. {
  245. goto Failure;
  246. }
  247. DebugSetCriticalSectionRecursionCount( &m_IODataLock, 1 );
  248. DebugSetCriticalSectionGroup( &m_IODataLock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
  249. m_InitFlags.fIODataLockInitialized = TRUE;
  250. if ( DNInitializeCriticalSection( &m_JobDataLock ) == FALSE )
  251. {
  252. goto Failure;
  253. }
  254. DebugSetCriticalSectionRecursionCount( &m_JobDataLock, 0 );
  255. DebugSetCriticalSectionGroup( &m_JobDataLock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
  256. m_InitFlags.fJobDataLockInitialized = TRUE;
  257. if ( DNInitializeCriticalSection( &m_TimerDataLock ) == FALSE )
  258. {
  259. goto Failure;
  260. }
  261. DebugSetCriticalSectionRecursionCount( &m_TimerDataLock, 0 );
  262. DebugSetCriticalSectionGroup( &m_TimerDataLock, &g_blDPNModemCritSecsHeld ); // separate dpnmodem CSes from the rest of DPlay's CSes
  263. m_InitFlags.fTimerDataLockInitialized = TRUE;
  264. //
  265. // handle table
  266. //
  267. if ( m_DataPortHandleTable.Initialize() != DPN_OK )
  268. {
  269. goto Failure;
  270. }
  271. m_InitFlags.fDataPortHandleTableInitialized = TRUE;
  272. //
  273. // initialize job queue
  274. //
  275. if ( m_JobQueue.Initialize() == FALSE )
  276. {
  277. goto Failure;
  278. }
  279. m_InitFlags.fJobQueueInitialized = TRUE;
  280. //
  281. // Create event to stop all threads. Win9x needs this to stop processing
  282. // and the NT enum thread uses this to stop processing
  283. //
  284. DNASSERT( m_hStopAllThreads == NULL );
  285. m_hStopAllThreads = DNCreateEvent( NULL, // pointer to security (none)
  286. TRUE, // manual reset
  287. FALSE, // start unsignalled
  288. NULL ); // pointer to name (none)
  289. if ( m_hStopAllThreads == NULL )
  290. {
  291. DWORD dwError;
  292. dwError = GetLastError();
  293. DPFX(DPFPREP, 0, "Failed to create event to stop all threads!" );
  294. DisplayErrorCode( 0, dwError );
  295. goto Failure;
  296. }
  297. DNASSERT( m_fAllowThreadCountReduction == FALSE );
  298. m_fAllowThreadCountReduction = TRUE;
  299. //
  300. // OS-specific initialization
  301. //
  302. #ifdef WINNT
  303. //
  304. // WinNT
  305. //
  306. if (FAILED(WinNTInit()))
  307. {
  308. goto Failure;
  309. }
  310. #else // WIN95
  311. //
  312. // Windows 9x
  313. //
  314. if (FAILED(Win9xInit()))
  315. {
  316. goto Failure;
  317. }
  318. #endif // WINNT
  319. //
  320. // Verify all internal flags. It's possible that TAPI didn't load
  321. // so don't check it (it's not a fatal condition).
  322. //
  323. DNASSERT( m_InitFlags.fLockInitialized != FALSE );
  324. DNASSERT( m_InitFlags.fIODataLockInitialized != FALSE );
  325. DNASSERT( m_InitFlags.fJobDataLockInitialized != FALSE );
  326. DNASSERT( m_InitFlags.fTimerDataLockInitialized != FALSE );
  327. DNASSERT( m_InitFlags.fJobQueueInitialized != FALSE );
  328. Exit:
  329. return fReturn;
  330. Failure:
  331. fReturn = FALSE;
  332. StopAllThreads();
  333. Deinitialize();
  334. goto Exit;
  335. }
  336. //**********************************************************************
  337. //**********************************************************************
  338. // ------------------------------
  339. // CModemThreadPool::Deinitialize - destroy work threads
  340. //
  341. // Entry: Nothing
  342. //
  343. // Exit: Nothing
  344. // ------------------------------
  345. #undef DPF_MODNAME
  346. #define DPF_MODNAME "CModemThreadPool::Deinitialize"
  347. void CModemThreadPool::Deinitialize( void )
  348. {
  349. // DNASSERT( m_JobQueue.IsEmpty() != FALSE );
  350. //
  351. // request that all threads stop and then cycle our timeslice to
  352. // allow the threads a chance for cleanup
  353. //
  354. m_fAllowThreadCountReduction = FALSE;
  355. DPFX(DPFPREP, 9, "SetIntendedThreadCount 0");
  356. SetIntendedThreadCount( 0 );
  357. StopAllThreads();
  358. SleepEx( 0, TRUE );
  359. #ifdef WINNT
  360. if ( m_hIOCompletionPort != NULL )
  361. {
  362. if ( CloseHandle( m_hIOCompletionPort ) == FALSE )
  363. {
  364. DWORD dwError;
  365. dwError = GetLastError();
  366. DPFX(DPFPREP, 0, "Problem closing handle to I/O completion port!" );
  367. DisplayErrorCode( 0, dwError );
  368. }
  369. m_hIOCompletionPort = NULL;
  370. }
  371. #endif // WINNT
  372. //
  373. // close StopAllThreads handle
  374. //
  375. if ( m_hStopAllThreads != NULL )
  376. {
  377. if ( DNCloseHandle( m_hStopAllThreads ) == FALSE )
  378. {
  379. DWORD dwError;
  380. dwError = GetLastError();
  381. DPFX(DPFPREP, 0, "Failed to close StopAllThreads handle!" );
  382. DisplayErrorCode( 0, dwError );
  383. }
  384. m_hStopAllThreads = NULL;
  385. }
  386. #ifdef WIN95
  387. //
  388. // close handles for I/O events
  389. //
  390. if ( m_hSendComplete != NULL )
  391. {
  392. if ( DNCloseHandle( m_hSendComplete ) == FALSE )
  393. {
  394. DWORD dwError;
  395. dwError = GetLastError();
  396. DPFX(DPFPREP, 0, "Problem closing SendComplete handle!" );
  397. DisplayErrorCode( 0, dwError );
  398. }
  399. m_hSendComplete = NULL;
  400. }
  401. if ( m_hReceiveComplete != NULL )
  402. {
  403. if ( DNCloseHandle( m_hReceiveComplete ) == FALSE )
  404. {
  405. DWORD dwError;
  406. dwError = GetLastError();
  407. DPFX(DPFPREP, 0, "Problem closing ReceiveComplete handle!" );
  408. DisplayErrorCode( 0, dwError );
  409. }
  410. m_hReceiveComplete = NULL;
  411. }
  412. #endif // WIN95
  413. //
  414. // Now that all of the threads are stopped, clean up any outstanding I/O.
  415. // this can be done without taking any locks
  416. //
  417. if (m_InitFlags.fJobQueueInitialized)
  418. {
  419. CancelOutstandingIO();
  420. }
  421. //
  422. // double-check empty IO lists
  423. //
  424. DNASSERT( m_OutstandingWriteList.IsEmpty() != FALSE );
  425. DNASSERT( m_OutstandingReadList.IsEmpty() != FALSE );
  426. //
  427. // deinitialize handle table
  428. //
  429. if ( m_InitFlags.fDataPortHandleTableInitialized != FALSE )
  430. {
  431. m_DataPortHandleTable.Deinitialize();
  432. m_InitFlags.fDataPortHandleTableInitialized = FALSE;
  433. }
  434. //
  435. // deinitialize job queue
  436. //
  437. if ( m_InitFlags.fJobQueueInitialized != FALSE )
  438. {
  439. m_JobQueue.Deinitialize();
  440. m_InitFlags.fJobQueueInitialized = FALSE;
  441. }
  442. if ( m_InitFlags.fTimerDataLockInitialized != FALSE )
  443. {
  444. DNDeleteCriticalSection( &m_TimerDataLock );
  445. m_InitFlags.fTimerDataLockInitialized = FALSE;
  446. }
  447. if ( m_InitFlags.fJobDataLockInitialized != FALSE )
  448. {
  449. DNDeleteCriticalSection( &m_JobDataLock );
  450. m_InitFlags.fJobDataLockInitialized = FALSE;
  451. }
  452. if ( m_InitFlags.fIODataLockInitialized != FALSE )
  453. {
  454. DNDeleteCriticalSection( &m_IODataLock );
  455. m_InitFlags.fIODataLockInitialized = FALSE;
  456. }
  457. if ( m_InitFlags.fLockInitialized != FALSE )
  458. {
  459. DNDeleteCriticalSection( &m_Lock );
  460. m_InitFlags.fLockInitialized = FALSE;
  461. }
  462. //
  463. // unload TAPI
  464. //
  465. if ( m_TAPIInfo.hApplicationInstance != NULL )
  466. {
  467. DNASSERT( p_lineShutdown != NULL );
  468. p_lineShutdown( m_TAPIInfo.hApplicationInstance );
  469. m_TAPIInfo.hApplicationInstance = NULL;
  470. }
  471. m_fTAPIAvailable = FALSE;
  472. memset( &m_TAPIInfo, 0x00, sizeof( m_TAPIInfo ) );
  473. #ifdef WIN95
  474. if (m_hTAPIEvent != NULL && m_hFakeTAPIEvent == NULL)
  475. {
  476. // In the case that we got the event from lineInitializeEx, lineShutdown will have closed the event handle,
  477. // so we can tell the handle tracking code that the handle is already cleaned up.
  478. REMOVE_DNHANDLE(m_hTAPIEvent);
  479. }
  480. m_hTAPIEvent = NULL;
  481. if ( m_hFakeTAPIEvent != NULL )
  482. {
  483. if ( DNCloseHandle( m_hFakeTAPIEvent ) == FALSE )
  484. {
  485. DWORD dwError;
  486. dwError = GetLastError();
  487. DPFX(DPFPREP, 0, "Problem closing fake TAPI event!" );
  488. DisplayErrorCode( 0, GetLastError() );
  489. }
  490. m_hFakeTAPIEvent = NULL;
  491. }
  492. #endif // WIN95
  493. //
  494. // close TAPI
  495. //
  496. if ( m_InitFlags.fTAPILoaded != FALSE )
  497. {
  498. UnloadTAPILibrary();
  499. m_InitFlags.fTAPILoaded = FALSE;
  500. }
  501. DNASSERT( m_InitFlags.fTAPILoaded == FALSE );
  502. DNASSERT( m_InitFlags.fLockInitialized == FALSE );
  503. DNASSERT( m_InitFlags.fIODataLockInitialized == FALSE );
  504. DNASSERT( m_InitFlags.fJobDataLockInitialized == FALSE );
  505. DNASSERT( m_InitFlags.fTimerDataLockInitialized == FALSE );
  506. DNASSERT( m_InitFlags.fJobQueueInitialized == FALSE );
  507. }
  508. //**********************************************************************
  509. //**********************************************************************
  510. // ------------------------------
  511. // CModemThreadPool::CreateReadIOData - create read IO data
  512. //
  513. // Entry: Nothing
  514. //
  515. // Exit: Pointer to Read IO Data
  516. // ------------------------------
  517. #undef DPF_MODNAME
  518. #define DPF_MODNAME "CModemThreadPool::CreateReadIOData"
  519. CModemReadIOData *CModemThreadPool::CreateReadIOData( void )
  520. {
  521. CModemReadIOData *pReadData;
  522. LockReadData();
  523. #ifdef WIN95
  524. pReadData = (CModemReadIOData*)g_ModemReadIODataPool.Get( GetReceiveCompleteEvent() );
  525. #else
  526. pReadData = (CModemReadIOData*)g_ModemReadIODataPool.Get( NULL );
  527. #endif // WIN95
  528. if ( pReadData != NULL )
  529. {
  530. pReadData->SetThreadPool( this );
  531. pReadData->m_OutstandingReadListLinkage.InsertBefore( &m_OutstandingReadList );
  532. }
  533. UnlockReadData();
  534. return pReadData;
  535. }
  536. //**********************************************************************
  537. //**********************************************************************
  538. // ------------------------------
  539. // CModemThreadPool::ReturnReadIOData - return read data to pool
  540. //
  541. // Entry: Pointer to read data
  542. //
  543. // Exit: Nothing
  544. // ------------------------------
  545. #undef DPF_MODNAME
  546. #define DPF_MODNAME "CModemThreadPool::ReturnReadIOData"
  547. void CModemThreadPool::ReturnReadIOData( CModemReadIOData *const pReadIOData )
  548. {
  549. DNASSERT( pReadIOData != NULL );
  550. LockReadData();
  551. pReadIOData->m_OutstandingReadListLinkage.RemoveFromList();
  552. DNASSERT( pReadIOData->m_OutstandingReadListLinkage.IsEmpty() != FALSE );
  553. g_ModemReadIODataPool.Release( pReadIOData );
  554. UnlockReadData();
  555. }
  556. //**********************************************************************
  557. //**********************************************************************
  558. // ------------------------------
  559. // CModemThreadPool::CreateWriteIOData - create Write IO data
  560. //
  561. // Entry: Nothing
  562. //
  563. // Exit: Pointer to Write IO Data
  564. // ------------------------------
  565. #undef DPF_MODNAME
  566. #define DPF_MODNAME "CModemThreadPool::CreateWriteIOData"
  567. CModemWriteIOData *CModemThreadPool::CreateWriteIOData( void )
  568. {
  569. CModemWriteIOData *pWriteData;
  570. LockWriteData();
  571. #ifdef WIN95
  572. pWriteData = (CModemWriteIOData*)g_ModemWriteIODataPool.Get( GetSendCompleteEvent() );
  573. #else // WINNT
  574. pWriteData = (CModemWriteIOData*)g_ModemWriteIODataPool.Get( NULL );
  575. #endif // WIN95
  576. if ( pWriteData != NULL )
  577. {
  578. pWriteData->m_OutstandingWriteListLinkage.InsertBefore( &m_OutstandingWriteList );
  579. }
  580. UnlockWriteData();
  581. return pWriteData;
  582. }
  583. //**********************************************************************
  584. //**********************************************************************
  585. // ------------------------------
  586. // CModemThreadPool::ReturnWriteIOData - return write data to pool
  587. //
  588. // Entry: Pointer to Write data
  589. //
  590. // Exit: Nothing
  591. // ------------------------------
  592. #undef DPF_MODNAME
  593. #define DPF_MODNAME "CModemThreadPool::ReturnWriteIOData"
  594. void CModemThreadPool::ReturnWriteIOData( CModemWriteIOData *const pWriteIOData )
  595. {
  596. DNASSERT( pWriteIOData != NULL );
  597. LockWriteData();
  598. pWriteIOData->m_OutstandingWriteListLinkage.RemoveFromList();
  599. g_ModemWriteIODataPool.Release( pWriteIOData );
  600. UnlockWriteData();
  601. }
  602. //**********************************************************************
  603. #ifdef WINNT
  604. //**********************************************************************
  605. // ------------------------------
  606. // CModemThreadPool::WinNTInit - initialize WinNT components
  607. //
  608. // Entry: Nothing
  609. //
  610. // Exit: Error code
  611. // ------------------------------
  612. #undef DPF_MODNAME
  613. #define DPF_MODNAME "CModemThreadPool::WinNTInit"
  614. HRESULT CModemThreadPool::WinNTInit( void )
  615. {
  616. HRESULT hr;
  617. LINEINITIALIZEEXPARAMS LineInitializeExParams;
  618. LONG lReturn;
  619. //
  620. // initialize
  621. //
  622. hr = DPN_OK;
  623. DNASSERT( m_hIOCompletionPort == NULL );
  624. m_hIOCompletionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, // don't associate a file handle yet
  625. NULL, // handle of existing completion port (none)
  626. NULL, // completion key for callback (none)
  627. 0 // number of concurent threads (0 = use number of processors)
  628. );
  629. if ( m_hIOCompletionPort == NULL )
  630. {
  631. hr = DPNERR_OUTOFMEMORY;
  632. DPFX(DPFPREP, 0, "Could not create NT IOCompletionPort!" );
  633. DisplayErrorCode( 0, GetLastError() );
  634. goto Failure;
  635. }
  636. //
  637. // Initialize TAPI. If TAPI doesn't start, it's not a problem, but note
  638. // the failure.
  639. //
  640. DNASSERT( m_fTAPIAvailable == FALSE );
  641. memset( &m_TAPIInfo, 0x00, sizeof( m_TAPIInfo ) );
  642. memset( &LineInitializeExParams, 0x00, sizeof( LineInitializeExParams ) );
  643. m_TAPIInfo.dwVersion = TAPI_CURRENT_VERSION;
  644. LineInitializeExParams.dwTotalSize = sizeof( LineInitializeExParams );
  645. LineInitializeExParams.dwOptions = LINEINITIALIZEEXOPTION_USECOMPLETIONPORT;
  646. LineInitializeExParams.dwCompletionKey = IO_COMPLETION_KEY_TAPI_MESSAGE;
  647. DNASSERT( GetIOCompletionPort() != NULL );
  648. LineInitializeExParams.Handles.hCompletionPort = GetIOCompletionPort();
  649. lReturn = LINEERR_UNINITIALIZED;
  650. if ( p_lineInitializeEx != NULL )
  651. {
  652. lReturn = p_lineInitializeEx( &m_TAPIInfo.hApplicationInstance, // pointer to application TAPI instance handle
  653. DNGetApplicationInstance(), // instance handle of .DLL
  654. NULL, // callback function (not used)
  655. NULL, // friendly application name (none)
  656. &m_TAPIInfo.dwLinesAvailable, // pointer to number of devices available to TAPI
  657. &m_TAPIInfo.dwVersion, // pointer to input/output TAPI version
  658. &LineInitializeExParams ); // pointer to extra params
  659. }
  660. if ( lReturn == LINEERR_NONE )
  661. {
  662. m_fTAPIAvailable = TRUE;
  663. }
  664. else
  665. {
  666. DPFX(DPFPREP, 0, "Failed to initialize TAPI for NT!" );
  667. }
  668. //
  669. // Prepare to spin up IOCompletionPort threads
  670. //
  671. DNASSERT( ThreadCount() == 0 );
  672. DNASSERT( NTCompletionThreadCount() == 0 );
  673. DPFX(DPFPREP, 9, "SetIntendedThreadCount %i", g_iThreadCount);
  674. SetIntendedThreadCount( g_iThreadCount );
  675. Exit:
  676. return hr;
  677. Failure:
  678. DPFX(DPFPREP, 0, "Failed WinNT initialization!" );
  679. DisplayDNError( 0, hr );
  680. goto Exit;
  681. }
  682. //**********************************************************************
  683. #endif // WINNT
  684. #ifdef WIN95
  685. //**********************************************************************
  686. // ------------------------------
  687. // CModemThreadPool::Win9xInit - initialize Win9x components
  688. //
  689. // Entry: Nothing
  690. //
  691. // Exit: Error code
  692. // ------------------------------
  693. #undef DPF_MODNAME
  694. #define DPF_MODNAME "CModemThreadPool::Win9xInit"
  695. HRESULT CModemThreadPool::Win9xInit( void )
  696. {
  697. HRESULT hr;
  698. DNHANDLE hPrimaryThread;
  699. DWORD dwPrimaryThreadID;
  700. WIN9X_THREAD_DATA *pPrimaryThreadInput;
  701. LINEINITIALIZEEXPARAMS LineInitializeExParams;
  702. LONG lReturn;
  703. //
  704. // initialize
  705. //
  706. hr = DPN_OK;
  707. hPrimaryThread = NULL;
  708. pPrimaryThreadInput = NULL;
  709. //
  710. // Initialize TAPI. If this succeeds, it will give us an event to use for
  711. // TAPI messages. If not, create a fake event that will take the place of
  712. // the TAPI event for the Win9x threads.
  713. //
  714. DNASSERT( m_fTAPIAvailable == FALSE );
  715. memset( &m_TAPIInfo, 0x00, sizeof( m_TAPIInfo ) );
  716. memset( &LineInitializeExParams, 0x00, sizeof( LineInitializeExParams ) );
  717. m_TAPIInfo.dwVersion = TAPI_CURRENT_VERSION;
  718. LineInitializeExParams.dwTotalSize = sizeof( LineInitializeExParams );
  719. LineInitializeExParams.dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
  720. lReturn = LINEERR_UNINITIALIZED;
  721. if ( p_lineInitializeEx != NULL )
  722. {
  723. lReturn = p_lineInitializeEx( &m_TAPIInfo.hApplicationInstance, // pointer to application TAPI instance handle
  724. DNGetApplicationInstance(), // instance handle of .DLL
  725. NULL, // callback function (not used)
  726. NULL, // friendly application name (none)
  727. &m_TAPIInfo.dwLinesAvailable, // pointer to number of devices available to TAPI
  728. &m_TAPIInfo.dwVersion, // pointer to input/output TAPI version
  729. &LineInitializeExParams ); // pointer to extra params
  730. }
  731. if ( lReturn == LINEERR_NONE )
  732. {
  733. m_hTAPIEvent = MAKE_DNHANDLE(LineInitializeExParams.Handles.hEvent);
  734. m_fTAPIAvailable = TRUE;
  735. }
  736. else
  737. {
  738. DPFX(DPFPREP, 0, "Failed to initialize TAPI for Win9x!" );
  739. DNASSERT( m_hTAPIEvent == NULL );
  740. DNASSERT( m_hFakeTAPIEvent == NULL );
  741. m_hFakeTAPIEvent = DNCreateEvent( NULL, // pointer to security (none)
  742. TRUE, // manual reset
  743. FALSE, // start unsignalled
  744. NULL ); // pointer to name (none)
  745. if ( m_hFakeTAPIEvent == NULL )
  746. {
  747. DWORD dwError;
  748. dwError = GetLastError();
  749. DPFX(DPFPREP, 0, "Failed to create fake TAPI event!" );
  750. DisplayErrorCode( 0, hr );
  751. hr = DPNERR_OUTOFMEMORY;
  752. goto Failure;
  753. }
  754. m_hTAPIEvent = m_hFakeTAPIEvent;
  755. DNASSERT( m_fTAPIAvailable == FALSE );
  756. }
  757. //
  758. // create send complete event
  759. //
  760. DNASSERT( m_hSendComplete == NULL );
  761. m_hSendComplete = DNCreateEvent( NULL, // pointer to security (none)
  762. TRUE, // manual reset
  763. FALSE, // start unsignalled
  764. NULL // pointer to name (none)
  765. );
  766. if ( m_hSendComplete == NULL )
  767. {
  768. DWORD dwError;
  769. dwError = GetLastError();
  770. DPFX(DPFPREP, 0, "Failed to create event for Send!" );
  771. DisplayErrorCode( 0, dwError );
  772. hr = DPNERR_OUTOFMEMORY;
  773. goto Failure;
  774. }
  775. //
  776. // create receive complete event
  777. //
  778. DNASSERT( m_hReceiveComplete == NULL );
  779. m_hReceiveComplete = DNCreateEvent( NULL, // pointer to security (none)
  780. TRUE, // manual reset
  781. FALSE, // start unsignalled
  782. NULL // pointer to name (none)
  783. );
  784. if ( m_hReceiveComplete == NULL )
  785. {
  786. DWORD dwError;
  787. dwError = GetLastError();
  788. DPFX(DPFPREP, 0, "Failed to create event for Receive!" );
  789. DisplayErrorCode( 0, dwError );
  790. hr = DPNERR_OUTOFMEMORY;
  791. goto Failure;
  792. }
  793. //
  794. // create parameters to worker threads
  795. //
  796. pPrimaryThreadInput = static_cast<WIN9X_THREAD_DATA*>( DNMalloc( sizeof( *pPrimaryThreadInput ) ) );
  797. if ( pPrimaryThreadInput == NULL )
  798. {
  799. DPFX(DPFPREP, 0, "Problem allocating memory for primary Win9x thread!" );
  800. hr = DPNERR_OUTOFMEMORY;
  801. goto Failure;
  802. }
  803. memset( pPrimaryThreadInput, 0x00, sizeof( *pPrimaryThreadInput ) );
  804. pPrimaryThreadInput->pThisThreadPool = this;
  805. //
  806. // Create one worker thread and boost its priority. If the primary thread
  807. // can be created and boosted, create a secondary thread. Do not create a
  808. // secondary thread if the primary could not be boosted because the system
  809. // is probably low on resources.
  810. //
  811. IncrementActiveThreadCount();
  812. hPrimaryThread = DNCreateThread( NULL, // pointer to security attributes (none)
  813. 0, // stack size (default)
  814. PrimaryWin9xThread, // pointer to thread function
  815. pPrimaryThreadInput, // pointer to input parameter
  816. 0, // let it run
  817. &dwPrimaryThreadID // pointer to destination of thread ID
  818. );
  819. if ( hPrimaryThread == NULL )
  820. {
  821. DWORD dwError;
  822. //
  823. // Failed to create thread, decrement active thread count and report
  824. // error.
  825. //
  826. DecrementActiveThreadCount();
  827. dwError = GetLastError();
  828. DPFX(DPFPREP, 0, "Problem creating Win9x thread!" );
  829. DisplayErrorCode( 0, dwError );
  830. hr = DPNERR_OUTOFMEMORY;
  831. goto Failure;
  832. }
  833. pPrimaryThreadInput = NULL;
  834. DPFX(DPFPREP, 8, "Created primary Win9x thread: 0x%x\tTotal Thread Count: %d", dwPrimaryThreadID, ThreadCount() );
  835. DNASSERT( hPrimaryThread != NULL );
  836. #if ADJUST_THREAD_PRIORITY
  837. if ( SetThreadPriority( hPrimaryThread, THREAD_PRIORITY_ABOVE_NORMAL ) == FALSE )
  838. {
  839. DWORD dwError;
  840. dwError = GetLastError();
  841. DPFX(DPFPREP, 0, "Failed to boost priority of primary Win9x read thread! Not starting secondary thread" );
  842. DisplayErrorCode( 0, dwError );
  843. }
  844. #endif // ADJUST_THREAD_PRIORITY
  845. //
  846. // Disallow thread reduction right off the bat.
  847. // We give them these two threads and that's what they're stuck with.
  848. //
  849. m_fAllowThreadCountReduction = FALSE;
  850. Exit:
  851. if ( pPrimaryThreadInput != NULL )
  852. {
  853. DNFree( pPrimaryThreadInput );
  854. pPrimaryThreadInput = NULL;
  855. }
  856. if ( hPrimaryThread != NULL )
  857. {
  858. if ( DNCloseHandle( hPrimaryThread ) == FALSE )
  859. {
  860. DWORD dwError;
  861. dwError = GetLastError();
  862. DPFX(DPFPREP, 0, "Problem closing Win9x thread hanle!" );
  863. DisplayErrorCode( 0, dwError );
  864. }
  865. hPrimaryThread = NULL;
  866. }
  867. return hr;
  868. Failure:
  869. DPFX(DPFPREP, 0, "Failed Win9x Initialization!" );
  870. DisplayDNError( 0, hr );
  871. goto Exit;
  872. }
  873. //**********************************************************************
  874. #endif // WIN95
  875. #ifdef WINNT
  876. //**********************************************************************
  877. // ------------------------------
  878. // CModemThreadPool::StartNTCompletionThread - start a WinNT completion thread
  879. //
  880. // Entry: Nothing
  881. //
  882. // Exit: Nothing
  883. // ------------------------------
  884. #undef DPF_MODNAME
  885. #define DPF_MODNAME "CModemThreadPool::StartNTCompletionThread"
  886. void CModemThreadPool::StartNTCompletionThread( void )
  887. {
  888. DNHANDLE hThread;
  889. DWORD dwThreadID;
  890. IOCOMPLETION_THREAD_DATA *pIOCompletionThreadData;
  891. pIOCompletionThreadData = static_cast<IOCOMPLETION_THREAD_DATA*>( DNMalloc( sizeof( *pIOCompletionThreadData ) ) );
  892. if ( pIOCompletionThreadData != NULL )
  893. {
  894. pIOCompletionThreadData->pThisThreadPool = this;
  895. hThread = NULL;
  896. hThread = DNCreateThread( NULL, // pointer to security attributes (none)
  897. 0, // stack size (default)
  898. WinNTIOCompletionThread, // thread function
  899. pIOCompletionThreadData, // thread parameter
  900. 0, // start thread immediately
  901. &dwThreadID // pointer to thread ID destination
  902. );
  903. if ( hThread != NULL )
  904. {
  905. //
  906. // note that a thread was created, and close the handle
  907. // to the thread because it's no longer needed.
  908. //
  909. IncrementActiveNTCompletionThreadCount();
  910. DPFX(DPFPREP, 8, "Creating I/O completion thread: 0x%x\tTotal Thread Count: %d\tNT Completion Thread Count: %d", dwThreadID, ThreadCount(), NTCompletionThreadCount() );
  911. if ( DNCloseHandle( hThread ) == FALSE )
  912. {
  913. DWORD dwError;
  914. dwError = GetLastError();
  915. DPFX(DPFPREP, 0, "Problem creating thread for I/O completion port" );
  916. DisplayErrorCode( 0, dwError );
  917. }
  918. }
  919. else
  920. {
  921. DWORD dwError;
  922. dwError = GetLastError();
  923. DPFX(DPFPREP, 0, "Failed to create I/O completion thread!" );
  924. DisplayErrorCode( 0, dwError );
  925. DNFree( pIOCompletionThreadData );
  926. }
  927. }
  928. }
  929. //**********************************************************************
  930. #endif // WINNT
  931. //**********************************************************************
  932. // ------------------------------
  933. // CModemThreadPool::StopAllThreads - stop all work threads
  934. //
  935. // Entry: Nothing
  936. //
  937. // Exit: Nothing
  938. // ------------------------------
  939. #undef DPF_MODNAME
  940. #define DPF_MODNAME "CModemThreadPool::StopAllThreads"
  941. void CModemThreadPool::StopAllThreads( void )
  942. {
  943. //
  944. // stop all non-I/O completion threads
  945. //
  946. if ( m_hStopAllThreads != NULL )
  947. {
  948. if ( DNSetEvent( m_hStopAllThreads ) == FALSE )
  949. {
  950. DWORD dwError;
  951. dwError = GetLastError();
  952. DPFX(DPFPREP, 0, "Failed to set event to stop all threads!" );
  953. DisplayErrorCode( 0, dwError );
  954. }
  955. }
  956. //
  957. // If we're running on NT submit enough jobs to stop all threads.
  958. //
  959. #ifdef WINNT
  960. UINT_PTR uIndex;
  961. uIndex = NTCompletionThreadCount();
  962. while ( uIndex > 0 )
  963. {
  964. uIndex--;
  965. if ( PostQueuedCompletionStatus( m_hIOCompletionPort, // handle of completion port
  966. 0, // number of bytes transferred
  967. IO_COMPLETION_KEY_SP_CLOSE, // completion key
  968. NULL // pointer to overlapped structure (none)
  969. ) == FALSE )
  970. {
  971. DWORD dwError;
  972. dwError = GetLastError();
  973. DPFX(DPFPREP, 0, "Problem submitting Stop job to IO completion port!" );
  974. DisplayErrorCode( 0, dwError );
  975. }
  976. }
  977. #endif // WINNT
  978. //
  979. // check for outstanding threads (no need to lock thread pool count)
  980. //
  981. DPFX(DPFPREP, 8, "Number of outstanding threads: %d", ThreadCount() );
  982. while ( ThreadCount() != 0 )
  983. {
  984. DPFX(DPFPREP, 8, "Waiting for %d threads to quit.", ThreadCount() );
  985. SleepEx( WORK_THREAD_CLOSE_SLEEP_TIME, TRUE );
  986. }
  987. DNASSERT( ThreadCount() == 0 );
  988. m_iTotalThreadCount = 0;
  989. }
  990. //**********************************************************************
  991. //**********************************************************************
  992. // ------------------------------
  993. // CModemThreadPool::CancelOutstandingIO - cancel outstanding IO
  994. //
  995. // Entry: Nothing
  996. //
  997. // Exit: Nothing
  998. // ------------------------------
  999. #undef DPF_MODNAME
  1000. #define DPF_MODNAME "CModemThreadPool::CancelOutstandingIO"
  1001. void CModemThreadPool::CancelOutstandingIO( void )
  1002. {
  1003. CBilink *pTemp;
  1004. DNASSERT( ThreadCount() == 0 );
  1005. //
  1006. // stop any receives with the notification that they were cancelled
  1007. //
  1008. pTemp = m_OutstandingReadList.GetNext();
  1009. while ( pTemp != &m_OutstandingReadList )
  1010. {
  1011. CModemReadIOData *pReadData;
  1012. pReadData = CModemReadIOData::ReadDataFromBilink( pTemp );
  1013. pTemp = pTemp->GetNext();
  1014. pReadData->m_OutstandingReadListLinkage.RemoveFromList();
  1015. DPFX(DPFPREP, 1, "Forcing read data 0x%p to be cancelled.", pReadData);
  1016. #ifdef WIN95
  1017. DNASSERT( pReadData->Win9xOperationPending() != FALSE );
  1018. pReadData->SetWin9xOperationPending( FALSE );
  1019. #endif // WIN95
  1020. pReadData->DataPort()->ProcessReceivedData( 0, ERROR_OPERATION_ABORTED );
  1021. }
  1022. //
  1023. // stop any pending writes with the notification that the user cancelled it.
  1024. //
  1025. pTemp = m_OutstandingWriteList.GetNext();
  1026. while ( pTemp != &m_OutstandingWriteList )
  1027. {
  1028. CModemWriteIOData *pWriteData;
  1029. pWriteData = CModemWriteIOData::WriteDataFromBilink( pTemp );
  1030. pTemp = pTemp->GetNext();
  1031. pWriteData->m_OutstandingWriteListLinkage.RemoveFromList();
  1032. DPFX(DPFPREP, 1, "Forcing write data 0x%p to be cancelled.", pWriteData);
  1033. #ifdef WIN95
  1034. DNASSERT( pWriteData->Win9xOperationPending() != FALSE );
  1035. pWriteData->SetWin9xOperationPending( FALSE );
  1036. #endif // WIN95
  1037. pWriteData->DataPort()->SendComplete( pWriteData, DPNERR_USERCANCEL );
  1038. }
  1039. while ( m_JobQueue.IsEmpty() == FALSE )
  1040. {
  1041. THREAD_POOL_JOB *pJob;
  1042. pJob = m_JobQueue.DequeueJob();
  1043. DNASSERT( pJob != NULL );
  1044. if (pJob->pCancelFunction != NULL)
  1045. {
  1046. pJob->pCancelFunction( pJob );
  1047. }
  1048. pJob->JobType = JOB_UNINITIALIZED;
  1049. g_ModemThreadPoolJobPool.Release( pJob );
  1050. };
  1051. }
  1052. //**********************************************************************
  1053. //**********************************************************************
  1054. // ------------------------------
  1055. // CModemThreadPool::ReturnSelfToPool - return this object to the pool
  1056. //
  1057. // Entry: Nothing
  1058. //
  1059. // Exit: Nothing
  1060. // ------------------------------
  1061. #undef DPF_MODNAME
  1062. #define DPF_MODNAME "CModemThreadPool::ReturnSelfToPool"
  1063. void CModemThreadPool::ReturnSelfToPool( void )
  1064. {
  1065. g_ModemThreadPoolPool.Release( this );
  1066. }
  1067. //**********************************************************************
  1068. //**********************************************************************
  1069. // ------------------------------
  1070. // CModemThreadPool::ProcessTimerJobs - process timed jobs
  1071. //
  1072. // Entry: Pointer to job list
  1073. // Pointer to destination for time of next job
  1074. //
  1075. // Exit: Boolean indicating active jobs exist
  1076. // TRUE = there are active jobs
  1077. // FALSE = there are no active jobs
  1078. //
  1079. // Notes: The input job queue is expected to be locked for the duration
  1080. // of this function call!
  1081. // ------------------------------
  1082. #undef DPF_MODNAME
  1083. #define DPF_MODNAME "CModemThreadPool::ProcessTimerJobs"
  1084. BOOL CModemThreadPool::ProcessTimerJobs( const CBilink *const pJobList, DWORD *const pdwNextJobTime )
  1085. {
  1086. BOOL fReturn;
  1087. CBilink *pWorkingEntry;
  1088. INT_PTR iActiveTimerJobCount;
  1089. DWORD dwCurrentTime;
  1090. DNASSERT( pJobList != NULL );
  1091. DNASSERT( pdwNextJobTime != NULL );
  1092. //
  1093. // Initialize. Set the next job time to be infinitely far in the future
  1094. // so this thread will wake up for any jobs that need to completed before
  1095. // then.
  1096. //
  1097. fReturn = FALSE;
  1098. DBG_CASSERT( OFFSETOF( TIMER_OPERATION_ENTRY, Linkage ) == 0 );
  1099. pWorkingEntry = pJobList->GetNext();
  1100. iActiveTimerJobCount = 0;
  1101. dwCurrentTime = GETTIMESTAMP();
  1102. (*pdwNextJobTime) = dwCurrentTime - 1;
  1103. //
  1104. // loop through all timer items
  1105. //
  1106. while ( pWorkingEntry != pJobList )
  1107. {
  1108. TIMER_OPERATION_ENTRY *pTimerEntry;
  1109. BOOL fJobActive;
  1110. pTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pWorkingEntry );
  1111. pWorkingEntry = pWorkingEntry->GetNext();
  1112. fJobActive = ProcessTimedOperation( pTimerEntry, dwCurrentTime, pdwNextJobTime );
  1113. DNASSERT( ( fJobActive == FALSE ) || ( fJobActive == TRUE ) );
  1114. fReturn |= fJobActive;
  1115. if ( fJobActive == FALSE )
  1116. {
  1117. RemoveTimerOperationEntry( pTimerEntry, DPN_OK );
  1118. }
  1119. }
  1120. DNASSERT( ( fReturn == FALSE ) || ( fReturn == TRUE ) );
  1121. return fReturn;
  1122. }
  1123. //**********************************************************************
  1124. //**********************************************************************
  1125. // ------------------------------
  1126. // CModemThreadPool::ProcessTimedOperation - process a timed operation
  1127. //
  1128. // Entry: Pointer to job information
  1129. // Current time
  1130. // Pointer to time to be updated
  1131. //
  1132. // Exit: Boolean indicating that the job is still active
  1133. // TRUE = operation active
  1134. // FALSE = operation not active
  1135. // ------------------------------
  1136. #undef DPF_MODNAME
  1137. #define DPF_MODNAME "CModemThreadPool::ProcessTimedOperation"
  1138. BOOL CModemThreadPool::ProcessTimedOperation( TIMER_OPERATION_ENTRY *const pTimedJob,
  1139. const DWORD dwCurrentTime,
  1140. DWORD *const pdwNextJobTime )
  1141. {
  1142. BOOL fEnumActive;
  1143. DNASSERT( pTimedJob != NULL );
  1144. DNASSERT( pdwNextJobTime != NULL );
  1145. DPFX(DPFPREP, 9, "(0x%p) Parameters: (0x%p, %u, 0x%p)",
  1146. this, pTimedJob, dwCurrentTime, pdwNextJobTime);
  1147. //
  1148. // Assume that this enum will remain active. If we retire this enum, this
  1149. // value will be reset.
  1150. //
  1151. fEnumActive = TRUE;
  1152. //
  1153. // If this enum has completed sending enums and is waiting only
  1154. // for responses, decrement the wait time (assuming it's not infinite)
  1155. // and remove the enum if the we've exceeded its wait time.
  1156. //
  1157. if ( pTimedJob->uRetryCount == 0 )
  1158. {
  1159. if ( (int) ( pTimedJob->dwIdleTimeout - dwCurrentTime ) <= 0 )
  1160. {
  1161. fEnumActive = FALSE;
  1162. }
  1163. else
  1164. {
  1165. //
  1166. // This enum isn't complete, check to see if it's the next enum
  1167. // to need service.
  1168. //
  1169. if ( (int) ( pTimedJob->dwIdleTimeout - (*pdwNextJobTime) ) < 0 )
  1170. {
  1171. (*pdwNextJobTime) = pTimedJob->dwIdleTimeout;
  1172. }
  1173. }
  1174. }
  1175. else
  1176. {
  1177. //
  1178. // This enum is still sending. Determine if it's time to send a new enum
  1179. // and adjust the wakeup time if appropriate.
  1180. //
  1181. if ( (int) ( pTimedJob->dwNextRetryTime - dwCurrentTime ) <= 0 )
  1182. {
  1183. #ifdef DBG
  1184. DWORD dwDelay;
  1185. dwDelay = dwCurrentTime - pTimedJob->dwNextRetryTime;
  1186. DPFX(DPFPREP, 7, "Threadpool 0x%p performing timed job 0x%p approximately %u ms after intended time of %u.",
  1187. this, pTimedJob, dwDelay, pTimedJob->dwNextRetryTime);
  1188. #endif // DBG
  1189. //
  1190. // Timeout, execute this timed item
  1191. //
  1192. pTimedJob->pTimerCallback( pTimedJob->pContext );
  1193. //
  1194. // If this job isn't running forever, decrement the retry count.
  1195. // If there are no more retries, set up wait time. If the job
  1196. // is waiting forever, set max wait timeout.
  1197. //
  1198. if ( pTimedJob->fRetryForever == FALSE )
  1199. {
  1200. pTimedJob->uRetryCount--;
  1201. if ( pTimedJob->uRetryCount == 0 )
  1202. {
  1203. if ( pTimedJob->fIdleWaitForever == FALSE )
  1204. {
  1205. //
  1206. // Compute stopping time for this job's 'Timeout' phase and
  1207. // see if this will be the next job to need service.
  1208. //
  1209. pTimedJob->dwIdleTimeout += dwCurrentTime;
  1210. if ( (int) (pTimedJob->dwIdleTimeout - (*pdwNextJobTime) ) < 0 )
  1211. {
  1212. (*pdwNextJobTime) = pTimedJob->dwIdleTimeout;
  1213. }
  1214. }
  1215. else
  1216. {
  1217. //
  1218. // We're waiting forever for enum returns. ASSERT that we
  1219. // have the maximum timeout and don't bother checking to see
  1220. // if this will be the next enum to need service (it'll never
  1221. // need service).
  1222. //
  1223. DNASSERT( pTimedJob->dwIdleTimeout == -1 );
  1224. }
  1225. goto SkipNextRetryTimeComputation;
  1226. }
  1227. }
  1228. pTimedJob->dwNextRetryTime = dwCurrentTime + pTimedJob->dwRetryInterval;
  1229. }
  1230. //
  1231. // is this the next enum to fire?
  1232. //
  1233. if ( (int) ( (*pdwNextJobTime) - pTimedJob->dwNextRetryTime ) < 0 )
  1234. {
  1235. (*pdwNextJobTime) = pTimedJob->dwNextRetryTime;
  1236. DPFX(DPFPREP, 8, "Job 0x%p is the next job to fire (at time %u).",
  1237. pTimedJob, pTimedJob->dwNextRetryTime);
  1238. }
  1239. else
  1240. {
  1241. //
  1242. // Not next job to fire.
  1243. //
  1244. }
  1245. SkipNextRetryTimeComputation:
  1246. //
  1247. // the following blank line is there to shut up the compiler
  1248. //
  1249. ;
  1250. }
  1251. DPFX(DPFPREP, 9, "(0x%p) Returning [%i]",
  1252. this, fEnumActive);
  1253. return fEnumActive;
  1254. }
  1255. //**********************************************************************
  1256. //**********************************************************************
  1257. // ------------------------------
  1258. // CModemThreadPool::SubmitWorkItem - submit a work item for processing and inform
  1259. // work thread that another job is available
  1260. //
  1261. // Entry: Pointer to job information
  1262. //
  1263. // Exit: Error code
  1264. //
  1265. // Note: This function assumes that the job data is locked.
  1266. // ------------------------------
  1267. #undef DPF_MODNAME
  1268. #define DPF_MODNAME "CModemThreadPool::SubmitWorkItem"
  1269. HRESULT CModemThreadPool::SubmitWorkItem( THREAD_POOL_JOB *const pJobInfo )
  1270. {
  1271. HRESULT hr;
  1272. DNASSERT( pJobInfo != NULL );
  1273. AssertCriticalSectionIsTakenByThisThread( &m_JobDataLock, TRUE );
  1274. //
  1275. // initialize
  1276. //
  1277. hr = DPN_OK;
  1278. //
  1279. // add job to queue and tell someone that there's a job available
  1280. //
  1281. m_JobQueue.Lock();
  1282. m_JobQueue.EnqueueJob( pJobInfo );
  1283. m_JobQueue.Unlock();
  1284. #ifdef WINNT
  1285. //
  1286. // WinNT, submit new I/O completion item
  1287. //
  1288. DNASSERT( m_hIOCompletionPort != NULL );
  1289. if ( PostQueuedCompletionStatus( m_hIOCompletionPort, // completion port
  1290. 0, // number of bytes written (unused)
  1291. IO_COMPLETION_KEY_NEW_JOB, // completion key
  1292. NULL // pointer to overlapped structure (unused)
  1293. ) == FALSE )
  1294. {
  1295. DWORD dwError;
  1296. hr = DPNERR_OUTOFMEMORY;
  1297. dwError = GetLastError();
  1298. DPFX(DPFPREP, 0, "Problem posting completion item for new job!" );
  1299. DisplayErrorCode( 0, dwError );
  1300. goto Failure;
  1301. }
  1302. #else // WIN95
  1303. //
  1304. // Win9x, set event that the work thread will listen for
  1305. //
  1306. DNASSERT( m_JobQueue.GetPendingJobHandle() != NULL );
  1307. if ( m_JobQueue.SignalPendingJob() == FALSE )
  1308. {
  1309. hr = DPNERR_OUTOFMEMORY;
  1310. DPFX(DPFPREP, 0, "Failed to signal pending job!" );
  1311. goto Failure;
  1312. }
  1313. #endif // WINNT
  1314. Exit:
  1315. if ( hr != DPN_OK )
  1316. {
  1317. DPFX(DPFPREP, 0, "Problem with SubmitWorkItem!" );
  1318. DisplayDNError( 0, hr );
  1319. }
  1320. return hr;
  1321. Failure:
  1322. goto Exit;
  1323. }
  1324. //**********************************************************************
  1325. //**********************************************************************
  1326. // ------------------------------
  1327. // CModemThreadPool::GetWorkItem - get a work item from the job queue
  1328. //
  1329. // Entry: Nothing
  1330. //
  1331. // Exit: Pointer to job information (may be NULL)
  1332. // ------------------------------
  1333. #undef DPF_MODNAME
  1334. #define DPF_MODNAME "CModemThreadPool::GetWorkItem"
  1335. THREAD_POOL_JOB *CModemThreadPool::GetWorkItem( void )
  1336. {
  1337. THREAD_POOL_JOB *pReturn;
  1338. //
  1339. // initialize
  1340. //
  1341. pReturn = NULL;
  1342. m_JobQueue.Lock();
  1343. pReturn = m_JobQueue.DequeueJob();
  1344. //
  1345. // if we're under Win9x (we have a 'pending job' handle),
  1346. // see if the handle needs to be reset
  1347. //
  1348. if ( m_JobQueue.IsEmpty() != FALSE )
  1349. {
  1350. DNASSERT( m_JobQueue.GetPendingJobHandle() != NULL );
  1351. if ( DNResetEvent( m_JobQueue.GetPendingJobHandle() ) == FALSE )
  1352. {
  1353. DWORD dwError;
  1354. dwError = GetLastError();
  1355. DPFX(DPFPREP, 0, "Problem resetting event for pending Win9x jobs!" );
  1356. DisplayErrorCode( 0, dwError );
  1357. }
  1358. }
  1359. m_JobQueue.Unlock();
  1360. return pReturn;
  1361. }
  1362. //**********************************************************************
  1363. //**********************************************************************
  1364. // ------------------------------
  1365. // CModemThreadPool::SubmitTimerJob - add a timer job to the timer list
  1366. //
  1367. // Entry: Retry count
  1368. // Boolean indicating that we retry forever
  1369. // Retry interval
  1370. // Boolean indicating that we wait forever
  1371. // Idle wait interval
  1372. // Pointer to callback when event fires
  1373. // Pointer to callback when event complete
  1374. // User context
  1375. //
  1376. // Exit: Error code
  1377. // ------------------------------
  1378. #undef DPF_MODNAME
  1379. #define DPF_MODNAME "CModemThreadPool::SubmitTimerJob"
  1380. HRESULT CModemThreadPool::SubmitTimerJob( const UINT_PTR uRetryCount,
  1381. const BOOL fRetryForever,
  1382. const DWORD dwRetryInterval,
  1383. const BOOL fIdleWaitForever,
  1384. const DWORD dwIdleTimeout,
  1385. TIMER_EVENT_CALLBACK *const pTimerCallbackFunction,
  1386. TIMER_EVENT_COMPLETE *const pTimerCompleteFunction,
  1387. void *const pContext )
  1388. {
  1389. HRESULT hr;
  1390. TIMER_OPERATION_ENTRY *pEntry;
  1391. THREAD_POOL_JOB *pJob;
  1392. BOOL fTimerJobListLocked;
  1393. DNASSERT( uRetryCount != 0 );
  1394. DNASSERT( pTimerCallbackFunction != NULL );
  1395. DNASSERT( pTimerCompleteFunction != NULL );
  1396. DNASSERT( pContext != NULL ); // must be non-NULL because it's the lookup key to remove job
  1397. //
  1398. // initialize
  1399. //
  1400. hr = DPN_OK;
  1401. pEntry = NULL;
  1402. pJob = NULL;
  1403. fTimerJobListLocked = FALSE;
  1404. LockJobData();
  1405. //
  1406. // If we're on NT, attempt to start the enum thread here so we can return
  1407. // an error if it fails to start. If it does start, it'll sit until it's
  1408. // informed that an enum job has been added.
  1409. //
  1410. #ifdef WINNT
  1411. hr = StartNTTimerThread();
  1412. if ( hr != DPN_OK )
  1413. {
  1414. DPFX(DPFPREP, 0, "Cannot spin up NT timer thread!" );
  1415. DisplayDNError( 0, hr );
  1416. goto Failure;
  1417. }
  1418. #endif // WINNT
  1419. //
  1420. // allocate new enum entry
  1421. //
  1422. pEntry = static_cast<TIMER_OPERATION_ENTRY*>( g_ModemTimerEntryPool.Get( ) );
  1423. if ( pEntry == NULL )
  1424. {
  1425. hr = DPNERR_OUTOFMEMORY;
  1426. DPFX(DPFPREP, 0, "Cannot allocate memory to add to timer list!" );
  1427. goto Failure;
  1428. }
  1429. DNASSERT( pEntry->pContext == NULL );
  1430. //
  1431. // build timer entry block
  1432. //
  1433. pEntry->pContext = pContext;
  1434. pEntry->uRetryCount = uRetryCount;
  1435. pEntry->fRetryForever = fRetryForever;
  1436. pEntry->dwRetryInterval = dwRetryInterval;
  1437. pEntry->dwIdleTimeout = dwIdleTimeout;
  1438. pEntry->fIdleWaitForever = fIdleWaitForever;
  1439. pEntry->pTimerCallback = pTimerCallbackFunction;
  1440. pEntry->pTimerComplete = pTimerCompleteFunction;
  1441. //
  1442. // set this enum to fire as soon as it gets a chance
  1443. //
  1444. pEntry->dwNextRetryTime = 0;
  1445. pJob = static_cast<THREAD_POOL_JOB*>( g_ModemThreadPoolJobPool.Get( ) );
  1446. if ( pJob == NULL )
  1447. {
  1448. hr = DPNERR_OUTOFMEMORY;
  1449. DPFX(DPFPREP, 0, "Cannot allocate memory for enum job!" );
  1450. goto Failure;
  1451. }
  1452. //
  1453. // Create job for work thread.
  1454. //
  1455. pJob->pCancelFunction = NULL;
  1456. pJob->JobType = JOB_REFRESH_TIMER_JOBS;
  1457. // set our dummy paramter to simulate passing data
  1458. DEBUG_ONLY( pJob->JobData.JobRefreshTimedJobs.uDummy = 0 );
  1459. LockTimerData();
  1460. fTimerJobListLocked = TRUE;
  1461. //
  1462. // we can submit the 'ENUM_REFRESH' job before inserting the enum entry
  1463. // into the active enum list because nobody will be able to pull the
  1464. // 'ENUM_REFRESH' job from the queue since we have the queue locked
  1465. //
  1466. hr = SubmitWorkItem( pJob );
  1467. if ( hr != DPN_OK )
  1468. {
  1469. DPFX(DPFPREP, 0, "Problem submitting enum work item" );
  1470. DisplayDNError( 0, hr );
  1471. goto Failure;
  1472. }
  1473. //
  1474. // debug block to check for duplicate contexts
  1475. //
  1476. DEBUG_ONLY(
  1477. {
  1478. CBilink *pTempLink;
  1479. pTempLink = m_TimerJobList.GetNext();
  1480. while ( pTempLink != &m_TimerJobList )
  1481. {
  1482. TIMER_OPERATION_ENTRY *pTempTimerEntry;
  1483. pTempTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempLink );
  1484. DNASSERT( pTempTimerEntry->pContext != pContext );
  1485. pTempLink = pTempLink->GetNext();
  1486. }
  1487. }
  1488. );
  1489. //
  1490. // link to rest of list
  1491. //
  1492. pEntry->Linkage.InsertAfter( &m_TimerJobList );
  1493. Exit:
  1494. if ( fTimerJobListLocked != FALSE )
  1495. {
  1496. UnlockTimerData();
  1497. fTimerJobListLocked = FALSE;
  1498. }
  1499. UnlockJobData();
  1500. if ( hr != DPN_OK )
  1501. {
  1502. DPFX(DPFPREP, 0, "Problem with SubmitEnumJob" );
  1503. DisplayDNError( 0, hr );
  1504. }
  1505. return hr;
  1506. Failure:
  1507. if ( pEntry != NULL )
  1508. {
  1509. g_ModemTimerEntryPool.Release( pEntry );
  1510. DEBUG_ONLY( pEntry = NULL );
  1511. }
  1512. if ( pJob != NULL )
  1513. {
  1514. g_ModemThreadPoolJobPool.Release( pJob );
  1515. DEBUG_ONLY( pJob = NULL );
  1516. }
  1517. //
  1518. // It's possible that the enum thread has been started for this enum.
  1519. // Since there's no way to stop it without completing the enums or
  1520. // closing the SP, leave it running.
  1521. //
  1522. goto Exit;
  1523. }
  1524. //**********************************************************************
  1525. //**********************************************************************
  1526. // ------------------------------
  1527. // CModemThreadPool::StopTimerJob - remove timer job from list
  1528. //
  1529. // Entry: Pointer to job context (these MUST be uniquie for jobs)
  1530. // Command result
  1531. //
  1532. // Exit: Boolean indicating whether a job was stopped or not
  1533. //
  1534. // Note: This function is for the forced removal of a job from the timed job
  1535. // list. It is assumed that the caller of this function will clean
  1536. // up any messes.
  1537. // ------------------------------
  1538. #undef DPF_MODNAME
  1539. #define DPF_MODNAME "CModemThreadPool::StopTimerJob"
  1540. BOOL CModemThreadPool::StopTimerJob( void *const pContext, const HRESULT hCommandResult )
  1541. {
  1542. BOOL fComplete = FALSE;
  1543. CBilink * pTempEntry;
  1544. TIMER_OPERATION_ENTRY * pTimerEntry;
  1545. DNASSERT( pContext != NULL );
  1546. DPFX(DPFPREP, 9, "Parameters (0x%p, 0x%lx)", pContext, hCommandResult);
  1547. //
  1548. // initialize
  1549. //
  1550. LockTimerData();
  1551. pTempEntry = m_TimerJobList.GetNext();
  1552. while ( pTempEntry != &m_TimerJobList )
  1553. {
  1554. pTimerEntry = TIMER_OPERATION_ENTRY::TimerOperationFromLinkage( pTempEntry );
  1555. if ( pTimerEntry->pContext == pContext )
  1556. {
  1557. //
  1558. // remove this link from the list
  1559. //
  1560. pTimerEntry->Linkage.RemoveFromList();
  1561. fComplete = TRUE;
  1562. //
  1563. // terminate loop
  1564. //
  1565. break;
  1566. }
  1567. pTempEntry = pTempEntry->GetNext();
  1568. }
  1569. UnlockTimerData();
  1570. //
  1571. // tell owner that the job is complete and return the job to the pool
  1572. // outside of the lock
  1573. //
  1574. if (fComplete)
  1575. {
  1576. pTimerEntry->pTimerComplete( hCommandResult, pTimerEntry->pContext );
  1577. g_ModemTimerEntryPool.Release( pTimerEntry );
  1578. }
  1579. DPFX(DPFPREP, 9, "Returning [%i]", fComplete);
  1580. return fComplete;
  1581. }
  1582. //**********************************************************************
  1583. //**********************************************************************
  1584. // ------------------------------
  1585. // CModemThreadPool::SpawnDialogThread - start a secondary thread to display service
  1586. // provider UI.
  1587. //
  1588. // Entry: Pointer to dialog function
  1589. // Dialog context
  1590. //
  1591. // Exit: Error code
  1592. // ------------------------------
  1593. #undef DPF_MODNAME
  1594. #define DPF_MODNAME "CModemThreadPool::SpawnDialogThread"
  1595. HRESULT CModemThreadPool::SpawnDialogThread( DIALOG_FUNCTION *const pDialogFunction, void *const pDialogContext )
  1596. {
  1597. HRESULT hr;
  1598. DNHANDLE hDialogThread;
  1599. DIALOG_THREAD_PARAM *pThreadParam;
  1600. DWORD dwThreadID;
  1601. DNASSERT( pDialogFunction != NULL );
  1602. DNASSERT( pDialogContext != NULL ); // why would anyone not want a dialog context??
  1603. //
  1604. // initialize
  1605. //
  1606. hr = DPN_OK;
  1607. pThreadParam = NULL;
  1608. //
  1609. // create and initialize thread param
  1610. //
  1611. pThreadParam = static_cast<DIALOG_THREAD_PARAM*>( DNMalloc( sizeof( *pThreadParam ) ) );
  1612. if ( pThreadParam == NULL )
  1613. {
  1614. hr = DPNERR_OUTOFMEMORY;
  1615. DPFX(DPFPREP, 0, "Failed to allocate memory for dialog thread!" );
  1616. goto Failure;
  1617. }
  1618. pThreadParam->pDialogFunction = pDialogFunction;
  1619. pThreadParam->pContext = pDialogContext;
  1620. pThreadParam->pThisThreadPool = this;
  1621. //
  1622. // assume that a thread will be created
  1623. //
  1624. IncrementActiveThreadCount();
  1625. //
  1626. // create thread
  1627. //
  1628. hDialogThread = DNCreateThread( NULL, // pointer to security (none)
  1629. 0, // stack size (default)
  1630. DialogThreadProc, // thread procedure
  1631. pThreadParam, // thread param
  1632. 0, // creation flags (none)
  1633. &dwThreadID ); // pointer to thread ID
  1634. if ( hDialogThread == NULL )
  1635. {
  1636. DWORD dwError;
  1637. //
  1638. // decrement active thread count and report error
  1639. //
  1640. DecrementActiveThreadCount();
  1641. dwError = GetLastError();
  1642. DPFX(DPFPREP, 0, "Failed to start dialog thread!" );
  1643. DisplayErrorCode( 0, dwError );
  1644. goto Failure;
  1645. }
  1646. if ( DNCloseHandle( hDialogThread ) == FALSE )
  1647. {
  1648. DWORD dwError;
  1649. dwError = GetLastError();
  1650. DPFX(DPFPREP, 0, "Problem closing handle from create dialog thread!" );
  1651. DisplayErrorCode( 0, dwError );
  1652. }
  1653. Exit:
  1654. return hr;
  1655. Failure:
  1656. if ( pThreadParam != NULL )
  1657. {
  1658. DNFree( pThreadParam );
  1659. pThreadParam = NULL;
  1660. }
  1661. goto Exit;
  1662. }
  1663. //**********************************************************************
  1664. //**********************************************************************
  1665. // ------------------------------
  1666. // CModemThreadPool::GetIOThreadCount - get I/O thread count
  1667. //
  1668. // Entry: Pointer to variable to fill
  1669. //
  1670. // Exit: Error code
  1671. // ------------------------------
  1672. #undef DPF_MODNAME
  1673. #define DPF_MODNAME "CModemThreadPool::GetIOThreadCount"
  1674. HRESULT CModemThreadPool::GetIOThreadCount( LONG *const piThreadCount )
  1675. {
  1676. HRESULT hr;
  1677. DNASSERT( piThreadCount != NULL );
  1678. //
  1679. // initialize
  1680. //
  1681. hr = DPN_OK;
  1682. Lock();
  1683. if ( IsThreadCountReductionAllowed() )
  1684. {
  1685. *piThreadCount = GetIntendedThreadCount();
  1686. }
  1687. else
  1688. {
  1689. #ifdef WIN95
  1690. *piThreadCount = ThreadCount();
  1691. #else // WINNT
  1692. DNASSERT( NTCompletionThreadCount() != 0 );
  1693. *piThreadCount = NTCompletionThreadCount();
  1694. #endif // WIN95
  1695. }
  1696. Unlock();
  1697. return hr;
  1698. }
  1699. //**********************************************************************
  1700. //**********************************************************************
  1701. // ------------------------------
  1702. // CModemThreadPool::SetIOThreadCount - set I/O thread count
  1703. //
  1704. // Entry: New thread count
  1705. //
  1706. // Exit: Error code
  1707. // ------------------------------
  1708. #undef DPF_MODNAME
  1709. #define DPF_MODNAME "CModemThreadPool::SetIOThreadCount"
  1710. HRESULT CModemThreadPool::SetIOThreadCount( const LONG iThreadCount )
  1711. {
  1712. HRESULT hr;
  1713. DNASSERT( iThreadCount > 0 );
  1714. //
  1715. // initialize
  1716. //
  1717. hr = DPN_OK;
  1718. Lock();
  1719. if ( IsThreadCountReductionAllowed() )
  1720. {
  1721. DPFX(DPFPREP, 4, "Thread pool not locked down, setting intended thread count to %i.", iThreadCount );
  1722. SetIntendedThreadCount( iThreadCount );
  1723. }
  1724. else
  1725. {
  1726. #ifdef WIN95
  1727. //
  1728. // Win9x can not adjust thread count.
  1729. //
  1730. DPFX(DPFPREP, 4, "Thread pool locked down and already has %i 9x threads, not adjusting to %i.",
  1731. ThreadCount(), iThreadCount );
  1732. #else // WINNT
  1733. //
  1734. // WinNT can have many threads. If the user wants more threads, attempt
  1735. // to boost the thread pool to the requested amount (if we fail to
  1736. // start a new thread, too bad). If the user wants fewer threads, check
  1737. // to see if the thread pool has been locked out of changes. If not,
  1738. // start killing off the threads.
  1739. //
  1740. if ( iThreadCount > NTCompletionThreadCount() )
  1741. {
  1742. INT_PTR iDeltaThreads;
  1743. iDeltaThreads = iThreadCount - NTCompletionThreadCount();
  1744. DPFX(DPFPREP, 4, "Thread pool locked down, spawning %i new NT threads (for a total of %i).",
  1745. iDeltaThreads, iThreadCount );
  1746. while ( iDeltaThreads > 0 )
  1747. {
  1748. iDeltaThreads--;
  1749. StartNTCompletionThread();
  1750. }
  1751. }
  1752. else
  1753. {
  1754. DPFX(DPFPREP, 4, "Thread pool locked down and already has %i NT threads, not adjusting to %i.",
  1755. NTCompletionThreadCount(), iThreadCount );
  1756. }
  1757. #endif // WIN95
  1758. }
  1759. Unlock();
  1760. return hr;
  1761. }
  1762. //**********************************************************************
  1763. //**********************************************************************
  1764. // ------------------------------
  1765. // CModemThreadPool::PreventThreadPoolReduction - prevents the thread pool size from being reduced
  1766. //
  1767. // Entry: None
  1768. //
  1769. // Exit: Error code
  1770. // ------------------------------
  1771. #undef DPF_MODNAME
  1772. #define DPF_MODNAME "CModemThreadPool::PreventThreadPoolReduction"
  1773. HRESULT CModemThreadPool::PreventThreadPoolReduction( void )
  1774. {
  1775. HRESULT hr = DPN_OK;
  1776. LONG iDesiredThreads;
  1777. #ifdef DBG
  1778. DWORD dwStartTime;
  1779. #endif // DBG
  1780. Lock();
  1781. //
  1782. // If we haven't already clamped down, do so, and spin up the threads.
  1783. //
  1784. if ( IsThreadCountReductionAllowed() )
  1785. {
  1786. m_fAllowThreadCountReduction = FALSE;
  1787. DNASSERT( GetIntendedThreadCount() > 0 );
  1788. DNASSERT( ThreadCount() == 0 );
  1789. iDesiredThreads = GetIntendedThreadCount();
  1790. SetIntendedThreadCount( 0 );
  1791. DPFX(DPFPREP, 3, "Locking down thread count at %i.", iDesiredThreads );
  1792. #ifdef DBG
  1793. dwStartTime = GETTIMESTAMP();
  1794. #endif // DBG
  1795. //
  1796. // OS-specific thread starting.
  1797. //
  1798. #ifdef WINNT
  1799. //
  1800. // WinNT
  1801. //
  1802. DNASSERT( NTCompletionThreadCount() == 0 );
  1803. while ( iDesiredThreads > 0 )
  1804. {
  1805. iDesiredThreads--;
  1806. StartNTCompletionThread();
  1807. }
  1808. //
  1809. // If at least one thread was created, the SP will perform in a
  1810. // non-optimal fashion, but we will still function. If no threads
  1811. // were created, fail.
  1812. //
  1813. if ( ThreadCount() == 0 )
  1814. {
  1815. hr = DPNERR_OUTOFMEMORY;
  1816. DPFX(DPFPREP, 0, "Unable to create any threads to service NT I/O completion port!" );
  1817. goto Failure;
  1818. }
  1819. #else // WIN95
  1820. //
  1821. // Windows 9x
  1822. //
  1823. //
  1824. // We should never get here because there will always only
  1825. // be 1 thread.
  1826. //
  1827. DNASSERT( FALSE );
  1828. #endif // WINNT
  1829. #ifdef DBG
  1830. DPFX(DPFPREP, 8, "Spent %u ms starting %i threads.", (GETTIMESTAMP() - dwStartTime), ThreadCount());
  1831. #endif // DBG
  1832. }
  1833. else
  1834. {
  1835. DPFX(DPFPREP, 3, "Thread count already locked down (at %i).", ThreadCount() );
  1836. }
  1837. #ifdef WINNT
  1838. Exit:
  1839. #endif // WINNT
  1840. Unlock();
  1841. return hr;
  1842. #ifdef WINNT
  1843. Failure:
  1844. goto Exit;
  1845. #endif // WINNT
  1846. }
  1847. //**********************************************************************
  1848. //**********************************************************************
  1849. // ------------------------------
  1850. // CModemThreadPool::CreateDataPortHandle - create a new handle and assign a CDataPort
  1851. // to it
  1852. //
  1853. // Entry: Nothing
  1854. //
  1855. // Exit: Error code
  1856. // ------------------------------
  1857. HRESULT CModemThreadPool::CreateDataPortHandle( CDataPort *const pDataPort )
  1858. {
  1859. HRESULT hr;
  1860. DPNHANDLE hDataPort;
  1861. DNASSERT( pDataPort != NULL );
  1862. //
  1863. // initialize
  1864. //
  1865. hr = DPN_OK;
  1866. hDataPort = 0;
  1867. hr = m_DataPortHandleTable.Create( pDataPort, &hDataPort );
  1868. if ( hr != DPN_OK )
  1869. {
  1870. DNASSERT( hDataPort == 0 );
  1871. DPFX(DPFPREP, 0, "Failed to create handle!" );
  1872. DisplayDNError( 0, hr );
  1873. goto Failure;
  1874. }
  1875. pDataPort->SetHandle( hDataPort );
  1876. pDataPort->AddRef();
  1877. Exit:
  1878. return hr;
  1879. Failure:
  1880. DNASSERT( hDataPort == 0 );
  1881. DNASSERT( pDataPort->GetHandle() == 0 );
  1882. goto Exit;
  1883. }
  1884. //**********************************************************************
  1885. //**********************************************************************
  1886. // ------------------------------
  1887. // CModemThreadPool::CloseDataPortHandle - invalidate a handle for a CDataPort
  1888. //
  1889. // Entry: Nothing
  1890. //
  1891. // Exit: Nothing
  1892. // ------------------------------
  1893. void CModemThreadPool::CloseDataPortHandle( CDataPort *const pDataPort )
  1894. {
  1895. DPNHANDLE hDataPort;
  1896. DNASSERT( pDataPort != NULL );
  1897. hDataPort = pDataPort->GetHandle();
  1898. if (SUCCEEDED(m_DataPortHandleTable.Destroy( hDataPort, NULL )))
  1899. {
  1900. pDataPort->SetHandle( 0 );
  1901. pDataPort->DecRef();
  1902. }
  1903. }
  1904. //**********************************************************************
  1905. //**********************************************************************
  1906. // ------------------------------
  1907. // CModemThreadPool::DataPortFromHandle - convert a handle to a CDataPort
  1908. //
  1909. // Entry: Handle
  1910. //
  1911. // Exit: Pointer to CDataPort (may be NULL for invalid handle)
  1912. // ------------------------------
  1913. CDataPort *CModemThreadPool::DataPortFromHandle( const DPNHANDLE hDataPort )
  1914. {
  1915. CDataPort *pDataPort;
  1916. DNASSERT( hDataPort != 0 );
  1917. pDataPort = NULL;
  1918. m_DataPortHandleTable.Lock();
  1919. if (SUCCEEDED(m_DataPortHandleTable.Find( hDataPort, (PVOID*)&pDataPort )))
  1920. {
  1921. pDataPort->AddRef();
  1922. }
  1923. m_DataPortHandleTable.Unlock();
  1924. return pDataPort;
  1925. }
  1926. //**********************************************************************
  1927. //**********************************************************************
  1928. // ------------------------------
  1929. // CModemThreadPool::SubmitDelayedCommand - submit request to enum query to remote session
  1930. //
  1931. // Entry: Pointer to callback function
  1932. // Pointer to cancel function
  1933. // Pointer to callback context
  1934. //
  1935. // Exit: Error code
  1936. // ------------------------------
  1937. #undef DPF_MODNAME
  1938. #define DPF_MODNAME "CModemThreadPool::SubmitDelayedCommand"
  1939. HRESULT CModemThreadPool::SubmitDelayedCommand( JOB_FUNCTION *const pFunction, JOB_FUNCTION *const pCancelFunction, void *const pContext )
  1940. {
  1941. HRESULT hr;
  1942. THREAD_POOL_JOB *pJob;
  1943. BOOL fJobDataLocked;
  1944. DNASSERT( pFunction != NULL );
  1945. DNASSERT( pCancelFunction != NULL );
  1946. //
  1947. // initialize
  1948. //
  1949. hr = DPN_OK;
  1950. pJob = NULL;
  1951. fJobDataLocked = FALSE;
  1952. pJob = static_cast<THREAD_POOL_JOB*>( g_ModemThreadPoolJobPool.Get( ) );
  1953. if ( pJob == NULL )
  1954. {
  1955. hr = DPNERR_OUTOFMEMORY;
  1956. DPFX(DPFPREP, 0, "Cannot allocate job for DelayedCommand!" );
  1957. goto Failure;
  1958. }
  1959. pJob->JobType = JOB_DELAYED_COMMAND;
  1960. pJob->pCancelFunction = pCancelFunction;
  1961. pJob->JobData.JobDelayedCommand.pCommandFunction = pFunction;
  1962. pJob->JobData.JobDelayedCommand.pContext = pContext;
  1963. LockJobData();
  1964. fJobDataLocked = TRUE;
  1965. hr = SubmitWorkItem( pJob );
  1966. if ( hr != DPN_OK )
  1967. {
  1968. DPFX(DPFPREP, 0, "Problem submitting DelayedCommand job!" );
  1969. DisplayDNError( 0, hr );
  1970. goto Failure;
  1971. }
  1972. Exit:
  1973. if ( fJobDataLocked != FALSE )
  1974. {
  1975. UnlockJobData();
  1976. fJobDataLocked = FALSE;
  1977. }
  1978. return hr;
  1979. Failure:
  1980. if ( pJob != NULL )
  1981. {
  1982. g_ModemThreadPoolJobPool.Release( pJob );
  1983. }
  1984. goto Exit;
  1985. }
  1986. //**********************************************************************
  1987. #ifdef WINNT
  1988. //**********************************************************************
  1989. // ------------------------------
  1990. // CModemThreadPool::StartNTTimerThread - start the timer thread for NT
  1991. //
  1992. // Entry: Nothing
  1993. //
  1994. // Exit: Error code
  1995. //
  1996. // Note: This function assumes that the enum data is locked.
  1997. // ------------------------------
  1998. #undef DPF_MODNAME
  1999. #define DPF_MODNAME "CModemThreadPool::StartNTTimerThread"
  2000. HRESULT CModemThreadPool::StartNTTimerThread( void )
  2001. {
  2002. HRESULT hr;
  2003. DNHANDLE hThread;
  2004. DWORD dwThreadID;
  2005. //
  2006. // initialize
  2007. //
  2008. hr = DPN_OK;
  2009. DNASSERT( m_JobQueue.GetPendingJobHandle() != NULL );
  2010. if ( m_fNTTimerThreadRunning != FALSE )
  2011. {
  2012. //
  2013. // the enum thread is already running, poke it to note new enums
  2014. //
  2015. if ( DNSetEvent( m_JobQueue.GetPendingJobHandle() ) == FALSE )
  2016. {
  2017. DWORD dwError;
  2018. hr = DPNERR_OUTOFMEMORY;
  2019. dwError = GetLastError();
  2020. DPFX(DPFPREP, 0, "Problem setting event to wake NTTimerThread!" );
  2021. DisplayErrorCode( 0, dwError );
  2022. goto Failure;
  2023. }
  2024. goto Exit;
  2025. }
  2026. // DNASSERT( m_hWakeNTEnumThread != NULL );
  2027. // m_NTEnumThreadData.hEventList[ EVENT_INDEX_ENUM_WAKEUP ] = m_hWakeNTEnumThread;
  2028. IncrementActiveThreadCount();
  2029. hThread = DNCreateThread( NULL, // pointer to security attributes (none)
  2030. 0, // stack size (default)
  2031. WinNTTimerThread, // thread function
  2032. this, // thread parameter
  2033. 0, // creation flags (none, start running now)
  2034. &dwThreadID // pointer to thread ID
  2035. );
  2036. if ( hThread == NULL )
  2037. {
  2038. DWORD dwError;
  2039. hr = DPNERR_OUTOFMEMORY;
  2040. dwError = GetLastError();
  2041. DPFX(DPFPREP, 0, "Failed to create NT timer thread!" );
  2042. DisplayErrorCode( 0, dwError );
  2043. DecrementActiveThreadCount();
  2044. goto Failure;
  2045. }
  2046. //
  2047. // note that the thread is running and close the handle to the thread
  2048. //
  2049. m_fNTTimerThreadRunning = TRUE;
  2050. DPFX(DPFPREP, 8, "Creating NT-Timer thread: 0x%x\tTotal Thread Count: %d\tNT Completion Thread Count: %d", dwThreadID, ThreadCount(), NTCompletionThreadCount() );
  2051. if ( DNCloseHandle( hThread ) == FALSE )
  2052. {
  2053. DWORD dwError;
  2054. dwError = GetLastError();
  2055. DPFX(DPFPREP, 0, "Problem closing handle after starting NTTimerThread!" );
  2056. DisplayErrorCode( 0, dwError );
  2057. }
  2058. Exit:
  2059. return hr;
  2060. Failure:
  2061. goto Exit;
  2062. }
  2063. //**********************************************************************
  2064. //**********************************************************************
  2065. // ------------------------------
  2066. // CModemThreadPool::WakeNTTimerThread - wake the timer thread because a timed event
  2067. // has been added
  2068. //
  2069. // Entry: Nothing
  2070. //
  2071. // Exit: Nothing
  2072. // ------------------------------
  2073. #undef DPF_MODNAME
  2074. #define DPF_MODNAME "CModemThreadPool::WakeNTTimerThread"
  2075. void CModemThreadPool::WakeNTTimerThread( void )
  2076. {
  2077. LockJobData();
  2078. DNASSERT( m_JobQueue.GetPendingJobHandle() != FALSE );
  2079. if ( DNSetEvent( m_JobQueue.GetPendingJobHandle() ) == FALSE )
  2080. {
  2081. DWORD dwError;
  2082. dwError = GetLastError();
  2083. DPFX(DPFPREP, 0, "Problem setting event to wake up NT timer thread!" );
  2084. DisplayErrorCode( 0, dwError );
  2085. }
  2086. UnlockJobData();
  2087. }
  2088. //**********************************************************************
  2089. #endif // WINNT
  2090. //**********************************************************************
  2091. // ------------------------------
  2092. // CModemThreadPool::RemoveTimerOperationEntry - remove timer operation job from list
  2093. //
  2094. // Entry: Pointer to timer operation
  2095. // Result code to return
  2096. //
  2097. // Exit: Nothing
  2098. //
  2099. // Note: This function assumes that the list is appropriately locked
  2100. // ------------------------------
  2101. #undef DPF_MODNAME
  2102. #define DPF_MODNAME "CModemThreadPool::RemoveTimerOperationEntry"
  2103. void CModemThreadPool::RemoveTimerOperationEntry( TIMER_OPERATION_ENTRY *const pTimerEntry, const HRESULT hJobResult )
  2104. {
  2105. DNASSERT( pTimerEntry != NULL );
  2106. AssertCriticalSectionIsTakenByThisThread( &m_TimerDataLock, TRUE );
  2107. //
  2108. // remove this link from the list, tell owner that the job is complete and
  2109. // return the job to the pool
  2110. //
  2111. pTimerEntry->Linkage.RemoveFromList();
  2112. pTimerEntry->pTimerComplete( hJobResult, pTimerEntry->pContext );
  2113. g_ModemTimerEntryPool.Release( pTimerEntry );
  2114. }
  2115. //**********************************************************************
  2116. #ifdef WIN95
  2117. //**********************************************************************
  2118. // ------------------------------
  2119. // CModemThreadPool::CompleteOutstandingSends - check for completed sends and
  2120. // indicate send completion for them.
  2121. //
  2122. // Entry: Send complete event
  2123. //
  2124. // Exit: Nothing
  2125. // ------------------------------
  2126. #undef DPF_MODNAME
  2127. #define DPF_MODNAME "CModemThreadPool::CompleteOutstandingSends"
  2128. void CModemThreadPool::CompleteOutstandingSends( const DNHANDLE hSendCompleteEvent )
  2129. {
  2130. CBilink *pCurrentOutstandingWrite;
  2131. CBilink WritesToBeProcessed;
  2132. DNASSERT( hSendCompleteEvent != NULL );
  2133. WritesToBeProcessed.Initialize();
  2134. //
  2135. // Loop through the list out outstanding sends. Any completed sends are
  2136. // removed from the list and processed after we release the write data lock.
  2137. //
  2138. LockWriteData();
  2139. pCurrentOutstandingWrite = m_OutstandingWriteList.GetNext();
  2140. while ( pCurrentOutstandingWrite != &m_OutstandingWriteList )
  2141. {
  2142. CDataPort *pDataPort;
  2143. CModemWriteIOData *pWriteIOData;
  2144. DWORD dwFlags;
  2145. DWORD dwBytesSent;
  2146. //
  2147. // note this send and advance pointer to the next pending send
  2148. //
  2149. pWriteIOData = CModemWriteIOData::WriteDataFromBilink( pCurrentOutstandingWrite );
  2150. pCurrentOutstandingWrite = pCurrentOutstandingWrite->GetNext();
  2151. if ( pWriteIOData->Win9xOperationPending() != FALSE )
  2152. {
  2153. if ( GetOverlappedResult( HANDLE_FROM_DNHANDLE(pWriteIOData->DataPort()->GetFileHandle()), // file handle
  2154. pWriteIOData->Overlap(), // pointer to overlap structure
  2155. &dwBytesSent, // pointer to bytes sent
  2156. FALSE // wait for completion (don't wait)
  2157. ) != FALSE )
  2158. {
  2159. pWriteIOData->jkm_hSendResult = DPN_OK;
  2160. }
  2161. else
  2162. {
  2163. DWORD dwError;
  2164. dwError = GetLastError();
  2165. switch( dwError )
  2166. {
  2167. //
  2168. // ERROR_IO_PENDING = operation pending
  2169. //
  2170. case ERROR_IO_PENDING:
  2171. {
  2172. goto SkipSendCompletion;
  2173. break;
  2174. }
  2175. //
  2176. // ERROR_IO_INCOMPLETE = overlapped I/O is not in a signalled state
  2177. // this is expected since the event is always
  2178. // cleared before checking I/O. Assume complete.
  2179. //
  2180. case ERROR_IO_INCOMPLETE:
  2181. {
  2182. pWriteIOData->jkm_hSendResult = DPN_OK;
  2183. break;
  2184. }
  2185. //
  2186. // ERROR_OPERATION_ABORTED = operation complete because of cancel or
  2187. // a thread quit. (Com port was closed)
  2188. //
  2189. case ERROR_OPERATION_ABORTED:
  2190. {
  2191. pWriteIOData->jkm_hSendResult = DPNERR_USERCANCEL;
  2192. break;
  2193. }
  2194. //
  2195. // ERROR_INVALID_HANDLE = the serial port is gone. This is
  2196. // possible when a modem hangs up.
  2197. //
  2198. case ERROR_INVALID_HANDLE:
  2199. {
  2200. pWriteIOData->jkm_hSendResult = DPNERR_NOCONNECTION;
  2201. break;
  2202. }
  2203. //
  2204. // other error, stop and take a look
  2205. //
  2206. default:
  2207. {
  2208. pWriteIOData->jkm_hSendResult = DPNERR_GENERIC;
  2209. DisplayErrorCode( 0, dwError );
  2210. DNASSERT( FALSE );
  2211. break;
  2212. }
  2213. }
  2214. }
  2215. DNASSERT( pWriteIOData->Win9xOperationPending() != FALSE );
  2216. pWriteIOData->SetWin9xOperationPending( FALSE );
  2217. pWriteIOData->m_OutstandingWriteListLinkage.RemoveFromList();
  2218. pWriteIOData->m_OutstandingWriteListLinkage.InsertBefore( &WritesToBeProcessed );
  2219. }
  2220. SkipSendCompletion:
  2221. //
  2222. // the following line is present to prevent the compiler from whining
  2223. // about a blank line
  2224. //
  2225. ;
  2226. }
  2227. //
  2228. // If there are no more outstanding reads, reset the write complete event.
  2229. // It will be signalled when the next posted write completes. No other read
  2230. // can be posted at this time because the write data lock is held.
  2231. //
  2232. if ( m_OutstandingWriteList.IsEmpty() != FALSE )
  2233. {
  2234. if ( DNResetEvent( hSendCompleteEvent ) == FALSE )
  2235. {
  2236. DWORD dwError;
  2237. dwError = GetLastError();
  2238. DPFX(DPFPREP, 0, "Failed to reset send event!" );
  2239. DisplayErrorCode( 0, dwError );
  2240. }
  2241. }
  2242. UnlockWriteData();
  2243. //
  2244. // process all writes that have been pulled to the side.
  2245. //
  2246. while ( WritesToBeProcessed.GetNext() != &WritesToBeProcessed )
  2247. {
  2248. CModemWriteIOData *pTempWrite;
  2249. CDataPort *pDataPort;
  2250. pTempWrite = CModemWriteIOData::WriteDataFromBilink( WritesToBeProcessed.GetNext() );
  2251. pTempWrite->m_OutstandingWriteListLinkage.RemoveFromList();
  2252. pDataPort = pTempWrite->DataPort();
  2253. DNASSERT( pDataPort != NULL );
  2254. pDataPort->SendComplete( pTempWrite, pTempWrite->jkm_hSendResult );
  2255. // pDataPort->SendFromWriteQueue();
  2256. // pDataPort->DecRef();
  2257. }
  2258. }
  2259. //**********************************************************************
  2260. #endif // WIN95
  2261. #ifdef WIN95
  2262. //**********************************************************************
  2263. // ------------------------------
  2264. // CModemThreadPool::CompleteOutstandingReceives - check for completed receives and
  2265. // indicate completion for them.
  2266. //
  2267. // Entry: Receive complete event
  2268. //
  2269. // Exit: Nothing
  2270. // ------------------------------
  2271. #undef DPF_MODNAME
  2272. #define DPF_MODNAME "CModemThreadPool::CompleteOutstandingReceives"
  2273. void CModemThreadPool::CompleteOutstandingReceives( const DNHANDLE hReceiveCompleteEvent )
  2274. {
  2275. CBilink *pCurrentOutstandingRead;
  2276. CBilink ReadsToBeProcessed;
  2277. DNASSERT( hReceiveCompleteEvent != NULL );
  2278. ReadsToBeProcessed.Initialize();
  2279. LockReadData();
  2280. //
  2281. // Loop through the list of outstanding reads and pull out the ones that need
  2282. // to be serviced. We don't want to service them while the read data lock
  2283. // is taken.
  2284. //
  2285. pCurrentOutstandingRead = m_OutstandingReadList.GetNext();
  2286. while ( pCurrentOutstandingRead != &m_OutstandingReadList )
  2287. {
  2288. CDataPort *pDataPort;
  2289. CModemReadIOData *pReadIOData;
  2290. DWORD dwFlags;
  2291. pReadIOData = CModemReadIOData::ReadDataFromBilink( pCurrentOutstandingRead );
  2292. pCurrentOutstandingRead = pCurrentOutstandingRead->GetNext();
  2293. //
  2294. // Make sure this operation is really pending before attempting to check
  2295. // for completion. It's possible that the read was added to the list, but
  2296. // we haven't actually called Winsock yet.
  2297. //
  2298. if ( pReadIOData->Win9xOperationPending() != FALSE )
  2299. {
  2300. if ( GetOverlappedResult( HANDLE_FROM_DNHANDLE(pReadIOData->DataPort()->GetFileHandle()),
  2301. pReadIOData->Overlap(),
  2302. &pReadIOData->jkm_dwOverlappedBytesReceived,
  2303. FALSE
  2304. ) != FALSE )
  2305. {
  2306. DBG_CASSERT( ERROR_SUCCESS == 0 );
  2307. // pReadIOData->jkm_hReceiveReturn = DPN_OK;
  2308. pReadIOData->m_dwWin9xReceiveErrorReturn = ERROR_SUCCESS;
  2309. }
  2310. else
  2311. {
  2312. DWORD dwError;
  2313. dwError = GetLastError();
  2314. switch( dwError )
  2315. {
  2316. //
  2317. // ERROR_IO_INCOMPLETE = treat as I/O complete. Event isn't
  2318. // signalled, but that's expected because
  2319. // it's cleared before checking for I/O
  2320. //
  2321. case ERROR_IO_INCOMPLETE:
  2322. {
  2323. pReadIOData->jkm_dwOverlappedBytesReceived = pReadIOData->m_dwBytesToRead;
  2324. pReadIOData->m_dwWin9xReceiveErrorReturn = ERROR_SUCCESS;
  2325. break;
  2326. }
  2327. //
  2328. // ERROR_IO_PENDING = io still pending
  2329. //
  2330. case ERROR_IO_PENDING:
  2331. {
  2332. DNASSERT( FALSE );
  2333. goto SkipReceiveCompletion;
  2334. break;
  2335. }
  2336. //
  2337. // other error, stop if not 'known'
  2338. //
  2339. default:
  2340. {
  2341. switch ( dwError )
  2342. {
  2343. //
  2344. // ERROR_OPERATION_ABORTED = operation was cancelled (COM port closed)
  2345. // ERROR_INVALID_HANDLE = operation was cancelled (COM port closed)
  2346. //
  2347. case ERROR_OPERATION_ABORTED:
  2348. case ERROR_INVALID_HANDLE:
  2349. {
  2350. break;
  2351. }
  2352. default:
  2353. {
  2354. DisplayErrorCode( 0, dwError );
  2355. DNASSERT( FALSE );
  2356. break;
  2357. }
  2358. }
  2359. pReadIOData->m_dwWin9xReceiveErrorReturn = dwError;
  2360. break;
  2361. }
  2362. }
  2363. }
  2364. DNASSERT( pReadIOData->Win9xOperationPending() != FALSE );
  2365. pReadIOData->SetWin9xOperationPending( FALSE );
  2366. pReadIOData->m_OutstandingReadListLinkage.RemoveFromList();
  2367. pReadIOData->m_OutstandingReadListLinkage.InsertBefore( &ReadsToBeProcessed );
  2368. }
  2369. SkipReceiveCompletion:
  2370. //
  2371. // the following line is present to prevent the compiler from whining
  2372. // about a blank line
  2373. //
  2374. ;
  2375. }
  2376. //
  2377. // If there are no more outstanding reads, reset the read complete event.
  2378. // It will be signalled when the next posted read completes. No other read
  2379. // can be posted at this time because the read data lock is held.
  2380. //
  2381. if ( m_OutstandingReadList.IsEmpty() != FALSE )
  2382. {
  2383. if ( DNResetEvent( hReceiveCompleteEvent ) == FALSE )
  2384. {
  2385. DWORD dwError;
  2386. dwError = GetLastError();
  2387. DPFX(DPFPREP, 0, "Failed to reset receive event!" );
  2388. DisplayErrorCode( 0, dwError );
  2389. }
  2390. }
  2391. UnlockReadData();
  2392. //
  2393. // loop through the list of reads that have completed and dispatch them
  2394. //
  2395. while ( ReadsToBeProcessed.GetNext() != &ReadsToBeProcessed )
  2396. {
  2397. CModemReadIOData *pTempRead;
  2398. CDataPort *pDataPort;
  2399. pTempRead = CModemReadIOData::ReadDataFromBilink( ReadsToBeProcessed.GetNext() );
  2400. pTempRead->m_OutstandingReadListLinkage.RemoveFromList();
  2401. pDataPort = pTempRead->DataPort();
  2402. DNASSERT( pDataPort != NULL );
  2403. pDataPort->ProcessReceivedData( pTempRead->jkm_dwOverlappedBytesReceived, pTempRead->m_dwWin9xReceiveErrorReturn );
  2404. }
  2405. }
  2406. //**********************************************************************
  2407. #endif // WIN95
  2408. #ifdef WIN95
  2409. //**********************************************************************
  2410. // ------------------------------
  2411. // CModemThreadPool::PrimaryWin9xThread - main thread to do everything that the SP is
  2412. // supposed to do under Win9x.
  2413. //
  2414. // Entry: Pointer to startup parameter
  2415. //
  2416. // Exit: Error Code
  2417. //
  2418. // Note: The startup parameter is allocated for this thread and must be
  2419. // deallocated by this thread when it exits
  2420. // ------------------------------
  2421. #undef DPF_MODNAME
  2422. #define DPF_MODNAME "CModemThreadPool::PrimaryWin9xThread"
  2423. DWORD WINAPI CModemThreadPool::PrimaryWin9xThread( void *pParam )
  2424. {
  2425. WIN9X_CORE_DATA CoreData;
  2426. DWORD dwCurrentTime;
  2427. DWORD dwMaxWaitTime;
  2428. BOOL fComInitialized;
  2429. CModemThreadPool *const pThisThreadPool = static_cast<WIN9X_THREAD_DATA *>( pParam )->pThisThreadPool;
  2430. DNASSERT( pParam != NULL );
  2431. DNASSERT( pThisThreadPool != NULL );
  2432. //
  2433. // initialize
  2434. //
  2435. memset( &CoreData, 0x00, sizeof CoreData );
  2436. fComInitialized = FALSE;
  2437. //
  2438. // before we do anything we need to make sure COM is happy
  2439. //
  2440. switch ( COM_CoInitialize( NULL ) )
  2441. {
  2442. //
  2443. // no problem
  2444. //
  2445. case S_OK:
  2446. {
  2447. fComInitialized = TRUE;
  2448. break;
  2449. }
  2450. //
  2451. // COM already initialized, huh?
  2452. //
  2453. case S_FALSE:
  2454. {
  2455. DNASSERT( FALSE );
  2456. break;
  2457. }
  2458. //
  2459. // COM init failed!
  2460. //
  2461. default:
  2462. {
  2463. DPFX(DPFPREP, 0, "Primary Win9x thread failed to initialize COM!" );
  2464. DNASSERT( FALSE );
  2465. break;
  2466. }
  2467. }
  2468. DNASSERT( CoreData.fTimerJobsActive == FALSE );
  2469. //
  2470. // set enums to happen infinitely in the future
  2471. //
  2472. CoreData.dwNextTimerJobTime = GETTIMESTAMP() - 1;
  2473. //
  2474. // set wait handles
  2475. //
  2476. CoreData.hWaitHandles[ EVENT_INDEX_STOP_ALL_THREADS ] = pThisThreadPool->m_hStopAllThreads;
  2477. CoreData.hWaitHandles[ EVENT_INDEX_PENDING_JOB ] = pThisThreadPool->m_JobQueue.GetPendingJobHandle();
  2478. CoreData.hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ] = pThisThreadPool->GetSendCompleteEvent();
  2479. CoreData.hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ] = pThisThreadPool->GetReceiveCompleteEvent();
  2480. CoreData.hWaitHandles[ EVENT_INDEX_TAPI_MESSAGE ] = pThisThreadPool->GetTAPIMessageEvent();
  2481. DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_STOP_ALL_THREADS ] != NULL );
  2482. DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_PENDING_JOB ] != NULL );
  2483. DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ] != NULL );
  2484. DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ] != NULL );
  2485. DNASSERT( CoreData.hWaitHandles[ EVENT_INDEX_TAPI_MESSAGE ] );
  2486. //
  2487. // go until we're told to stop
  2488. //
  2489. CoreData.fLooping = TRUE;
  2490. while ( CoreData.fLooping != FALSE )
  2491. {
  2492. DWORD dwWaitReturn;
  2493. //
  2494. // Update the job time so we know how long to wait. We can
  2495. // only get here if a socket was just added to the socket list, or
  2496. // we've been servicing sockets.
  2497. //
  2498. dwCurrentTime = GETTIMESTAMP();
  2499. if ( (int) ( dwCurrentTime - CoreData.dwNextTimerJobTime ) >= 0 )
  2500. {
  2501. pThisThreadPool->LockTimerData();
  2502. CoreData.fTimerJobsActive = pThisThreadPool->ProcessTimerJobs( &pThisThreadPool->m_TimerJobList,
  2503. &CoreData.dwNextTimerJobTime );
  2504. if ( CoreData.fTimerJobsActive != FALSE )
  2505. {
  2506. DPFX(DPFPREP, 8, "There are active jobs left with Winsock1 sockets active!" );
  2507. }
  2508. pThisThreadPool->UnlockTimerData();
  2509. }
  2510. dwMaxWaitTime = CoreData.dwNextTimerJobTime - dwCurrentTime;
  2511. //
  2512. // Check for I/O
  2513. //
  2514. dwWaitReturn = DNWaitForMultipleObjectsEx( LENGTHOF( CoreData.hWaitHandles ), // count of handles
  2515. CoreData.hWaitHandles, // handles to wait on
  2516. FALSE, // don't wait for all to be signalled
  2517. dwMaxWaitTime, // wait timeout
  2518. TRUE // we're alertable for APCs
  2519. );
  2520. switch ( dwWaitReturn )
  2521. {
  2522. //
  2523. // timeout, don't do anything, we'll probably process timer jobs on
  2524. // the next loop
  2525. //
  2526. case WAIT_TIMEOUT:
  2527. {
  2528. break;
  2529. }
  2530. case ( WAIT_OBJECT_0 + EVENT_INDEX_PENDING_JOB ):
  2531. case ( WAIT_OBJECT_0 + EVENT_INDEX_STOP_ALL_THREADS ):
  2532. case ( WAIT_OBJECT_0 + EVENT_INDEX_SEND_COMPLETE ):
  2533. case ( WAIT_OBJECT_0 + EVENT_INDEX_RECEIVE_COMPLETE ):
  2534. case ( WAIT_OBJECT_0 + EVENT_INDEX_TAPI_MESSAGE ):
  2535. {
  2536. pThisThreadPool->ProcessWin9xEvents( &CoreData );
  2537. break;
  2538. }
  2539. //
  2540. // There are I/O completion routines scheduled on this thread.
  2541. // This is not a good thing!
  2542. //
  2543. case WAIT_IO_COMPLETION:
  2544. {
  2545. DPFX(DPFPREP, 1, "WARNING: APC was serviced on the primary Win9x IO service thread! What is the application doing??" );
  2546. break;
  2547. }
  2548. //
  2549. // wait failed
  2550. //
  2551. case WAIT_FAILED:
  2552. {
  2553. DWORD dwError;
  2554. dwError = GetLastError();
  2555. DPFX(DPFPREP, 0, "Primary Win9x thread wait failed!" );
  2556. DisplayDNError( 0, dwError );
  2557. break;
  2558. }
  2559. //
  2560. // problem
  2561. //
  2562. default:
  2563. {
  2564. DWORD dwError;
  2565. dwError = GetLastError();
  2566. DPFX(DPFPREP, 0, "Primary Win9x thread unknown problem in wait!" );
  2567. DisplayDNError( 0, dwError );
  2568. DNASSERT( FALSE );
  2569. break;
  2570. }
  2571. }
  2572. }
  2573. pThisThreadPool->DecrementActiveThreadCount();
  2574. DNFree( pParam );
  2575. if ( fComInitialized != FALSE )
  2576. {
  2577. COM_CoUninitialize();
  2578. fComInitialized = FALSE;
  2579. }
  2580. return 0;
  2581. }
  2582. //**********************************************************************
  2583. #endif // WIN95
  2584. #ifdef WINNT
  2585. //**********************************************************************
  2586. // ------------------------------
  2587. // CModemThreadPool::WinNTIOCompletionThread - thread to service I/O completion port
  2588. //
  2589. // Entry: Pointer to startup parameter
  2590. //
  2591. // Exit: Error Code
  2592. //
  2593. // Note: The startup parameter is allocated for this thread and must be
  2594. // deallocated by this thread when it exits
  2595. // ------------------------------
  2596. #undef DPF_MODNAME
  2597. #define DPF_MODNAME "CModemThreadPool::WinNTIOCompletionThread"
  2598. DWORD WINAPI CModemThreadPool::WinNTIOCompletionThread( void *pParam )
  2599. {
  2600. IOCOMPLETION_THREAD_DATA *pInput;
  2601. BOOL fLooping;
  2602. HANDLE hIOCompletionPort;
  2603. BOOL fComInitialized;
  2604. DNASSERT( pParam != NULL );
  2605. //
  2606. // initialize
  2607. //
  2608. pInput = static_cast<IOCOMPLETION_THREAD_DATA*>( pParam );
  2609. DNASSERT( pInput->pThisThreadPool != NULL );
  2610. fLooping = TRUE;
  2611. hIOCompletionPort = pInput->pThisThreadPool->m_hIOCompletionPort;
  2612. DNASSERT( hIOCompletionPort != NULL );
  2613. fComInitialized = FALSE;
  2614. //
  2615. // before we do anything we need to make sure COM is happy
  2616. //
  2617. switch ( COM_CoInitialize( NULL ) )
  2618. {
  2619. //
  2620. // no problem
  2621. //
  2622. case S_OK:
  2623. {
  2624. fComInitialized = TRUE;
  2625. break;
  2626. }
  2627. //
  2628. // COM already initialized, huh?
  2629. //
  2630. case S_FALSE:
  2631. {
  2632. DNASSERT( FALSE );
  2633. break;
  2634. }
  2635. //
  2636. // COM init failed!
  2637. //
  2638. default:
  2639. {
  2640. DNASSERT( FALSE );
  2641. DPFX(DPFPREP, 0, "Failed to initialize COM!" );
  2642. break;
  2643. }
  2644. }
  2645. //
  2646. // go until we're told to stop
  2647. //
  2648. while ( fLooping != FALSE )
  2649. {
  2650. BOOL fStatusReturn;
  2651. DWORD dwBytesTransferred;
  2652. ULONG_PTR uCompletionKey;
  2653. OVERLAPPED *pOverlapped;
  2654. DNASSERT( hIOCompletionPort != NULL );
  2655. fStatusReturn = GetQueuedCompletionStatus( hIOCompletionPort, // handle of completion port
  2656. &dwBytesTransferred, // pointer to number of bytes read
  2657. &uCompletionKey, // pointer to completion key
  2658. &pOverlapped, // pointer to overlapped structure
  2659. INFINITE // wait forever
  2660. );
  2661. if ( ( fStatusReturn == FALSE ) && ( pOverlapped == FALSE ) )
  2662. {
  2663. DWORD dwError;
  2664. dwError = GetLastError();
  2665. DPFX(DPFPREP, 0, "Problem getting item from completion port!" );
  2666. DisplayErrorCode( 0, dwError );
  2667. }
  2668. else
  2669. {
  2670. switch ( uCompletionKey )
  2671. {
  2672. //
  2673. // ReadFile or WriteFile completed. Check error status and
  2674. // complete the appropriate operation.
  2675. //
  2676. case IO_COMPLETION_KEY_IO_COMPLETE:
  2677. {
  2678. CIOData *pIOData;
  2679. DWORD dwError;
  2680. DNASSERT( pOverlapped != NULL );
  2681. if ( fStatusReturn != FALSE )
  2682. {
  2683. dwError = ERROR_SUCCESS;
  2684. }
  2685. else
  2686. {
  2687. dwError = GetLastError();
  2688. }
  2689. pIOData = CIOData::IODataFromOverlap( pOverlapped );
  2690. if ( pIOData->NTIOOperationType() == NT_IO_OPERATION_RECEIVE )
  2691. {
  2692. DNASSERT( ( dwError == ERROR_SUCCESS ) || ( dwBytesTransferred == 0 ) );
  2693. pIOData->DataPort()->ProcessReceivedData( dwBytesTransferred, dwError );
  2694. }
  2695. else
  2696. {
  2697. HRESULT hOperationResult;
  2698. DNASSERT( pIOData->NTIOOperationType() == NT_IO_OPERATION_SEND );
  2699. switch ( dwError )
  2700. {
  2701. //
  2702. // no problem
  2703. //
  2704. case ERROR_SUCCESS:
  2705. {
  2706. hOperationResult = DPN_OK;
  2707. break;
  2708. }
  2709. //
  2710. // ERROR_OPERATION_ABORTED = operation was stopped, most likely
  2711. // because of a user cancel
  2712. //
  2713. case ERROR_OPERATION_ABORTED:
  2714. {
  2715. hOperationResult = DPNERR_USERCANCEL;
  2716. break;
  2717. }
  2718. default:
  2719. {
  2720. DNASSERT( FALSE );
  2721. DPFX(DPFPREP, 0, "Failed on I/O completion send!" );
  2722. DisplayErrorCode( 0, dwError );
  2723. hOperationResult = DPNERR_GENERIC;
  2724. break;
  2725. }
  2726. }
  2727. pIOData->DataPort()->SendComplete( static_cast<CModemWriteIOData*>( pIOData ), hOperationResult );
  2728. }
  2729. break;
  2730. }
  2731. //
  2732. // SP is closing, stop all threads
  2733. //
  2734. case IO_COMPLETION_KEY_SP_CLOSE:
  2735. {
  2736. DNASSERT( DNWaitForSingleObjectEx( pInput->pThisThreadPool->m_hStopAllThreads, 0, TRUE ) == WAIT_OBJECT_0 );
  2737. fLooping = FALSE;
  2738. break;
  2739. }
  2740. //
  2741. // a new job was submitted to the job queue, or the SP is closing from above
  2742. //
  2743. case IO_COMPLETION_KEY_NEW_JOB:
  2744. {
  2745. THREAD_POOL_JOB *pJobInfo;
  2746. //
  2747. // SP is still running, process our job
  2748. //
  2749. pJobInfo = pInput->pThisThreadPool->GetWorkItem();
  2750. if ( pJobInfo != NULL )
  2751. {
  2752. switch ( pJobInfo->JobType )
  2753. {
  2754. //
  2755. // enum refresh
  2756. //
  2757. case JOB_REFRESH_TIMER_JOBS:
  2758. {
  2759. DPFX(DPFPREP, 8, "IOCompletion job REFRESH_TIMER_JOBS" );
  2760. DNASSERT( pJobInfo->JobData.JobRefreshTimedJobs.uDummy == 0 );
  2761. pInput->pThisThreadPool->WakeNTTimerThread();
  2762. break;
  2763. }
  2764. //
  2765. // issue callback for this job
  2766. //
  2767. case JOB_DELAYED_COMMAND:
  2768. {
  2769. DPFX(DPFPREP, 8, "IOCompletion job DELAYED_COMMAND" );
  2770. DNASSERT( pJobInfo->JobData.JobDelayedCommand.pCommandFunction != NULL );
  2771. pJobInfo->JobData.JobDelayedCommand.pCommandFunction( pJobInfo );
  2772. break;
  2773. }
  2774. //
  2775. // other job
  2776. //
  2777. default:
  2778. {
  2779. DPFX(DPFPREP, 0, "IOCompletion job unknown!" );
  2780. DNASSERT( FALSE );
  2781. break;
  2782. }
  2783. }
  2784. pJobInfo->JobType = JOB_UNINITIALIZED;
  2785. g_ModemThreadPoolJobPool.Release( pJobInfo );
  2786. }
  2787. break;
  2788. }
  2789. //
  2790. // TAPI message, pointer to line message is in pOverlapped and
  2791. // we are responsible for freeing it via LocalFree()
  2792. //
  2793. case IO_COMPLETION_KEY_TAPI_MESSAGE:
  2794. {
  2795. LINEMESSAGE *pLineMessage;
  2796. CDataPort *pModemPort;
  2797. DPNHANDLE hModemPort;
  2798. DNASSERT( pOverlapped != NULL );
  2799. DBG_CASSERT( sizeof( pLineMessage ) == sizeof( pOverlapped ) );
  2800. pLineMessage = reinterpret_cast<LINEMESSAGE*>( pOverlapped );
  2801. DBG_CASSERT( sizeof( pModemPort ) == sizeof( pLineMessage->dwCallbackInstance ) );
  2802. hModemPort = (DPNHANDLE)( pLineMessage->dwCallbackInstance );
  2803. pModemPort = static_cast<CDataPort*>( pInput->pThisThreadPool->DataPortFromHandle( hModemPort ) );
  2804. if ( pModemPort != NULL )
  2805. {
  2806. pModemPort->ProcessTAPIMessage( pLineMessage );
  2807. if ( LocalFree( pOverlapped ) != NULL )
  2808. {
  2809. DWORD dwError;
  2810. dwError = GetLastError();
  2811. DPFX(DPFPREP, 0, "Problem with LocalFree in NTProcessTAPIMessage" );
  2812. DisplayErrorCode( 0, dwError );
  2813. }
  2814. pModemPort->DecRef();
  2815. }
  2816. else
  2817. {
  2818. DPFX(DPFPREP, 5, "Dropping TAPI message on closed modem port!" );
  2819. DisplayTAPIMessage( 5, pLineMessage );
  2820. }
  2821. break;
  2822. }
  2823. //
  2824. // unknown I/O completion message type
  2825. //
  2826. default:
  2827. {
  2828. DNASSERT( FALSE );
  2829. break;
  2830. }
  2831. }
  2832. }
  2833. }
  2834. pInput->pThisThreadPool->DecrementActiveNTCompletionThreadCount();
  2835. DNFree( pInput );
  2836. if ( fComInitialized != FALSE )
  2837. {
  2838. COM_CoUninitialize();
  2839. fComInitialized = FALSE;
  2840. }
  2841. return 0;
  2842. }
  2843. //**********************************************************************
  2844. #endif // WINNT
  2845. #ifdef WINNT
  2846. //**********************************************************************
  2847. // ------------------------------
  2848. // CModemThreadPool::WinNTTimerThread - timer thread for NT
  2849. //
  2850. // Entry: Pointer to startup parameter
  2851. //
  2852. // Exit: Error Code
  2853. //
  2854. // Note: The startup parameter is a static memory chunk and cannot be freed.
  2855. // Cleanup of this memory is the responsibility of this thread.
  2856. // ------------------------------
  2857. #undef DPF_MODNAME
  2858. #define DPF_MODNAME "CModemThreadPool::WinNTTimerThread"
  2859. DWORD WINAPI CModemThreadPool::WinNTTimerThread( void *pParam )
  2860. {
  2861. CModemThreadPool *pThisThreadPool;
  2862. BOOL fLooping;
  2863. DWORD dwWaitReturn;
  2864. DWORD dwNextEnumTime;
  2865. DNHANDLE hEvents[ 2 ];
  2866. BOOL fComInitialized;
  2867. DNASSERT( pParam != NULL );
  2868. //
  2869. // initialize
  2870. //
  2871. DNASSERT( pParam != NULL );
  2872. pThisThreadPool = static_cast<CModemThreadPool*>( pParam );
  2873. DNASSERT( pThisThreadPool->m_JobQueue.GetPendingJobHandle() != NULL );
  2874. dwNextEnumTime = GETTIMESTAMP() - 1;
  2875. hEvents[ EVENT_INDEX_STOP_ALL_THREADS ] = pThisThreadPool->m_hStopAllThreads;
  2876. hEvents[ EVENT_INDEX_WAKE_NT_TIMER_THREAD ] = pThisThreadPool->m_JobQueue.GetPendingJobHandle();
  2877. fComInitialized = FALSE;
  2878. //
  2879. // before we do anything we need to make sure COM is happy
  2880. //
  2881. switch ( COM_CoInitialize( NULL ) )
  2882. {
  2883. //
  2884. // no problem
  2885. //
  2886. case S_OK:
  2887. {
  2888. fComInitialized = TRUE;
  2889. break;
  2890. }
  2891. //
  2892. // COM already initialized, huh?
  2893. //
  2894. case S_FALSE:
  2895. {
  2896. DNASSERT( FALSE );
  2897. fComInitialized = TRUE;
  2898. break;
  2899. }
  2900. //
  2901. // COM init failed!
  2902. //
  2903. default:
  2904. {
  2905. DNASSERT( FALSE );
  2906. DPFX(DPFPREP, 0, "Failed to initialize COM!" );
  2907. break;
  2908. }
  2909. }
  2910. //
  2911. // there were no active enums so we want to wait forever for something to
  2912. // happen
  2913. //
  2914. fLooping = TRUE;
  2915. //
  2916. // go until we're told to stop
  2917. //
  2918. while ( fLooping != FALSE )
  2919. {
  2920. DWORD dwCurrentTime;
  2921. DWORD dwMaxWaitTime;
  2922. dwCurrentTime = GETTIMESTAMP();
  2923. if ( (int) ( dwNextEnumTime - dwCurrentTime ) <= 0 )
  2924. {
  2925. //
  2926. // acknowledge that we've handled this event and then process the
  2927. // enums
  2928. //
  2929. pThisThreadPool->LockTimerData();
  2930. if ( DNResetEvent( hEvents[ EVENT_INDEX_WAKE_NT_TIMER_THREAD ] ) == FALSE )
  2931. {
  2932. DWORD dwError;
  2933. dwError = GetLastError();
  2934. DPFX(DPFPREP, 0, "Problem resetting event to wake NT timer thread!" );
  2935. DisplayErrorCode( 0, dwError );
  2936. }
  2937. pThisThreadPool->ProcessTimerJobs( &pThisThreadPool->m_TimerJobList, &dwNextEnumTime );
  2938. pThisThreadPool->UnlockTimerData();
  2939. }
  2940. dwMaxWaitTime = dwNextEnumTime - dwCurrentTime;
  2941. DPFX(DPFPREP, 9, "Waiting %u ms until next timed job.", dwMaxWaitTime);
  2942. dwWaitReturn = DNWaitForMultipleObjectsEx( LENGTHOF( hEvents ), // number of events
  2943. hEvents, // event list
  2944. FALSE, // wait for any one event to be signalled
  2945. dwMaxWaitTime, // timeout
  2946. TRUE // be nice and allow APCs
  2947. );
  2948. switch ( dwWaitReturn )
  2949. {
  2950. //
  2951. // SP closing
  2952. //
  2953. case ( WAIT_OBJECT_0 + EVENT_INDEX_STOP_ALL_THREADS ):
  2954. {
  2955. DPFX(DPFPREP, 8, "NT timer thread thread detected SPClose!" );
  2956. fLooping = FALSE;
  2957. break;
  2958. }
  2959. //
  2960. // Enum wakeup event, someone added an enum to the list. Clear
  2961. // our enum time and go back to the top of the loop where we
  2962. // will process enums.
  2963. //
  2964. case ( WAIT_OBJECT_0 + EVENT_INDEX_WAKE_NT_TIMER_THREAD ):
  2965. {
  2966. dwNextEnumTime = GETTIMESTAMP();
  2967. break;
  2968. }
  2969. //
  2970. // Wait timeout. We're probably going to process enums, go back
  2971. // to the top of the loop.
  2972. //
  2973. case WAIT_TIMEOUT:
  2974. {
  2975. break;
  2976. }
  2977. //
  2978. // wait failed
  2979. //
  2980. case WAIT_FAILED:
  2981. {
  2982. DPFX(DPFPREP, 0, "NT timer thread WaitForMultipleObjects failed: 0x%x", dwWaitReturn );
  2983. DNASSERT( FALSE );
  2984. break;
  2985. }
  2986. //
  2987. // problem
  2988. //
  2989. default:
  2990. {
  2991. DNASSERT( FALSE );
  2992. break;
  2993. }
  2994. }
  2995. }
  2996. DPFX(DPFPREP, 8, "NT timer thread is exiting!" );
  2997. pThisThreadPool->LockTimerData();
  2998. pThisThreadPool->m_fNTTimerThreadRunning = FALSE;
  2999. pThisThreadPool->DecrementActiveThreadCount();
  3000. pThisThreadPool->UnlockTimerData();
  3001. if ( fComInitialized != FALSE )
  3002. {
  3003. COM_CoUninitialize();
  3004. fComInitialized = FALSE;
  3005. }
  3006. return 0;
  3007. }
  3008. //**********************************************************************
  3009. #endif // WINNT
  3010. //**********************************************************************
  3011. // ------------------------------
  3012. // CModemThreadPool::DialogThreadProc - thread proc for spawning dialogs
  3013. //
  3014. // Entry: Pointer to startup parameter
  3015. //
  3016. // Exit: Error Code
  3017. // ------------------------------
  3018. #undef DPF_MODNAME
  3019. #define DPF_MODNAME "CModemThreadPool::DialogThreadProc"
  3020. DWORD WINAPI CModemThreadPool::DialogThreadProc( void *pParam )
  3021. {
  3022. const DIALOG_THREAD_PARAM *pThreadParam;
  3023. BOOL fComInitialized;
  3024. //
  3025. // Initialize COM. If this fails, we'll have problems later.
  3026. //
  3027. fComInitialized = FALSE;
  3028. switch ( COM_CoInitialize( NULL ) )
  3029. {
  3030. case S_OK:
  3031. {
  3032. fComInitialized = TRUE;
  3033. break;
  3034. }
  3035. case S_FALSE:
  3036. {
  3037. DNASSERT( FALSE );
  3038. break;
  3039. }
  3040. //
  3041. // COM init failed!
  3042. //
  3043. default:
  3044. {
  3045. DPFX(DPFPREP, 0, "Failed to initialize COM!" );
  3046. DNASSERT( FALSE );
  3047. break;
  3048. }
  3049. }
  3050. DNASSERT( pParam != NULL );
  3051. pThreadParam = static_cast<DIALOG_THREAD_PARAM*>( pParam );
  3052. pThreadParam->pDialogFunction( pThreadParam->pContext );
  3053. pThreadParam->pThisThreadPool->DecrementActiveThreadCount();
  3054. DNFree( pParam );
  3055. if ( fComInitialized != FALSE )
  3056. {
  3057. COM_CoUninitialize();
  3058. fComInitialized = FALSE;
  3059. }
  3060. return 0;
  3061. }
  3062. //**********************************************************************
  3063. #ifdef WIN95
  3064. //**********************************************************************
  3065. // ------------------------------
  3066. // CModemThreadPool::ProcessWin9xEvents - process a Win9x events
  3067. //
  3068. // Entry: Pointer core data
  3069. //
  3070. // Exit: Nothing
  3071. // ------------------------------
  3072. #undef DPF_MODNAME
  3073. #define DPF_MODNAME "CModemThreadPool::ProcessWin9xEvents"
  3074. void CModemThreadPool::ProcessWin9xEvents( WIN9X_CORE_DATA *const pCoreData )
  3075. {
  3076. BOOL fAllIOComplete;
  3077. DNASSERT( pCoreData != NULL );
  3078. //
  3079. // this funciton checks each of the handles to see if they're signalled
  3080. // to prevent I/O from starving the rest of the handles
  3081. //
  3082. fAllIOComplete = TRUE;
  3083. //
  3084. // New job. Account for the time spent in the wait. Don't
  3085. // account for time after the job is complete because it's
  3086. // possible that the job was an job submission which will want
  3087. // to reset the wait time.
  3088. //
  3089. switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_PENDING_JOB ], 0 ) )
  3090. {
  3091. case WAIT_OBJECT_0:
  3092. {
  3093. DPFX(DPFPREP, 8, "Primary Win9x thread has a pending job!" );
  3094. ProcessWin9xJob( pCoreData );
  3095. break;
  3096. }
  3097. case WAIT_TIMEOUT:
  3098. {
  3099. break;
  3100. }
  3101. default:
  3102. {
  3103. DNASSERT( FALSE );
  3104. break;
  3105. }
  3106. }
  3107. //
  3108. // TAPI message
  3109. //
  3110. switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_TAPI_MESSAGE ], 0 ) )
  3111. {
  3112. case WAIT_OBJECT_0:
  3113. {
  3114. DPFX(DPFPREP, 8, "Processing TAPI event!" );
  3115. ProcessTapiEvent();
  3116. break;
  3117. }
  3118. case WAIT_TIMEOUT:
  3119. {
  3120. break;
  3121. }
  3122. default:
  3123. {
  3124. DNASSERT( FALSE );
  3125. break;
  3126. }
  3127. }
  3128. //
  3129. // send complete
  3130. //
  3131. switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ], 0 ) )
  3132. {
  3133. case WAIT_OBJECT_0:
  3134. {
  3135. // DPFX(DPFPREP, 0, "\n\n\nPrimary Win9x thread servicing sends!\n\n\n" );
  3136. CompleteOutstandingSends( pCoreData->hWaitHandles[ EVENT_INDEX_SEND_COMPLETE ] );
  3137. break;
  3138. }
  3139. case WAIT_TIMEOUT:
  3140. {
  3141. break;
  3142. }
  3143. default:
  3144. {
  3145. DNASSERT( FALSE );
  3146. break;
  3147. }
  3148. }
  3149. //
  3150. // receive complete
  3151. //
  3152. switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ], 0 ) )
  3153. {
  3154. case WAIT_OBJECT_0:
  3155. {
  3156. // DPFX(DPFPREP, 0, "\n\n\nPrimary Win9x thread servicing receives!\n\n\n" );
  3157. CompleteOutstandingReceives( pCoreData->hWaitHandles[ EVENT_INDEX_RECEIVE_COMPLETE ] );
  3158. break;
  3159. }
  3160. case WAIT_TIMEOUT:
  3161. {
  3162. break;
  3163. }
  3164. default:
  3165. {
  3166. DNASSERT( FALSE );
  3167. break;
  3168. }
  3169. }
  3170. //
  3171. // SP closing
  3172. //
  3173. switch ( DNWaitForSingleObject( pCoreData->hWaitHandles[ EVENT_INDEX_STOP_ALL_THREADS ], 0 ) )
  3174. {
  3175. case WAIT_OBJECT_0:
  3176. {
  3177. DPFX(DPFPREP, 8, "Primary Win9x thread exit because SP closing!" );
  3178. pCoreData->fLooping = FALSE;
  3179. break;
  3180. }
  3181. case WAIT_TIMEOUT:
  3182. {
  3183. break;
  3184. }
  3185. default:
  3186. {
  3187. DNASSERT( FALSE );
  3188. break;
  3189. }
  3190. }
  3191. //
  3192. // If there is I/O pending the Read/Write handles are probably still signalled.
  3193. // Wait 5 milliseconds to process it before running through the handles again.
  3194. //
  3195. LockReadData();
  3196. LockWriteData();
  3197. if ( ( m_OutstandingReadList.IsEmpty() == FALSE ) ||
  3198. ( m_OutstandingWriteList.IsEmpty() == FALSE ) )
  3199. {
  3200. fAllIOComplete = FALSE;
  3201. }
  3202. UnlockReadData();
  3203. UnlockWriteData();
  3204. if ( fAllIOComplete == FALSE )
  3205. {
  3206. SleepEx( 5, TRUE );
  3207. }
  3208. }
  3209. //**********************************************************************
  3210. #endif // WIN95
  3211. #ifdef WIN95
  3212. //**********************************************************************
  3213. // ------------------------------
  3214. // CModemThreadPool::ProcessWin9xJob - process a Win9x job
  3215. //
  3216. // Entry: Pointer core data
  3217. //
  3218. // Exit: Nothing
  3219. // ------------------------------
  3220. #undef DPF_MODNAME
  3221. #define DPF_MODNAME "CModemThreadPool::ProcessWin9xJob"
  3222. void CModemThreadPool::ProcessWin9xJob( WIN9X_CORE_DATA *const pCoreData )
  3223. {
  3224. THREAD_POOL_JOB *pJobInfo;
  3225. //
  3226. // remove and process a single job from the list
  3227. //
  3228. pJobInfo = GetWorkItem();
  3229. if ( pJobInfo != NULL )
  3230. {
  3231. switch ( pJobInfo->JobType )
  3232. {
  3233. //
  3234. // enum refresh
  3235. //
  3236. case JOB_REFRESH_TIMER_JOBS:
  3237. {
  3238. DPFX(DPFPREP, 8, "WorkThread job REFRESH_ENUM" );
  3239. DNASSERT( pJobInfo->JobData.JobRefreshTimedJobs.uDummy == 0 );
  3240. LockTimerData();
  3241. pCoreData->fTimerJobsActive = ProcessTimerJobs( &m_TimerJobList, &pCoreData->dwNextTimerJobTime );
  3242. UnlockTimerData();
  3243. if ( pCoreData->fTimerJobsActive != FALSE )
  3244. {
  3245. DPFX(DPFPREP, 8, "There are active timer jobs left after processing a Win9x REFRESH_TIMER_JOBS" );
  3246. }
  3247. break;
  3248. }
  3249. //
  3250. // issue callback for this job
  3251. //
  3252. case JOB_DELAYED_COMMAND:
  3253. {
  3254. DPFX(DPFPREP, 8, "WorkThread job DELAYED_COMMAND" );
  3255. DNASSERT( pJobInfo->JobData.JobDelayedCommand.pCommandFunction != NULL );
  3256. pJobInfo->JobData.JobDelayedCommand.pCommandFunction( pJobInfo );
  3257. break;
  3258. }
  3259. //
  3260. // other job
  3261. //
  3262. default:
  3263. {
  3264. DPFX(DPFPREP, 0, "WorkThread Win9x job unknown!" );
  3265. DNASSERT( FALSE );
  3266. break;
  3267. }
  3268. }
  3269. pJobInfo->JobType = JOB_UNINITIALIZED;
  3270. g_ModemThreadPoolJobPool.Release( pJobInfo );
  3271. }
  3272. }
  3273. //**********************************************************************
  3274. #endif // WIN95
  3275. //**********************************************************************
  3276. // ------------------------------
  3277. // CModemThreadPool::ProcessTapiEvent - process TAPI event
  3278. //
  3279. // Entry: Nothing
  3280. //
  3281. // Exit: Nothing
  3282. // ------------------------------
  3283. void CModemThreadPool::ProcessTapiEvent( void )
  3284. {
  3285. LONG lTapiReturn;
  3286. LINEMESSAGE LineMessage;
  3287. lTapiReturn = p_lineGetMessage( GetTAPIInfo()->hApplicationInstance, &LineMessage, 0 );
  3288. if ( lTapiReturn != LINEERR_NONE )
  3289. {
  3290. DPFX(DPFPREP, 0, "Failed to process Win9x TAPI message!" );
  3291. DisplayTAPIError( 0, lTapiReturn );
  3292. }
  3293. else
  3294. {
  3295. CDataPort *pModemPort;
  3296. DPNHANDLE hModemPort;
  3297. DNASSERT( sizeof( hModemPort ) == sizeof( LineMessage.dwCallbackInstance ) );
  3298. hModemPort = (DPNHANDLE)( LineMessage.dwCallbackInstance );
  3299. pModemPort = static_cast<CDataPort*>( DataPortFromHandle( hModemPort ) );
  3300. if ( pModemPort != NULL )
  3301. {
  3302. pModemPort->ProcessTAPIMessage( &LineMessage );
  3303. pModemPort->DecRef();
  3304. }
  3305. }
  3306. }
  3307. //**********************************************************************
  3308. //**********************************************************************
  3309. // ------------------------------
  3310. // ThreadPoolJob_Alloc - allocate a new job
  3311. //
  3312. // Entry: Pointer to new entry
  3313. //
  3314. // Exit: Boolean indicating success
  3315. // TRUE = initialization successful
  3316. // FALSE = initialization failed
  3317. // ------------------------------
  3318. #undef DPF_MODNAME
  3319. #define DPF_MODNAME "ThreadPoolJob_Alloc"
  3320. BOOL ThreadPoolJob_Alloc( void *pvItem, void* pvContext )
  3321. {
  3322. BOOL fReturn;
  3323. THREAD_POOL_JOB *pJob;
  3324. //
  3325. // initialize
  3326. //
  3327. fReturn = TRUE;
  3328. pJob = static_cast<THREAD_POOL_JOB*>( pvItem );
  3329. memset( pJob, 0x00, sizeof( *pJob ) );
  3330. return fReturn;
  3331. }
  3332. //**********************************************************************
  3333. //**********************************************************************
  3334. // ------------------------------
  3335. // ThreadPoolJob_Get - a job is being removed from the pool
  3336. //
  3337. // Entry: Pointer to job
  3338. //
  3339. // Exit: Nothing
  3340. // ------------------------------
  3341. #undef DPF_MODNAME
  3342. #define DPF_MODNAME "ThreadPoolJob_Get"
  3343. void ThreadPoolJob_Get( void *pvItem, void* pvContext )
  3344. {
  3345. THREAD_POOL_JOB *pJob;
  3346. //
  3347. // initialize
  3348. //
  3349. pJob = static_cast<THREAD_POOL_JOB*>( pvItem );
  3350. DNASSERT( pJob->JobType == JOB_UNINITIALIZED );
  3351. DNASSERT( pJob->pNext == NULL );
  3352. }
  3353. //**********************************************************************
  3354. //**********************************************************************
  3355. // ------------------------------
  3356. // ThreadPoolJob_Release - a job is being returned to the pool
  3357. //
  3358. // Entry: Pointer to job
  3359. //
  3360. // Exit: Nothing
  3361. // ------------------------------
  3362. #undef DPF_MODNAME
  3363. #define DPF_MODNAME "ThreadPoolJob_Release"
  3364. void ThreadPoolJob_Release( void *pvItem )
  3365. {
  3366. THREAD_POOL_JOB *pJob;
  3367. DNASSERT( pvItem != NULL );
  3368. pJob = static_cast<THREAD_POOL_JOB*>( pvItem );
  3369. DNASSERT( pJob->JobType == JOB_UNINITIALIZED );
  3370. pJob->pNext = NULL;
  3371. }
  3372. //**********************************************************************
  3373. //**********************************************************************
  3374. // ------------------------------
  3375. // ModemTimerEntry_Alloc - allocate a new timer job entry
  3376. //
  3377. // Entry: Pointer to new entry
  3378. //
  3379. // Exit: Boolean indicating success
  3380. // TRUE = initialization successful
  3381. // FALSE = initialization failed
  3382. // ------------------------------
  3383. #undef DPF_MODNAME
  3384. #define DPF_MODNAME "ModemTimerEntry_Alloc"
  3385. BOOL ModemTimerEntry_Alloc( void *pvItem, void* pvContext )
  3386. {
  3387. BOOL fReturn;
  3388. TIMER_OPERATION_ENTRY *pTimerEntry;
  3389. DNASSERT( pvItem != NULL );
  3390. //
  3391. // initialize
  3392. //
  3393. fReturn = TRUE;
  3394. pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
  3395. memset( pTimerEntry, 0x00, sizeof( *pTimerEntry ) );
  3396. pTimerEntry->pContext = NULL;
  3397. pTimerEntry->Linkage.Initialize();
  3398. return fReturn;
  3399. }
  3400. //**********************************************************************
  3401. //**********************************************************************
  3402. // ------------------------------
  3403. // ModemTimerEntry_Get - get new timer job entry from pool
  3404. //
  3405. // Entry: Pointer to new entry
  3406. //
  3407. // Exit: Nothing
  3408. // ------------------------------
  3409. #undef DPF_MODNAME
  3410. #define DPF_MODNAME "ModemTimerEntry_Get"
  3411. void ModemTimerEntry_Get( void *pvItem, void* pvContext )
  3412. {
  3413. TIMER_OPERATION_ENTRY *pTimerEntry;
  3414. DNASSERT( pvItem != NULL );
  3415. pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
  3416. pTimerEntry->Linkage.Initialize();
  3417. DNASSERT( pTimerEntry->pContext == NULL );
  3418. }
  3419. //**********************************************************************
  3420. //**********************************************************************
  3421. // ------------------------------
  3422. // ModemTimerEntry_Release - return timer job entry to pool
  3423. //
  3424. // Entry: Pointer to entry
  3425. //
  3426. // Exit: Nothing
  3427. // ------------------------------
  3428. #undef DPF_MODNAME
  3429. #define DPF_MODNAME "ModemTimerEntry_Release"
  3430. void ModemTimerEntry_Release( void *pvItem )
  3431. {
  3432. TIMER_OPERATION_ENTRY *pTimerEntry;
  3433. DNASSERT( pvItem != NULL );
  3434. pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
  3435. pTimerEntry->pContext= NULL;
  3436. DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
  3437. }
  3438. //**********************************************************************
  3439. //**********************************************************************
  3440. // ------------------------------
  3441. // ModemTimerEntry_Dealloc - deallocate a timer job entry
  3442. //
  3443. // Entry: Pointer to entry
  3444. //
  3445. // Exit: Nothing
  3446. // ------------------------------
  3447. #undef DPF_MODNAME
  3448. #define DPF_MODNAME "ModemTimerEntry_Dealloc"
  3449. void ModemTimerEntry_Dealloc( void *pvItem )
  3450. {
  3451. TIMER_OPERATION_ENTRY *pTimerEntry;
  3452. DNASSERT( pvItem != NULL );
  3453. //
  3454. // initialize
  3455. //
  3456. pTimerEntry = static_cast<TIMER_OPERATION_ENTRY*>( pvItem );
  3457. //
  3458. // return associated poiner to write data
  3459. //
  3460. DNASSERT( pTimerEntry->Linkage.IsEmpty() != FALSE );
  3461. DNASSERT( pTimerEntry->pContext == NULL );
  3462. }
  3463. //**********************************************************************