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

3080 lines
80 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name :
  4. atqmain.cxx
  5. Abstract:
  6. This module implements entry points for ATQ - Asynchronous Thread Queue.
  7. Author:
  8. Murali R. Krishnan ( MuraliK ) 8-Apr-1996
  9. Environment:
  10. User Mode -- Win32
  11. Project:
  12. Internet Services Common DLL
  13. Functions Exported:
  14. BOOL AtqInitialize();
  15. BOOL AtqTerminate();
  16. BOOL AtqGetCompletionPort();
  17. DWORD AtqSetInfo();
  18. DWORD AtqGetInfo();
  19. BOOL AtqGetStatistics();
  20. BOOL AtqClearStatistics();
  21. BOOL AtqAddAcceptExSockets();
  22. BOOL AtqAddAsyncHandle();
  23. DWORD AtqContextSetInfo();
  24. VOID AtqCloseSocket();
  25. VOID AtqFreeContext();
  26. BOOL AtqReadFile();
  27. BOOL AtqWriteFile();
  28. BOOL AtqTransmitFile();
  29. BOOL AtqPostCompletionStatus();
  30. PVOID AtqAllocateBandwidthInfo();
  31. BOOL AtqFreeBandwidthInfo();
  32. DWORD AtqBandwidthSetInfo();
  33. --*/
  34. #include "isatq.hxx"
  35. #include <iscaptrc.h>
  36. # define ATQ_REG_DEF_THREAD_TIMEOUT_PWS (30*60) // 30 minutes
  37. /************************************************************
  38. * Globals
  39. ************************************************************/
  40. //
  41. // specifies the registry location to use for getting the ATQ Configuration
  42. // (Global overrides)
  43. //
  44. CHAR g_PSZ_ATQ_CONFIG_PARAMS_REG_KEY[] =
  45. TEXT("System\\CurrentControlSet\\Services\\InetInfo\\Parameters");
  46. // ----------------------------------------
  47. // # of CPUs in machine (for thread-tuning)
  48. // ----------------------------------------
  49. DWORD g_cCPU = 0;
  50. //
  51. // concurrent # of threads to run per processor
  52. //
  53. DWORD g_cConcurrency = ATQ_REG_DEF_PER_PROCESSOR_CONCURRENCY;
  54. //
  55. // Amount of time (in ms) a worker thread will be idle before suicide
  56. //
  57. DWORD g_msThreadTimeout = ATQ_REG_DEF_THREAD_TIMEOUT * 1000;
  58. BOOL g_fUseAcceptEx = TRUE; // Use AcceptEx if available
  59. //
  60. // The absolute thread limit
  61. //
  62. LONG g_cMaxThreadLimit = ATQ_REG_DEF_POOL_THREAD_LIMIT;
  63. //
  64. // Should we use fake completion port
  65. //
  66. BOOL g_fUseFakeCompletionPort = FALSE;
  67. //
  68. // Assumed minimum file transfer rate
  69. //
  70. DWORD g_cbMinKbSec = ATQ_REG_DEF_MIN_KB_SEC;
  71. //
  72. // Size of buffers for fake xmits
  73. //
  74. DWORD g_cbXmitBufferSize = ATQ_REG_DEF_NONTF_BUFFER_SIZE;
  75. //
  76. // number of active context list
  77. //
  78. DWORD g_dwNumContextLists = ATQ_NUM_CONTEXT_LIST;
  79. /*
  80. g_pfnExitThreadCallback()
  81. This routine sets the callback routine to be called when one of the
  82. Atq threads exit so that thread state data can be cleaned up. Currently
  83. support is for a single routine. One way to support multiple routines would
  84. be for the caller to save the return value. Such an application would not
  85. be able to delete the "saved" callback routine.
  86. */
  87. ATQ_THREAD_EXIT_CALLBACK g_pfnExitThreadCallback = NULL;
  88. // ----------------------------------
  89. // Fake Completion port
  90. // -----------------------------------
  91. //
  92. // Used to gauge pool thread creation. This variable shows number of
  93. // ATQ contexts // ready to be processed by ATQ pool thread. Basically
  94. // this is length of outcoming queue in SIO module and is modified by
  95. // routines there
  96. //
  97. DWORD g_AtqWaitingContextsCount = 0;
  98. //
  99. // mswsock entry points
  100. //
  101. HINSTANCE g_hMSWsock = NULL;
  102. PFN_ACCEPTEX g_pfnAcceptEx = NULL;
  103. PFN_GETACCEPTEXSOCKADDRS g_pfnGetAcceptExSockaddrs = NULL;
  104. PFN_TRANSMITFILE g_pfnTransmitFile = NULL;
  105. PFN_GET_QUEUED_COMPLETION_STATUS g_pfnGetQueuedCompletionStatus = NULL;
  106. PFN_CREATE_COMPLETION_PORT g_pfnCreateCompletionPort = NULL;
  107. PFN_CLOSE_COMPLETION_PORT g_pfnCloseCompletionPort = NULL;
  108. PFN_POST_COMPLETION_STATUS g_pfnPostCompletionStatus = NULL;
  109. HINSTANCE g_hNtdll = NULL;
  110. PFN_RTL_INIT_UNICODE_STRING g_pfnRtlInitUnicodeString = NULL;
  111. PFN_NT_LOAD_DRIVER g_pfnNtLoadDriver = NULL;
  112. PFN_RTL_NTSTATUS_TO_DOSERR g_pfnRtlNtStatusToDosError = NULL;
  113. PFN_RTL_INIT_ANSI_STRING g_pfnRtlInitAnsiString = NULL;
  114. PFN_RTL_ANSI_STRING_TO_UNICODE_STRING g_pfnRtlAnsiStringToUnicodeString = NULL;
  115. PFN_RTL_DOS_PATHNAME_TO_NT_PATHNAME g_pfnRtlDosPathNameToNtPathName_U = NULL;
  116. PFN_RTL_FREE_HEAP g_pfnRtlFreeHeap = NULL;
  117. //
  118. // NT specific
  119. //
  120. PFN_READ_DIR_CHANGES_W g_pfnReadDirChangesW = NULL;
  121. // ------------------------------
  122. // Current State Information
  123. // ------------------------------
  124. HANDLE g_hCompPort = NULL; // Handle for completion port
  125. LONG g_cThreads = 0; // number of thread in the pool
  126. LONG g_cAvailableThreads = 0; // # of threads waiting on the port.
  127. //
  128. // Is the NTS driver in use
  129. //
  130. BOOL g_fUseDriver = FALSE;
  131. //
  132. // Should we use the TF_USE_KERNEL_APC flag for TransmitFile
  133. //
  134. BOOL g_fUseKernelApc = ATQ_REG_DEF_USE_KERNEL_APC;
  135. //
  136. // Current thread limit
  137. //
  138. LONG g_cMaxThreads = ATQ_REG_DEF_PER_PROCESSOR_ATQ_THREADS;
  139. DWORD g_cListenBacklog = ATQ_REG_DEF_LISTEN_BACKLOG;
  140. BOOL g_fShutdown = FALSE; // if set, indicates that we are shutting down
  141. // in that case, all threads should exit.
  142. HANDLE g_hShutdownEvent = NULL; // set when all running threads shutdown
  143. BOOL g_fEnableDebugThreads = FALSE; // if TRUE, debug IO threads can be
  144. // created
  145. BOOL g_fCreateDebugThread = FALSE; // set to TRUE to create a debug thread
  146. // ------------------------------
  147. // Bandwidth Throttling Info
  148. // ------------------------------
  149. PBANDWIDTH_INFO g_pBandwidthInfo = NULL;
  150. // ------------------------------
  151. // Various State/Object Lists
  152. // ------------------------------
  153. //
  154. // Used to switch context between lists
  155. //
  156. DWORD AtqGlobalContextCount = 0;
  157. //
  158. // List of active context
  159. //
  160. ATQ_CONTEXT_LISTHEAD AtqActiveContextList[ATQ_NUM_CONTEXT_LIST];
  161. //
  162. // List of Endpoints in ATQ - one per listen socket
  163. //
  164. LIST_ENTRY AtqEndpointList;
  165. CRITICAL_SECTION AtqEndpointLock;
  166. PALLOC_CACHE_HANDLER g_pachAtqContexts;
  167. #ifdef IIS_AUX_COUNTERS
  168. LONG g_AuxCounters[NUM_AUX_COUNTERS];
  169. #endif // IIS_AUX_COUNTERS
  170. //
  171. // Timeout before closing pending listens in case backlog is full
  172. //
  173. DWORD g_cForceTimeout;
  174. //
  175. // Flag enabling/disabling the backlog monitor
  176. //
  177. BOOL g_fDisableBacklogMonitor = FALSE;
  178. // ------------------------------
  179. // local to this module
  180. // ------------------------------
  181. LONG sg_AtqInitializeCount = -1;
  182. BOOL g_fSpudInitialized = FALSE;
  183. HANDLE g_hCapThread = NULL;
  184. DWORD
  185. I_AtqGetGlobalConfiguration(VOID);
  186. DWORD
  187. I_NumAtqEndpointsOpen(VOID);
  188. DWORD
  189. AtqDebugCreatorThread(
  190. LPDWORD param
  191. );
  192. //
  193. // Capacity Planning variables
  194. //
  195. extern TRACEHANDLE IISCapTraceRegistrationHandle;
  196. //
  197. // Ensure that initialization/termination don't happen at the same time
  198. //
  199. CRITICAL_SECTION g_csInitTermLock;
  200. /************************************************************
  201. * Functions
  202. ************************************************************/
  203. BOOL
  204. AtqInitialize(
  205. IN DWORD dwFlags
  206. )
  207. /*++
  208. Routine Description:
  209. Initializes the ATQ package
  210. Arguments:
  211. dwFlags - DWORD containing the flags for use to initialize ATQ library.
  212. Notably in many cases one may not need the SPUD driver initialized
  213. for processes other than the IIS main process. This dword helps
  214. to shut off the unwanted flags.
  215. This is an ugly way to initialize/shutdown SPUD, but that is what we
  216. will do. SPUD supports only ONE completion port and hence when we use
  217. ATQ in multiple processes we should be careful to initialize SPUD only
  218. once and hopefully in the IIS main process!
  219. Return Value:
  220. TRUE if successful, FALSE on error (call GetLastError)
  221. Note:
  222. As of 4/16/97 the pszRegKey that is sent is no more utilized.
  223. We always load the internal configuration parameters from
  224. one single registry entry specified by PSZ_ATQ_CONFIG_PARAMS_REG_KEY
  225. The parameter is left in the command line for compatibility
  226. with old callers :( - NYI: Need to change this.
  227. --*/
  228. {
  229. DWORD i;
  230. DWORD dwThreadID;
  231. DBGPRINTF(( DBG_CONTEXT, "AtqInitialize, %d, %x\n",
  232. sg_AtqInitializeCount, dwFlags));
  233. if ( InterlockedIncrement( &sg_AtqInitializeCount) != 0) {
  234. IF_DEBUG( API_ENTRY) {
  235. ATQ_PRINTF(( DBG_CONTEXT,
  236. "AtqInitialize( %08x). ATQ is already initialized.\n",
  237. dwFlags));
  238. }
  239. //
  240. // we are already initialized. Ignore the new registry settings
  241. //
  242. return ( TRUE);
  243. }
  244. EnterCriticalSection( &g_csInitTermLock );
  245. IF_DEBUG( API_ENTRY) {
  246. ATQ_PRINTF(( DBG_CONTEXT,
  247. "AtqInitialize[%08x]. Initializing....\n",
  248. dwFlags));
  249. }
  250. // get the number of processors for this machine
  251. // do it only for NT Server only (don't scale workstation)
  252. if ( TsIsNtServer() ) {
  253. SYSTEM_INFO si;
  254. GetSystemInfo( &si );
  255. g_cCPU = si.dwNumberOfProcessors;
  256. } else {
  257. g_cCPU = 1;
  258. }
  259. //
  260. // Initialize context lists and crit sects
  261. //
  262. ATQ_CONTEXT_LISTHEAD * pacl;
  263. for ( pacl = AtqActiveContextList;
  264. pacl < (AtqActiveContextList + g_dwNumContextLists);
  265. pacl++) {
  266. pacl->Initialize();
  267. }
  268. InitializeListHead( &AtqEndpointList );
  269. INITIALIZE_CRITICAL_SECTION( &AtqEndpointLock );
  270. //
  271. // init bandwidth throttling
  272. //
  273. ATQ_REQUIRE( BANDWIDTH_INFO::AbwInitialize() );
  274. //
  275. // Read registry configurable Atq options. We have to read these now
  276. // because concurrency is set for the completion port at creation time.
  277. //
  278. DWORD dwError = I_AtqGetGlobalConfiguration();
  279. if ( NO_ERROR != dwError) {
  280. SetLastError( dwError );
  281. goto cleanup;
  282. }
  283. //
  284. // Setup an allocation cache for the ATQ Contexts
  285. // NYI: Auto-tune the threshold limit
  286. //
  287. {
  288. ALLOC_CACHE_CONFIGURATION acConfig;
  289. DWORD nCachedAtq = ATQ_CACHE_LIMIT_NTS;
  290. if ( TsIsWindows95()) { nCachedAtq = ATQ_CACHE_LIMIT_W95; }
  291. acConfig.nConcurrency = 1;
  292. acConfig.nThreshold = nCachedAtq;
  293. acConfig.cbSize = sizeof(ATQ_CONTEXT);
  294. g_pachAtqContexts = new ALLOC_CACHE_HANDLER( "ATQ", &acConfig);
  295. if ( NULL == g_pachAtqContexts) {
  296. goto cleanup;
  297. }
  298. }
  299. //
  300. // Create the shutdown event
  301. //
  302. g_hShutdownEvent = IIS_CREATE_EVENT(
  303. "g_hShutdownEvent",
  304. &g_hShutdownEvent,
  305. TRUE, // Manual reset
  306. FALSE // Not signalled
  307. );
  308. if ( !g_hShutdownEvent ) {
  309. DBGERROR(( DBG_CONTEXT, "Create Shutdown event failed. Last Error = 0x%x\n",
  310. GetLastError()
  311. ));
  312. goto cleanup;
  313. }
  314. //
  315. // Create the completion port
  316. //
  317. g_hCompPort = g_pfnCreateCompletionPort(INVALID_HANDLE_VALUE,
  318. NULL,
  319. 0,
  320. g_cConcurrency
  321. );
  322. if ( !g_hCompPort ) {
  323. DBGERROR(( DBG_CONTEXT, "Create IoComp port failed. Last Error = 0x%x\n",
  324. GetLastError()
  325. ));
  326. goto cleanup;
  327. }
  328. //
  329. // initialize spud driver
  330. //
  331. if ( (dwFlags & ATQ_INIT_SPUD_FLAG) && g_fUseDriver ) {
  332. if (! I_AtqSpudInitialize() ) {
  333. goto cleanup;
  334. }
  335. g_fSpudInitialized = TRUE;
  336. }
  337. //
  338. // Initialize Backlog Monitor
  339. //
  340. if ( !g_fDisableBacklogMonitor )
  341. {
  342. DBG_ASSERT( g_pAtqBacklogMonitor == NULL );
  343. g_pAtqBacklogMonitor = new ATQ_BACKLOG_MONITOR;
  344. if (!g_pAtqBacklogMonitor) {
  345. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  346. goto cleanup;
  347. }
  348. }
  349. //
  350. // Ensure all other initializations also are done
  351. //
  352. g_cThreads = 0;
  353. g_fShutdown = FALSE;
  354. g_cAvailableThreads = 0;
  355. if ( !I_AtqStartTimeoutProcessing( NULL ) ) {
  356. goto cleanup;
  357. }
  358. IF_DEBUG(INIT_CLEAN) {
  359. DBGPRINTF(( DBG_CONTEXT,
  360. "fUseAcceptEx[%d] NT CompPort[%d] Platform[%d]"
  361. " fUseDriver[%d]\n",
  362. g_fUseAcceptEx, !g_fUseFakeCompletionPort,
  363. IISPlatformType(),
  364. g_fUseDriver
  365. ));
  366. }
  367. //
  368. // Create the initial ATQ thread.
  369. //
  370. (VOID)I_AtqCheckThreadStatus( (PVOID)ATQ_INITIAL_THREAD );
  371. //
  372. // Create a second thread if we are NTS
  373. //
  374. if ( TsIsNtServer() ) {
  375. (VOID)I_AtqCheckThreadStatus( (PVOID)ATQ_INITIAL_THREAD );
  376. }
  377. //
  378. // Initialize Capacity Planning Trace
  379. //
  380. // Spawn another thread to do this since IISInitializeCapTrace() can
  381. // take a while and we are not in a state where SCM is getting
  382. // SERVICE_STARTING messages
  383. //
  384. g_hCapThread = CreateThread( NULL,
  385. 0,
  386. IISInitializeCapTrace,
  387. NULL,
  388. 0,
  389. &dwThreadID
  390. );
  391. IF_DEBUG( API_EXIT) {
  392. ATQ_PRINTF(( DBG_CONTEXT,
  393. "AtqInitialize( %08x) returns %d.\n",
  394. dwFlags, TRUE));
  395. }
  396. //
  397. // Create the debug thread starter if necessary
  398. //
  399. if ( g_fEnableDebugThreads )
  400. {
  401. DWORD dwError;
  402. DWORD dwThreadID;
  403. HANDLE hThread;
  404. hThread = CreateThread( NULL,
  405. 0,
  406. (LPTHREAD_START_ROUTINE)AtqDebugCreatorThread,
  407. NULL,
  408. 0,
  409. &dwThreadID );
  410. if ( !hThread )
  411. {
  412. goto cleanup;
  413. }
  414. CloseHandle( hThread );
  415. }
  416. LeaveCriticalSection( &g_csInitTermLock );
  417. return TRUE;
  418. cleanup:
  419. DWORD dwSaveError = GetLastError();
  420. for (i=0; i<g_dwNumContextLists; i++) {
  421. AtqActiveContextList[i].Cleanup();
  422. }
  423. DeleteCriticalSection( &AtqEndpointLock);
  424. if ( g_hShutdownEvent != NULL ) {
  425. CloseHandle( g_hShutdownEvent );
  426. g_hShutdownEvent = NULL;
  427. }
  428. if ( g_hCompPort != NULL ) {
  429. g_pfnCloseCompletionPort( g_hCompPort );
  430. g_hCompPort = NULL;
  431. }
  432. if ( NULL != g_pachAtqContexts) {
  433. delete g_pachAtqContexts;
  434. g_pachAtqContexts = NULL;
  435. }
  436. if ( NULL != g_pAtqBacklogMonitor ) {
  437. delete g_pAtqBacklogMonitor;
  438. g_pAtqBacklogMonitor = NULL;
  439. }
  440. ATQ_REQUIRE( BANDWIDTH_INFO::AbwTerminate());
  441. IF_DEBUG( API_EXIT) {
  442. ATQ_PRINTF(( DBG_CONTEXT,
  443. "AtqInitialize( %08x) returns %d.\n",
  444. dwFlags, FALSE));
  445. }
  446. sg_AtqInitializeCount = -1;
  447. SetLastError(dwSaveError);
  448. LeaveCriticalSection( &g_csInitTermLock );
  449. return(FALSE);
  450. } // AtqInitialize()
  451. BOOL
  452. AtqTerminate(
  453. VOID
  454. )
  455. /*++
  456. Routine Description:
  457. Cleans up the ATQ package. Should only be called after all of the
  458. clients of ATQ have been shutdown.
  459. Arguments:
  460. None.
  461. Return Value:
  462. TRUE, if ATQ was shutdown properly
  463. FALSE, otherwise
  464. --*/
  465. {
  466. DBGPRINTF(( DBG_CONTEXT, "AtqTerminate, %d\n", sg_AtqInitializeCount));
  467. DWORD currentThreadCount;
  468. ATQ_CONTEXT_LISTHEAD * pacl;
  469. BOOL fRet = TRUE;
  470. DWORD dwErr;
  471. // there are outstanding users, don't fully terminate
  472. if ( InterlockedDecrement( &sg_AtqInitializeCount) >= 0) {
  473. /*IF_DEBUG( API_ENTRY)*/ {
  474. ATQ_PRINTF(( DBG_CONTEXT,
  475. "AtqTerminate() - there are other users."
  476. " Not terminating now\n"
  477. ));
  478. }
  479. return (TRUE);
  480. }
  481. EnterCriticalSection( &g_csInitTermLock );
  482. /*IF_DEBUG( API_ENTRY)*/ {
  483. ATQ_PRINTF(( DBG_CONTEXT,
  484. "AtqTerminate() - Terminating ATQ ...\n"
  485. ));
  486. }
  487. //
  488. // All the ATQ endpoints should have been terminated before calling
  489. // this ATQTerminate() function. If not, sorry return failure.
  490. //
  491. DWORD nEndpointsToBeClosed = I_NumAtqEndpointsOpen();
  492. if ( nEndpointsToBeClosed > 0) {
  493. DBGPRINTF(( DBG_CONTEXT,
  494. " There are %d endpoints remaining to be closed."
  495. " Somebody above stream did not close endpoints."
  496. " BUG IN CODE ABOVE ATQ\n"
  497. ,
  498. nEndpointsToBeClosed
  499. ));
  500. SetLastError( ERROR_NETWORK_BUSY);
  501. fRet = FALSE;
  502. goto Finished;
  503. }
  504. if ( (g_hShutdownEvent == NULL) || g_fShutdown ) {
  505. //
  506. // We have not been intialized or have already terminated.
  507. //
  508. SetLastError( ERROR_NOT_READY );
  509. fRet = FALSE;
  510. goto Finished;
  511. }
  512. //
  513. // All clients should have cleaned themselves up before calling us.
  514. //
  515. for ( pacl = AtqActiveContextList;
  516. pacl < (AtqActiveContextList + g_dwNumContextLists);
  517. pacl++) {
  518. pacl->Lock();
  519. if ( !IsListEmpty(&pacl->ActiveListHead)) {
  520. ATQ_ASSERT( IsListEmpty( &pacl->ActiveListHead));
  521. pacl->Unlock();
  522. IF_DEBUG( API_EXIT) {
  523. ATQ_PRINTF(( DBG_CONTEXT,
  524. "AtqTerminate() - ContextList(%08x) has "
  525. "Active Contexts. Failed Termination.\n",
  526. pacl
  527. ));
  528. }
  529. fRet = FALSE;
  530. goto Finished;
  531. }
  532. pacl->Unlock();
  533. } // for
  534. //
  535. // Note that we are shutting down and prevent any more handles from
  536. // being added to the completion port.
  537. //
  538. g_fShutdown = TRUE;
  539. //
  540. // Attempt and remove the TimeOut Context from scheduler queue
  541. //
  542. DBG_REQUIRE( I_AtqStopTimeoutProcessing());
  543. currentThreadCount = g_cThreads;
  544. if (currentThreadCount > 0) {
  545. DWORD i;
  546. BOOL fRes;
  547. OVERLAPPED overlapped;
  548. //
  549. // Post a message to the completion port for each worker thread
  550. // telling it to exit. The indicator is a NULL context in the
  551. // completion.
  552. //
  553. ZeroMemory( &overlapped, sizeof(OVERLAPPED) );
  554. for (i=0; i<currentThreadCount; i++) {
  555. fRes = g_pfnPostCompletionStatus( g_hCompPort,
  556. 0,
  557. 0,
  558. &overlapped );
  559. ATQ_ASSERT( (fRes == TRUE) ||
  560. ( (fRes == FALSE) &&
  561. (GetLastError() == ERROR_IO_PENDING) )
  562. );
  563. }
  564. }
  565. //
  566. // Now wait for the pool threads to shutdown.
  567. //
  568. dwErr = WaitForSingleObject( g_hShutdownEvent, ATQ_WAIT_FOR_THREAD_DEATH);
  569. #if 0
  570. DWORD dwWaitCount = 0;
  571. while ( dwErr == WAIT_TIMEOUT) {
  572. dwWaitCount++;
  573. DebugBreak();
  574. Sleep( 10*1000); // sleep for some time
  575. dwErr =
  576. WaitForSingleObject( g_hShutdownEvent, ATQ_WAIT_FOR_THREAD_DEATH);
  577. } // while
  578. # endif // 0
  579. //
  580. // At this point, no other threads should be left running.
  581. //
  582. //
  583. // g_cThreads counter is decremented by AtqPoolThread().
  584. // AtqTerminate() is called during the DLL termination
  585. // But at DLL termination, all ATQ pool threads are killed =>
  586. // no one is decrementing the count. Hence this assert will always fail.
  587. //
  588. // ATQ_ASSERT( !g_cThreads );
  589. ATQ_REQUIRE( CloseHandle( g_hShutdownEvent ) );
  590. g_pfnCloseCompletionPort( g_hCompPort );
  591. g_hShutdownEvent = NULL;
  592. g_hCompPort = NULL;
  593. //
  594. // Cleanup our synchronization resources
  595. //
  596. for ( pacl = AtqActiveContextList;
  597. pacl < (AtqActiveContextList + g_dwNumContextLists);
  598. pacl++) {
  599. PLIST_ENTRY pEntry;
  600. pacl->Lock();
  601. if ( !IsListEmpty( &pacl->PendingAcceptExListHead)) {
  602. for ( pEntry = pacl->PendingAcceptExListHead.Flink;
  603. pEntry != &pacl->PendingAcceptExListHead;
  604. pEntry = pEntry->Flink ) {
  605. PATQ_CONT pContext =
  606. CONTAINING_RECORD( pEntry, ATQ_CONTEXT, m_leTimeout );
  607. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  608. pContext->Print();
  609. } // for
  610. }
  611. pacl->Unlock();
  612. pacl->Cleanup();
  613. }
  614. //
  615. // Free all the elements in the Allocation caching list
  616. //
  617. if ( NULL != g_pachAtqContexts) {
  618. delete g_pachAtqContexts;
  619. g_pachAtqContexts = NULL;
  620. }
  621. if ( g_hCapThread )
  622. {
  623. WaitForSingleObject( g_hCapThread, INFINITE );
  624. CloseHandle( g_hCapThread );
  625. g_hCapThread = NULL;
  626. if (IISCapTraceRegistrationHandle != (TRACEHANDLE)0)
  627. {
  628. UnregisterTraceGuids( IISCapTraceRegistrationHandle );
  629. }
  630. }
  631. DeleteCriticalSection( &AtqEndpointLock);
  632. //
  633. // cleanup backlog monitor
  634. //
  635. delete g_pAtqBacklogMonitor;
  636. g_pAtqBacklogMonitor = NULL;
  637. // Cleanup variables in ATQ Bandwidth throttle module
  638. if ( !BANDWIDTH_INFO::AbwTerminate()) {
  639. // there may be a few blocked IO. We should avoid them all.
  640. // All clients should have cleaned themselves up before coming here.
  641. fRet = FALSE;
  642. goto Finished;
  643. }
  644. //
  645. // cleanup driver
  646. //
  647. if ( g_fSpudInitialized ) {
  648. (VOID)I_AtqSpudTerminate();
  649. g_fSpudInitialized = FALSE;
  650. }
  651. if ( g_hNtdll != NULL ) {
  652. FreeLibrary(g_hNtdll);
  653. g_hNtdll = NULL;
  654. }
  655. if ( g_hMSWsock != NULL ) {
  656. FreeLibrary(g_hMSWsock);
  657. g_hMSWsock = NULL;
  658. }
  659. IF_DEBUG( API_EXIT) {
  660. ATQ_PRINTF(( DBG_CONTEXT,
  661. "AtqTerminate() - Successfully cleaned up.\n"
  662. ));
  663. }
  664. Finished:
  665. LeaveCriticalSection( &g_csInitTermLock );
  666. return fRet;
  667. } // AtqTerminate()
  668. HANDLE
  669. AtqGetCompletionPort()
  670. /*++
  671. Routine Description:
  672. Return the completion port created by ATQ
  673. Arguments:
  674. Return Value:
  675. Handle to ATQ completion port
  676. --*/
  677. {
  678. return g_hCompPort;
  679. } // AtqGetCompletionPort()
  680. ULONG_PTR
  681. AtqSetInfo(
  682. IN ATQ_INFO atqInfo,
  683. IN ULONG_PTR Data
  684. )
  685. /*++
  686. Routine Description:
  687. Sets various bits of information for the ATQ module
  688. Arguments:
  689. atqInfo - Data item to set
  690. data - New value for item
  691. Return Value:
  692. The old value of the parameter
  693. --*/
  694. {
  695. ULONG_PTR oldVal = 0;
  696. switch ( atqInfo ) {
  697. case AtqBandwidthThrottle:
  698. ATQ_ASSERT( g_pBandwidthInfo != NULL );
  699. oldVal = (ULONG_PTR)g_pBandwidthInfo->SetBandwidthLevel( (DWORD)Data );
  700. break;
  701. case AtqBandwidthThrottleMaxBlocked:
  702. ATQ_ASSERT( g_pBandwidthInfo != NULL );
  703. oldVal = (ULONG_PTR)g_pBandwidthInfo->SetMaxBlockedListSize( (DWORD)Data );
  704. break;
  705. case AtqExitThreadCallback:
  706. oldVal = (ULONG_PTR)g_pfnExitThreadCallback;
  707. g_pfnExitThreadCallback = (ATQ_THREAD_EXIT_CALLBACK ) Data;
  708. break;
  709. case AtqMaxPoolThreads:
  710. // the value is per processor values
  711. // internally we maintain value for all processors
  712. oldVal = (ULONG_PTR)( g_cMaxThreads/g_cCPU );
  713. g_cMaxThreads = (DWORD)Data * g_cCPU;
  714. break;
  715. //
  716. // Increment or decrement the max thread count. In this instance, we
  717. // do not scale by the number of CPUs
  718. //
  719. case AtqIncMaxPoolThreads:
  720. InterlockedIncrement( (LONG *) &g_cMaxThreads );
  721. oldVal = TRUE;
  722. break;
  723. case AtqDecMaxPoolThreads:
  724. InterlockedDecrement( (LONG *) &g_cMaxThreads );
  725. oldVal = TRUE;
  726. break;
  727. case AtqMaxConcurrency:
  728. oldVal = (ULONG_PTR)g_cConcurrency;
  729. g_cConcurrency = (DWORD)Data;
  730. break;
  731. case AtqThreadTimeout:
  732. oldVal = (ULONG_PTR)(g_msThreadTimeout/1000); // convert back to seconds
  733. g_msThreadTimeout = (DWORD)Data * 1000; // convert value to millisecs
  734. break;
  735. case AtqUseAcceptEx:
  736. oldVal = (ULONG_PTR)g_fUseAcceptEx;
  737. if ( !TsIsWindows95() ) {
  738. g_fUseAcceptEx = (DWORD)Data;
  739. }
  740. break;
  741. case AtqMinKbSec:
  742. //
  743. // Ignore it if the value is zero
  744. //
  745. if ( Data ) {
  746. oldVal = (ULONG_PTR)g_cbMinKbSec;
  747. g_cbMinKbSec = (DWORD)Data;
  748. }
  749. break;
  750. default:
  751. ATQ_ASSERT( FALSE );
  752. break;
  753. }
  754. return oldVal;
  755. } // AtqSetInfo()
  756. ULONG_PTR
  757. AtqGetInfo(
  758. IN ATQ_INFO atqInfo
  759. )
  760. /*++
  761. Routine Description:
  762. Gets various bits of information for the ATQ module
  763. Arguments:
  764. atqInfo - Data item to set
  765. Return Value:
  766. The old value of the parameter
  767. --*/
  768. {
  769. ULONG_PTR dwVal = 0;
  770. switch ( atqInfo ) {
  771. case AtqBandwidthThrottle:
  772. ATQ_ASSERT( g_pBandwidthInfo != NULL );
  773. dwVal = (ULONG_PTR ) g_pBandwidthInfo->QueryBandwidthLevel();
  774. break;
  775. case AtqExitThreadCallback:
  776. dwVal = (ULONG_PTR ) g_pfnExitThreadCallback;
  777. break;
  778. case AtqMaxPoolThreads:
  779. dwVal = (ULONG_PTR ) (g_cMaxThreads/g_cCPU);
  780. break;
  781. case AtqMaxConcurrency:
  782. dwVal = (ULONG_PTR ) g_cConcurrency;
  783. break;
  784. case AtqThreadTimeout:
  785. dwVal = (ULONG_PTR ) (g_msThreadTimeout/1000); // convert back to seconds
  786. break;
  787. case AtqUseAcceptEx:
  788. dwVal = (ULONG_PTR ) g_fUseAcceptEx;
  789. break;
  790. case AtqMinKbSec:
  791. dwVal = (ULONG_PTR ) g_cbMinKbSec;
  792. break;
  793. case AtqMaxThreadLimit:
  794. dwVal = (ULONG_PTR ) g_cMaxThreadLimit;
  795. break;
  796. case AtqAvailableThreads:
  797. dwVal = (ULONG_PTR) g_cAvailableThreads;
  798. break;
  799. default:
  800. ATQ_ASSERT( FALSE );
  801. break;
  802. } // switch
  803. return dwVal;
  804. } // AtqGetInfo()
  805. BOOL
  806. AtqGetStatistics(IN OUT ATQ_STATISTICS * pAtqStats)
  807. {
  808. if ( pAtqStats != NULL) {
  809. return g_pBandwidthInfo->GetStatistics( pAtqStats );
  810. } else {
  811. SetLastError( ERROR_INVALID_PARAMETER);
  812. return (FALSE);
  813. }
  814. } // AtqGetStatistics()
  815. BOOL
  816. AtqClearStatistics( VOID)
  817. {
  818. return g_pBandwidthInfo->ClearStatistics();
  819. } // AtqClearStatistics()
  820. ULONG_PTR
  821. AtqContextSetInfo(
  822. PATQ_CONTEXT patqContext,
  823. enum ATQ_CONTEXT_INFO atqInfo,
  824. ULONG_PTR Data
  825. )
  826. /*++
  827. Routine Description:
  828. Sets various bits of information for this context
  829. Arguments:
  830. patqContext - pointer to ATQ context
  831. atqInfo - Data item to set
  832. data - New value for item
  833. Return Value:
  834. The old value of the parameter
  835. --*/
  836. {
  837. PATQ_CONT pContext = (PATQ_CONT) patqContext;
  838. ULONG_PTR OldVal = 0;
  839. ATQ_ASSERT( pContext );
  840. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  841. if ( pContext && pContext->Signature == ATQ_CONTEXT_SIGNATURE )
  842. {
  843. switch ( atqInfo ) {
  844. case ATQ_INFO_TIMEOUT:
  845. OldVal = (ULONG_PTR)pContext->TimeOut;
  846. pContext->TimeOut = CanonTimeout( (DWORD)Data );
  847. break;
  848. case ATQ_INFO_RESUME_IO:
  849. //
  850. // set back the max timeout from pContext->TimeOut
  851. // This will ensure that timeout processing can go on
  852. // peacefully.
  853. //
  854. {
  855. DWORD currentTime = AtqGetCurrentTick( );
  856. DWORD timeout;
  857. OldVal = (ULONG_PTR)pContext->NextTimeout;
  858. timeout = pContext->TimeOut;
  859. //
  860. // Set the new timeout
  861. //
  862. I_SetNextTimeout(pContext);
  863. //
  864. // Return the old
  865. //
  866. if ( currentTime >= (DWORD)OldVal ) {
  867. ATQ_ASSERT((OldVal & ATQ_INFINITE) == 0);
  868. OldVal = 0;
  869. } else if ( (OldVal & ATQ_INFINITE) == 0 ) {
  870. OldVal -= currentTime;
  871. }
  872. // return correct units
  873. OldVal = (ULONG_PTR)UndoCanonTimeout( (DWORD)OldVal );
  874. }
  875. break;
  876. case ATQ_INFO_COMPLETION:
  877. OldVal = (ULONG_PTR)pContext->pfnCompletion;
  878. pContext->pfnCompletion = (ATQ_COMPLETION) Data;
  879. break;
  880. case ATQ_INFO_COMPLETION_CONTEXT:
  881. ATQ_ASSERT( Data != 0 ); // NULL context not allowed
  882. OldVal = (ULONG_PTR)pContext->ClientContext;
  883. pContext->ClientContext = (void *) Data;
  884. break;
  885. case ATQ_INFO_BANDWIDTH_INFO:
  886. {
  887. ATQ_ASSERT( Data != 0 );
  888. PBANDWIDTH_INFO pBandwidthInfo = (PBANDWIDTH_INFO) Data;
  889. ATQ_ASSERT( pBandwidthInfo->QuerySignature() ==
  890. ATQ_BW_INFO_SIGNATURE );
  891. if ( !pBandwidthInfo->IsFreed() )
  892. {
  893. pContext->m_pBandwidthInfo = (PBANDWIDTH_INFO) Data;
  894. pContext->m_pBandwidthInfo->Reference();
  895. }
  896. break;
  897. }
  898. case ATQ_INFO_ABORTIVE_CLOSE:
  899. OldVal = (ULONG_PTR)pContext->IsFlag( ACF_ABORTIVE_CLOSE );
  900. if ( Data )
  901. {
  902. pContext->SetFlag( ACF_ABORTIVE_CLOSE );
  903. }
  904. else
  905. {
  906. pContext->ResetFlag( ACF_ABORTIVE_CLOSE );
  907. }
  908. break;
  909. case ATQ_INFO_FORCE_CLOSE:
  910. OldVal = (ULONG_PTR)pContext->ForceClose();
  911. pContext->SetForceClose( Data ? TRUE : FALSE );
  912. break;
  913. default:
  914. ATQ_ASSERT( FALSE );
  915. }
  916. }
  917. return OldVal;
  918. } // AtqContextSetInfo()
  919. BOOL
  920. AtqAddAsyncHandle(
  921. PATQ_CONTEXT * ppatqContext,
  922. PVOID EndpointObject,
  923. PVOID ClientContext,
  924. ATQ_COMPLETION pfnCompletion,
  925. DWORD TimeOut,
  926. HANDLE hAsyncIO
  927. )
  928. /*++
  929. Routine Description:
  930. Adds a handle to the thread queue
  931. The client should call this after the IO handle is opened
  932. and before the first IO request is made
  933. Even in the case of failure, client should call AtqFreeContext() and
  934. free the memory associated with this object.
  935. Arguments:
  936. ppatqContext - Receives allocated ATQ Context
  937. Context - Context to call client with
  938. pfnCompletion - Completion to call when IO completes
  939. TimeOut - Time to wait (sec) for IO completion (INFINITE is valid)
  940. hAsyncIO - Handle with pending read or write
  941. Return Value:
  942. TRUE if successful, FALSE on error (call GetLastError)
  943. --*/
  944. {
  945. return ( I_AtqAddAsyncHandle( (PATQ_CONT *) ppatqContext,
  946. (PATQ_ENDPOINT) EndpointObject,
  947. ClientContext,
  948. pfnCompletion,
  949. TimeOut,
  950. hAsyncIO)
  951. &&
  952. I_AddAtqContextToPort( *((PATQ_CONT *) ppatqContext))
  953. );
  954. } // AtqAddAsyncHandle()
  955. VOID
  956. AtqGetAcceptExAddrs(
  957. IN PATQ_CONTEXT patqContext,
  958. OUT SOCKET * pSock,
  959. OUT PVOID * ppvBuff,
  960. OUT PVOID * pEndpointContext,
  961. OUT SOCKADDR * * ppsockaddrLocal,
  962. OUT SOCKADDR * * ppsockaddrRemote
  963. )
  964. {
  965. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  966. INT cbsockaddrLocal;
  967. INT cbsockaddrRemote;
  968. DWORD cb;
  969. ATQ_ASSERT( g_fUseAcceptEx);
  970. ATQ_ASSERT( pContext->pEndpoint);
  971. *pSock = HANDLE_TO_SOCKET(pContext->hAsyncIO);
  972. *pEndpointContext = pContext->pEndpoint->Context;
  973. //
  974. // The buffer not only receives the initial received data, it also
  975. // gets the sock addrs, which must be at least sockaddr_in + 16 bytes
  976. // large
  977. //
  978. g_pfnGetAcceptExSockaddrs( pContext->pvBuff,
  979. (cb = pContext->pEndpoint->InitialRecvSize),
  980. MIN_SOCKADDR_SIZE,
  981. MIN_SOCKADDR_SIZE,
  982. ppsockaddrLocal,
  983. &cbsockaddrLocal,
  984. ppsockaddrRemote,
  985. &cbsockaddrRemote );
  986. *ppvBuff = ( ( cb == 0) ? NULL : pContext->pvBuff);
  987. return;
  988. } // AtqGetAcceptExAddrs()
  989. BOOL
  990. AtqCloseSocket(
  991. PATQ_CONTEXT patqContext,
  992. BOOL fShutdown
  993. )
  994. /*++
  995. Routine Description:
  996. Closes the socket in this atq structure if it wasn't
  997. closed by transmitfile. This function should be called only
  998. if the embedded handle in AtqContext is a Socket.
  999. Arguments:
  1000. patqContext - Context whose socket should be closed.
  1001. fShutdown - If TRUE, means we call shutdown and always close the socket.
  1002. Note that if TransmitFile closed the socket, it will have done the
  1003. shutdown for us
  1004. Returns:
  1005. TRUE on success and FALSE if there is a failure.
  1006. --*/
  1007. {
  1008. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1009. if ( pContext ) {
  1010. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1011. BOOL fAbortiveClose;
  1012. fAbortiveClose = pContext->IsFlag( ACF_ABORTIVE_CLOSE );
  1013. pContext->ResetFlag( ACF_ABORTIVE_CLOSE );
  1014. //
  1015. // Don't delete the socket if we don't have to
  1016. //
  1017. if ( pContext->IsState( ACS_SOCK_UNCONNECTED |
  1018. ACS_SOCK_CLOSED) &&
  1019. !pContext->ForceClose()
  1020. ) {
  1021. //
  1022. // Do nothing
  1023. //
  1024. } else {
  1025. // default:
  1026. // case ACS_SOCK_LISTENING:
  1027. // case ACS_SOCK_CONNECTED: {
  1028. HANDLE hIO;
  1029. PATQ_ENDPOINT pEndpoint;
  1030. pEndpoint = pContext->pEndpoint;
  1031. pContext->MoveState( ACS_SOCK_CLOSED);
  1032. //
  1033. // During shutdown, the socket may be closed while this thread
  1034. // is doing processing, so only give a warning if any of the
  1035. // following fail
  1036. //
  1037. hIO = (HANDLE )InterlockedExchangePointer(
  1038. (PVOID *)&pContext->hAsyncIO,
  1039. NULL
  1040. );
  1041. if ( hIO == NULL ) {
  1042. //
  1043. // No socket - it is already closed - do nothing.
  1044. //
  1045. } else {
  1046. if (fAbortiveClose || fShutdown ) {
  1047. //
  1048. // If this is an AcceptEx socket, we must first force a
  1049. // user mode context update before we can call shutdown
  1050. //
  1051. if ( (pEndpoint != NULL) && (pEndpoint->UseAcceptEx) ) {
  1052. if ( setsockopt( HANDLE_TO_SOCKET(hIO),
  1053. SOL_SOCKET,
  1054. SO_UPDATE_ACCEPT_CONTEXT,
  1055. (char *) &pEndpoint->ListenSocket,
  1056. sizeof(SOCKET) ) == SOCKET_ERROR ) {
  1057. ATQ_PRINTF(( DBG_CONTEXT,
  1058. "[AtqCloseSocket] Warning- setsockopt "
  1059. "failed, error %d, socket = %x,"
  1060. " Context= %08x, Listen = %lx\n",
  1061. GetLastError(),
  1062. hIO,
  1063. pContext,
  1064. pEndpoint->ListenSocket ));
  1065. }
  1066. }
  1067. } // setsock-opt call
  1068. if ( fAbortiveClose ) {
  1069. LINGER linger;
  1070. linger.l_onoff = TRUE;
  1071. linger.l_linger = 0;
  1072. if ( setsockopt( HANDLE_TO_SOCKET(hIO),
  1073. SOL_SOCKET,
  1074. SO_LINGER,
  1075. (char *) &linger,
  1076. sizeof(linger) ) == SOCKET_ERROR
  1077. ) {
  1078. ATQ_PRINTF(( DBG_CONTEXT,
  1079. "[AtqCloseSocket] Warning- setsockopt "
  1080. "failed, error %d, socket = %x,"
  1081. " Context= %08x, Listen = %lx\n",
  1082. GetLastError(),
  1083. hIO,
  1084. pContext,
  1085. pEndpoint->ListenSocket ));
  1086. }
  1087. else {
  1088. ATQ_PRINTF(( DBG_CONTEXT,
  1089. "[AtqCloseSocket(%08x)] requested"
  1090. " abortive close\n",
  1091. pContext));
  1092. }
  1093. } // set up linger
  1094. if ( fShutdown ) {
  1095. //
  1096. // Note that shutdown can fail in instances where the
  1097. // client aborts in the middle of a TransmitFile.
  1098. // This is an acceptable failure case
  1099. //
  1100. shutdown( HANDLE_TO_SOCKET(hIO), 1 );
  1101. }
  1102. DBG_ASSERT( hIO != NULL);
  1103. if ( closesocket( HANDLE_TO_SOCKET(hIO) ) ) {
  1104. ATQ_PRINTF(( DBG_CONTEXT,
  1105. "[AtqCloseSocket] Warning- closesocket "
  1106. " failed, Context = %08x, error %d,"
  1107. " socket = %x\n",
  1108. pContext,
  1109. GetLastError(),
  1110. hIO ));
  1111. }
  1112. } // if (hIO != NULL)
  1113. }
  1114. return TRUE;
  1115. }
  1116. DBGPRINTF(( DBG_CONTEXT, "[AtqCloseSocket] Warning - NULL Atq context\n"));
  1117. SetLastError( ERROR_INVALID_PARAMETER );
  1118. return FALSE;
  1119. } // AtqCloseSocket()
  1120. BOOL
  1121. AtqCloseFileHandle(
  1122. PATQ_CONTEXT patqContext
  1123. )
  1124. /*++
  1125. Routine Description:
  1126. Closes the file handle in this atq structure.
  1127. This function should be called only if the embedded handle
  1128. in AtqContext is a file handle.
  1129. Arguments:
  1130. patqContext - Context whose file handle should be closed.
  1131. Returns:
  1132. TRUE on success and FALSE if there is a failure.
  1133. Note:
  1134. THIS FUNCTIONALITY IS ADDED TO SERVE A SPECIAL REQUEST!!!
  1135. Most of the ATQ code thinks that the handle here is a socket.
  1136. Except of course this function...
  1137. --*/
  1138. {
  1139. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1140. if ( pContext != NULL ) {
  1141. HANDLE hIO;
  1142. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1143. ATQ_ASSERT( !pContext->IsAcceptExRootContext());
  1144. ATQ_ASSERT( !TsIsWindows95() ); // NYI
  1145. hIO =
  1146. (HANDLE ) InterlockedExchangePointer(
  1147. (PVOID *)&pContext->hAsyncIO,
  1148. NULL
  1149. );
  1150. if ( (hIO == NULL) || !CloseHandle( hIO ) ) {
  1151. ATQ_PRINTF(( DBG_CONTEXT,
  1152. "[AtqCloseFileHandle] Warning- CloseHandle failed, "
  1153. " Context = %08x, error %d, handle = %x\n",
  1154. pContext,
  1155. GetLastError(),
  1156. hIO ));
  1157. }
  1158. return TRUE;
  1159. }
  1160. DBGPRINTF(( DBG_CONTEXT, "[AtqCloseSocket] Warning - NULL Atq context\n"));
  1161. SetLastError( ERROR_INVALID_PARAMETER );
  1162. return FALSE;
  1163. } // AtqCloseFileHandle()
  1164. VOID
  1165. AtqFreeContext(
  1166. PATQ_CONTEXT patqContext,
  1167. BOOL fReuseContext
  1168. )
  1169. /*++
  1170. Routine Description:
  1171. Frees the context created in AtqAddAsyncHandle.
  1172. Call this after the async handle has been closed and all outstanding
  1173. IO operations have been completed. The context is invalid after this call.
  1174. Call AtqFreeContext() for same context only ONCE.
  1175. Arguments:
  1176. patqContext - Context to free
  1177. fReuseContext - TRUE if this can context can be reused in the context of
  1178. the calling thread. Should be FALSE if the calling thread will exit
  1179. soon (i.e., isn't an AtqPoolThread).
  1180. --*/
  1181. {
  1182. PATQ_CONT pContext = (PATQ_CONT)patqContext;
  1183. ATQ_ASSERT( pContext != NULL );
  1184. IF_DEBUG( API_ENTRY) {
  1185. ATQ_PRINTF(( DBG_CONTEXT, "AtqFreeContext( %08x (handle=%08x,"
  1186. " nIOs = %d), fReuse=%d)\n",
  1187. patqContext, patqContext->hAsyncIO,
  1188. pContext->m_nIO,
  1189. fReuseContext));
  1190. }
  1191. if ( pContext ) {
  1192. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1193. if ( fReuseContext ) {
  1194. pContext->SetFlag( ACF_REUSE_CONTEXT);
  1195. } else {
  1196. pContext->ResetFlag( ACF_REUSE_CONTEXT);
  1197. }
  1198. if ( InterlockedDecrement( &pContext->m_nIO) == 0) {
  1199. //
  1200. // The number of outstanding ref holders is ZERO.
  1201. // Free up this ATQ context.
  1202. //
  1203. // We really do not free up the context - but try to reuse
  1204. // it if possible
  1205. //
  1206. DBG_ASSERT( pContext->lSyncTimeout == 0);
  1207. AtqpReuseOrFreeContext( pContext, fReuseContext);
  1208. }
  1209. }
  1210. return;
  1211. } // AtqFreeContext()
  1212. BOOL
  1213. AtqReadFile(
  1214. IN PATQ_CONTEXT patqContext,
  1215. IN LPVOID lpBuffer,
  1216. IN DWORD BytesToRead,
  1217. IN OVERLAPPED * lpo OPTIONAL
  1218. )
  1219. /*++
  1220. Routine Description:
  1221. Does an async read using the handle defined in the context.
  1222. Arguments:
  1223. patqContext - pointer to ATQ context
  1224. lpBuffer - Buffer to put read data in
  1225. BytesToRead - number of bytes to read
  1226. lpo - Overlapped structure to use
  1227. Returns:
  1228. TRUE on success and FALSE if there is a failure.
  1229. --*/
  1230. {
  1231. BOOL fRes;
  1232. DWORD cbRead; // discarded after usage ( since this is Async)
  1233. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1234. PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
  1235. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1236. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1237. ATQ_ASSERT( !TsIsWindows95() ); // NYI
  1238. ATQ_ASSERT( pBandwidthInfo != NULL );
  1239. ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  1240. InterlockedIncrement( &pContext->m_nIO);
  1241. I_SetNextTimeout(pContext);
  1242. pContext->BytesSent = 0;
  1243. if ( !lpo ) {
  1244. lpo = &pContext->Overlapped;
  1245. }
  1246. switch ( pBandwidthInfo->QueryStatus( AtqIoRead ) ) {
  1247. case StatusAllowOperation:
  1248. pBandwidthInfo->IncTotalAllowedRequests();
  1249. fRes = ( ReadFile( pContext->hAsyncIO,
  1250. lpBuffer,
  1251. BytesToRead,
  1252. &cbRead,
  1253. lpo ) ||
  1254. GetLastError() == ERROR_IO_PENDING);
  1255. if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
  1256. break;
  1257. case StatusBlockOperation:
  1258. // store data for restarting the operation.
  1259. pContext->arInfo.atqOp = AtqIoRead;
  1260. pContext->arInfo.lpOverlapped = lpo;
  1261. pContext->arInfo.uop.opReadWrite.buf1.len = BytesToRead;
  1262. pContext->arInfo.uop.opReadWrite.buf1.buf = (CHAR * ) lpBuffer;
  1263. pContext->arInfo.uop.opReadWrite.dwBufferCount = 1;
  1264. pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
  1265. // Put this request in queue of blocked requests.
  1266. fRes = pBandwidthInfo->BlockRequest( pContext );
  1267. if ( fRes )
  1268. {
  1269. pBandwidthInfo->IncTotalBlockedRequests();
  1270. break;
  1271. }
  1272. // fall through
  1273. case StatusRejectOperation:
  1274. InterlockedDecrement( &pContext->m_nIO);
  1275. pBandwidthInfo->IncTotalRejectedRequests();
  1276. SetLastError( ERROR_NETWORK_BUSY);
  1277. fRes = FALSE;
  1278. break;
  1279. default:
  1280. ATQ_ASSERT( FALSE);
  1281. InterlockedDecrement( &pContext->m_nIO);
  1282. SetLastError( ERROR_INVALID_PARAMETER);
  1283. fRes = FALSE;
  1284. break;
  1285. } // switch()
  1286. return fRes;
  1287. } // AtqReadFile()
  1288. BOOL
  1289. AtqReadSocket(
  1290. IN PATQ_CONTEXT patqContext,
  1291. IN LPWSABUF pwsaBuffers,
  1292. IN DWORD dwBufferCount,
  1293. IN OVERLAPPED * lpo OPTIONAL
  1294. )
  1295. /*++
  1296. Routine Description:
  1297. Does an async recv using the handle defined in the context
  1298. as a socket.
  1299. Arguments:
  1300. patqContext - pointer to ATQ context
  1301. lpBuffer - Buffer to put read data in
  1302. BytesToRead - number of bytes to read
  1303. lpo - Overlapped structure to use
  1304. Returns:
  1305. TRUE on success and FALSE if there is a failure.
  1306. --*/
  1307. {
  1308. BOOL fRes;
  1309. DWORD cbRead; // discarded after usage ( since this is Async)
  1310. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1311. PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
  1312. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1313. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1314. ATQ_ASSERT( pBandwidthInfo != NULL );
  1315. ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  1316. IF_DEBUG(API_ENTRY) {
  1317. ATQ_PRINTF(( DBG_CONTEXT,
  1318. "AtqReadSocket(%08lx) called.\n", pContext));
  1319. }
  1320. if (pContext->IsFlag( ACF_RECV_ISSUED)) {
  1321. IF_DEBUG( SPUD ) {
  1322. ATQ_PRINTF(( DBG_CONTEXT,
  1323. "AtqReadSocket -> WSARecv bypassed.\n"));
  1324. }
  1325. pContext->BytesSent = 0;
  1326. pContext->SetFlag( ACF_RECV_CALLED);
  1327. #if CC_REF_TRACKING
  1328. //
  1329. // ATQ notification trace
  1330. //
  1331. // Notify client context of all non-oplock notification.
  1332. // This is for debugging purpose only.
  1333. //
  1334. // Code 0xf9f9f9f9 indicates a ACF_RECV_CALLED is set in the flags field
  1335. //
  1336. pContext->NotifyIOCompletion( (ULONG_PTR)pContext, pContext->m_acFlags, 0xf9f9f9f9 );
  1337. #endif
  1338. return TRUE;
  1339. }
  1340. I_SetNextTimeout(pContext);
  1341. // count the number of bytes
  1342. DBG_ASSERT( dwBufferCount >= 1);
  1343. pContext->BytesSent = 0;
  1344. InterlockedIncrement( &pContext->m_nIO);
  1345. if ( !lpo ) {
  1346. lpo = &pContext->Overlapped;
  1347. }
  1348. //
  1349. // NYI: Create an optimal function table
  1350. //
  1351. if ( !g_fUseFakeCompletionPort &&
  1352. (StatusAllowOperation == pBandwidthInfo->QueryStatus( AtqIoRead ) ) ) {
  1353. DWORD lpFlags = 0;
  1354. fRes = ( (WSARecv( HANDLE_TO_SOCKET(pContext->hAsyncIO),
  1355. pwsaBuffers,
  1356. dwBufferCount,
  1357. &cbRead,
  1358. &lpFlags, // no flags
  1359. lpo,
  1360. NULL // no completion routine
  1361. ) == 0) ||
  1362. (WSAGetLastError() == WSA_IO_PENDING));
  1363. if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
  1364. } else {
  1365. switch ( pBandwidthInfo->QueryStatus( AtqIoRead ) ) {
  1366. case StatusAllowOperation:
  1367. DBG_ASSERT(g_fUseFakeCompletionPort);
  1368. fRes = SIOWSARecv(
  1369. pContext,
  1370. pwsaBuffers,
  1371. dwBufferCount,
  1372. lpo
  1373. );
  1374. break;
  1375. case StatusBlockOperation:
  1376. // store data for restarting the operation.
  1377. pContext->arInfo.atqOp = AtqIoRead;
  1378. pContext->arInfo.lpOverlapped = lpo;
  1379. pContext->arInfo.uop.opReadWrite.dwBufferCount = dwBufferCount;
  1380. if ( dwBufferCount == 1) {
  1381. pContext->arInfo.uop.opReadWrite.buf1.len = pwsaBuffers->len;
  1382. pContext->arInfo.uop.opReadWrite.buf1.buf = pwsaBuffers->buf;
  1383. pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
  1384. } else {
  1385. DBG_ASSERT( dwBufferCount > 1);
  1386. //
  1387. // Inefficient: But we will burn CPU for b/w throttling.
  1388. //
  1389. WSABUF * pBuf = (WSABUF *)
  1390. ::LocalAlloc( LPTR, dwBufferCount * sizeof (WSABUF));
  1391. if ( NULL != pBuf) {
  1392. pContext->arInfo.uop.opReadWrite.pBufAll = pBuf;
  1393. CopyMemory( pBuf, pwsaBuffers,
  1394. dwBufferCount * sizeof(WSABUF));
  1395. } else {
  1396. InterlockedDecrement( &pContext->m_nIO);
  1397. fRes = FALSE;
  1398. break;
  1399. }
  1400. }
  1401. // Put this request in queue of blocked requests.
  1402. fRes = pBandwidthInfo->BlockRequest( pContext );
  1403. if ( fRes )
  1404. {
  1405. pBandwidthInfo->IncTotalBlockedRequests();
  1406. break;
  1407. }
  1408. // fall through
  1409. case StatusRejectOperation:
  1410. InterlockedDecrement( &pContext->m_nIO);
  1411. pBandwidthInfo->IncTotalRejectedRequests();
  1412. SetLastError( ERROR_NETWORK_BUSY);
  1413. fRes = FALSE;
  1414. break;
  1415. default:
  1416. ATQ_ASSERT( FALSE);
  1417. InterlockedDecrement( &pContext->m_nIO);
  1418. SetLastError( ERROR_INVALID_PARAMETER);
  1419. fRes = FALSE;
  1420. break;
  1421. } // switch()
  1422. }
  1423. return fRes;
  1424. } // AtqReadSocket()
  1425. BOOL
  1426. AtqWriteFile(
  1427. IN PATQ_CONTEXT patqContext,
  1428. IN LPCVOID lpBuffer,
  1429. IN DWORD BytesToWrite,
  1430. IN OVERLAPPED * lpo OPTIONAL
  1431. )
  1432. /*++
  1433. Routine Description:
  1434. Does an async write using the handle defined in the context.
  1435. Arguments:
  1436. patqContext - pointer to ATQ context
  1437. lpBuffer - Buffer to write
  1438. BytesToWrite - number of bytes to write
  1439. lpo - Overlapped structure to use
  1440. Returns:
  1441. TRUE on success and FALSE if there is a failure.
  1442. --*/
  1443. {
  1444. BOOL fRes;
  1445. DWORD cbWritten; // discarded after usage ( since this is Async)
  1446. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1447. PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
  1448. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1449. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1450. ATQ_ASSERT( !TsIsWindows95() ); // NYI
  1451. ATQ_ASSERT( pBandwidthInfo != NULL );
  1452. ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  1453. I_SetNextTimeout(pContext);
  1454. pContext->BytesSent = BytesToWrite;
  1455. if ( !lpo ) {
  1456. lpo = &pContext->Overlapped;
  1457. }
  1458. InterlockedIncrement( &pContext->m_nIO);
  1459. switch ( pBandwidthInfo->QueryStatus( AtqIoWrite) ) {
  1460. case StatusAllowOperation:
  1461. pBandwidthInfo->IncTotalAllowedRequests();
  1462. fRes = ( WriteFile( pContext->hAsyncIO,
  1463. lpBuffer,
  1464. BytesToWrite,
  1465. &cbWritten,
  1466. lpo ) ||
  1467. GetLastError() == ERROR_IO_PENDING);
  1468. if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
  1469. break;
  1470. case StatusBlockOperation:
  1471. // store data for restarting the operation.
  1472. pContext->arInfo.atqOp = AtqIoWrite;
  1473. pContext->arInfo.lpOverlapped = lpo;
  1474. pContext->arInfo.uop.opReadWrite.buf1.len = BytesToWrite;
  1475. pContext->arInfo.uop.opReadWrite.buf1.buf = (CHAR * ) lpBuffer;
  1476. pContext->arInfo.uop.opReadWrite.dwBufferCount = 1;
  1477. pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
  1478. // Put this request in queue of blocked requests.
  1479. fRes = pBandwidthInfo->BlockRequest( pContext );
  1480. if ( fRes )
  1481. {
  1482. pBandwidthInfo->IncTotalBlockedRequests();
  1483. break;
  1484. }
  1485. // fall through
  1486. case StatusRejectOperation:
  1487. InterlockedDecrement( &pContext->m_nIO);
  1488. pBandwidthInfo->IncTotalRejectedRequests();
  1489. SetLastError( ERROR_NETWORK_BUSY);
  1490. fRes = FALSE;
  1491. break;
  1492. default:
  1493. ATQ_ASSERT( FALSE);
  1494. InterlockedDecrement( &pContext->m_nIO);
  1495. SetLastError( ERROR_INVALID_PARAMETER);
  1496. fRes = FALSE;
  1497. break;
  1498. } // switch()
  1499. return fRes;
  1500. } // AtqWriteFile()
  1501. BOOL
  1502. AtqWriteSocket(
  1503. IN PATQ_CONTEXT patqContext,
  1504. IN LPWSABUF pwsaBuffers,
  1505. IN DWORD dwBufferCount,
  1506. IN OVERLAPPED * lpo OPTIONAL
  1507. )
  1508. /*++
  1509. Routine Description:
  1510. Does an async write using the handle defined in the context as a socket.
  1511. Arguments:
  1512. patqContext - pointer to ATQ context
  1513. pwsaBuffer - pointer to Winsock Buffers for scatter/gather
  1514. dwBufferCount - DWORD containing the count of buffers pointed
  1515. to by pwsaBuffer
  1516. lpo - Overlapped structure to use
  1517. Returns:
  1518. TRUE on success and FALSE if there is a failure.
  1519. --*/
  1520. {
  1521. BOOL fRes;
  1522. DWORD cbWritten; // discarded after usage ( since this is Async)
  1523. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1524. PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
  1525. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1526. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1527. ATQ_ASSERT( pBandwidthInfo != NULL );
  1528. ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  1529. I_SetNextTimeout(pContext);
  1530. //
  1531. // count the number of bytes
  1532. //
  1533. DBG_ASSERT( dwBufferCount >= 1);
  1534. pContext->BytesSent = pwsaBuffers->len;
  1535. if ( dwBufferCount > 1) {
  1536. LPWSABUF pWsaBuf;
  1537. for ( pWsaBuf = pwsaBuffers + 1;
  1538. pWsaBuf < (pwsaBuffers + dwBufferCount);
  1539. pWsaBuf++) {
  1540. pContext->BytesSent += pWsaBuf->len;
  1541. }
  1542. }
  1543. if ( lpo == NULL ) {
  1544. lpo = &pContext->Overlapped;
  1545. }
  1546. InterlockedIncrement( &pContext->m_nIO);
  1547. if ( !g_fUseFakeCompletionPort &&
  1548. (StatusAllowOperation ==
  1549. pBandwidthInfo->QueryStatus( AtqIoWrite ) ) ) {
  1550. pBandwidthInfo->IncTotalAllowedRequests();
  1551. fRes = ( (WSASend( HANDLE_TO_SOCKET(pContext->hAsyncIO),
  1552. pwsaBuffers,
  1553. dwBufferCount,
  1554. &cbWritten,
  1555. 0, // no flags
  1556. lpo,
  1557. NULL // no completion routine
  1558. ) == 0) ||
  1559. (WSAGetLastError() == WSA_IO_PENDING));
  1560. if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
  1561. } else {
  1562. switch ( pBandwidthInfo->QueryStatus( AtqIoWrite ) ) {
  1563. case StatusAllowOperation:
  1564. pBandwidthInfo->IncTotalAllowedRequests();
  1565. DBG_ASSERT(g_fUseFakeCompletionPort);
  1566. fRes = SIOWSASend(
  1567. pContext,
  1568. pwsaBuffers,
  1569. dwBufferCount,
  1570. lpo
  1571. );
  1572. break;
  1573. case StatusBlockOperation:
  1574. // store data for restarting the operation.
  1575. pContext->arInfo.atqOp = AtqIoWrite;
  1576. pContext->arInfo.lpOverlapped = lpo;
  1577. pContext->arInfo.uop.opReadWrite.dwBufferCount = dwBufferCount;
  1578. if ( dwBufferCount == 1) {
  1579. pContext->arInfo.uop.opReadWrite.buf1.len = pwsaBuffers->len;
  1580. pContext->arInfo.uop.opReadWrite.buf1.buf = pwsaBuffers->buf;
  1581. pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
  1582. } else {
  1583. DBG_ASSERT( dwBufferCount > 1);
  1584. //
  1585. // Inefficient: But we will burn CPU for b/w throttling.
  1586. //
  1587. WSABUF * pBuf = (WSABUF *)
  1588. ::LocalAlloc( LPTR, dwBufferCount * sizeof (WSABUF));
  1589. if ( NULL != pBuf) {
  1590. pContext->arInfo.uop.opReadWrite.pBufAll = pBuf;
  1591. CopyMemory( pBuf, pwsaBuffers,
  1592. dwBufferCount * sizeof(WSABUF));
  1593. } else {
  1594. InterlockedDecrement( &pContext->m_nIO);
  1595. fRes = FALSE;
  1596. break;
  1597. }
  1598. }
  1599. // Put this request in queue of blocked requests.
  1600. fRes = pBandwidthInfo->BlockRequest( pContext );
  1601. if ( fRes )
  1602. {
  1603. pBandwidthInfo->IncTotalBlockedRequests();
  1604. break;
  1605. }
  1606. // fall through
  1607. case StatusRejectOperation:
  1608. InterlockedDecrement( &pContext->m_nIO);
  1609. pBandwidthInfo->IncTotalRejectedRequests();
  1610. SetLastError( ERROR_NETWORK_BUSY);
  1611. fRes = FALSE;
  1612. break;
  1613. default:
  1614. ATQ_ASSERT( FALSE);
  1615. InterlockedDecrement( &pContext->m_nIO);
  1616. SetLastError( ERROR_INVALID_PARAMETER);
  1617. fRes = FALSE;
  1618. break;
  1619. } // switch()
  1620. }
  1621. return fRes;
  1622. } // AtqWriteSocket()
  1623. BOOL
  1624. AtqSyncWsaSend(
  1625. IN PATQ_CONTEXT patqContext,
  1626. IN LPWSABUF pwsaBuffers,
  1627. IN DWORD dwBufferCount,
  1628. OUT LPDWORD pcbWritten
  1629. )
  1630. /*++
  1631. Routine Description:
  1632. Does a sync write of an array of wsa buffers using WSASend.
  1633. Arguments:
  1634. patqContext - pointer to ATQ context
  1635. pwsaBuffer - pointer to Winsock Buffers for scatter/gather
  1636. dwBufferCount - DWORD containing the count of buffers pointed
  1637. to by pwsaBuffer
  1638. pcbWritten - ptr to count of bytes written
  1639. Returns:
  1640. TRUE on success and FALSE if there is a failure.
  1641. --*/
  1642. {
  1643. BOOL fRes = FALSE;
  1644. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1645. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1646. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1647. fRes = ( WSASend( HANDLE_TO_SOCKET(pContext->hAsyncIO),
  1648. pwsaBuffers,
  1649. dwBufferCount,
  1650. pcbWritten,
  1651. 0, // no flags
  1652. NULL, // lpo == NULL for sync write
  1653. NULL // no completion routine
  1654. ) == 0);
  1655. return fRes;
  1656. } // AtqSyncWsaSend()
  1657. BOOL
  1658. AtqTransmitFile(
  1659. IN PATQ_CONTEXT patqContext,
  1660. IN HANDLE hFile,
  1661. IN DWORD dwBytesInFile,
  1662. IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
  1663. IN DWORD dwFlags
  1664. )
  1665. /*++
  1666. Routine Description:
  1667. Does a TransmitFile using the handle defined in the context.
  1668. Arguments:
  1669. patqContext - pointer to ATQ context
  1670. hFile - handle of file to read from
  1671. dwBytesInFile - Bytes to transmit
  1672. lpTransmitBuffers - transmit buffer structure
  1673. dwFlags - Transmit file flags
  1674. Returns:
  1675. TRUE on success and FALSE if there is a failure.
  1676. --*/
  1677. {
  1678. BOOL fRes;
  1679. PATQ_CONT pContext = (PATQ_CONT) patqContext;
  1680. PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
  1681. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1682. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1683. ATQ_ASSERT( pBandwidthInfo != NULL );
  1684. ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  1685. //
  1686. // For large file sends, the client's default timeout may not be
  1687. // adequte for slow links. Scale based on bytes being sent
  1688. //
  1689. I_SetNextTimeout(pContext);
  1690. pContext->BytesSent = dwBytesInFile;
  1691. if ( dwFlags == 0 ) {
  1692. //
  1693. // If no flags are set, then we can attempt to use the special
  1694. // write-behind flag. This flag can cause the TransmitFile to
  1695. // complete immediately, before the send actually completes.
  1696. // This can be a significant performance improvement inside the
  1697. // system.
  1698. //
  1699. dwFlags = TF_WRITE_BEHIND;
  1700. } else if ( dwFlags & TF_DISCONNECT ) {
  1701. //
  1702. // If the socket is getting disconnected, mark it appropriately
  1703. //
  1704. pContext->MoveState( ( ( dwFlags & TF_REUSE_SOCKET )?
  1705. ACS_SOCK_UNCONNECTED:
  1706. ACS_SOCK_CLOSED
  1707. )
  1708. );
  1709. }
  1710. //
  1711. // Use kernel apc flag unless configured not to
  1712. //
  1713. if ( g_fUseKernelApc ) {
  1714. dwFlags |= TF_USE_KERNEL_APC;
  1715. }
  1716. InterlockedIncrement( &pContext->m_nIO);
  1717. if ( !g_fUseFakeCompletionPort &&
  1718. (StatusAllowOperation == pBandwidthInfo->QueryStatus( AtqIoXmitFile ) ) ) {
  1719. pBandwidthInfo->IncTotalAllowedRequests();
  1720. fRes = (g_pfnTransmitFile( HANDLE_TO_SOCKET(pContext->hAsyncIO),
  1721. hFile,
  1722. dwBytesInFile,
  1723. 0,
  1724. &pContext->Overlapped,
  1725. lpTransmitBuffers,
  1726. dwFlags ) ||
  1727. (GetLastError() == ERROR_IO_PENDING));
  1728. if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
  1729. } else {
  1730. switch ( pBandwidthInfo->QueryStatus( AtqIoXmitFile ) ) {
  1731. case StatusAllowOperation:
  1732. DBG_ASSERT(g_fUseFakeCompletionPort);
  1733. pBandwidthInfo->IncTotalAllowedRequests();
  1734. fRes = SIOTransmitFile(
  1735. pContext,
  1736. hFile,
  1737. dwBytesInFile,
  1738. lpTransmitBuffers
  1739. );
  1740. break;
  1741. case StatusBlockOperation:
  1742. // store data for restarting the operation.
  1743. pContext->arInfo.atqOp = AtqIoXmitFile;
  1744. pContext->arInfo.lpOverlapped = &pContext->Overlapped;
  1745. pContext->arInfo.uop.opXmit.hFile = hFile;
  1746. pContext->arInfo.uop.opXmit.dwBytesInFile = dwBytesInFile;
  1747. pContext->arInfo.uop.opXmit.lpXmitBuffers = lpTransmitBuffers;
  1748. pContext->arInfo.uop.opXmit.dwFlags = dwFlags;
  1749. // Put this request in queue of blocked requests.
  1750. fRes = pBandwidthInfo->BlockRequest( pContext);
  1751. if ( fRes )
  1752. {
  1753. pBandwidthInfo->IncTotalBlockedRequests();
  1754. break;
  1755. }
  1756. // fall through
  1757. case StatusRejectOperation:
  1758. InterlockedDecrement( &pContext->m_nIO);
  1759. pBandwidthInfo->IncTotalRejectedRequests();
  1760. SetLastError( ERROR_NETWORK_BUSY);
  1761. fRes = FALSE;
  1762. break;
  1763. default:
  1764. ATQ_ASSERT( FALSE);
  1765. InterlockedDecrement( &pContext->m_nIO);
  1766. SetLastError( ERROR_INVALID_PARAMETER);
  1767. fRes = FALSE;
  1768. break;
  1769. } // switch()
  1770. }
  1771. //
  1772. // Restore the socket state if we failed so that the handle gets freed
  1773. //
  1774. if ( !fRes )
  1775. {
  1776. pContext->MoveState( ACS_SOCK_CONNECTED);
  1777. }
  1778. return fRes;
  1779. } // AtqTransmitFile()
  1780. BOOL
  1781. AtqTransmitFileEx(
  1782. IN PATQ_CONTEXT patqContext,
  1783. IN HANDLE hFile,
  1784. IN DWORD dwBytesInFile,
  1785. IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
  1786. IN DWORD dwFlags,
  1787. IN OVERLAPPED * lpo
  1788. )
  1789. /*++
  1790. Routine Description:
  1791. Does a TransmitFile using the handle defined in the context.
  1792. Uses the parameter lpo instead of the structure in the context.
  1793. Arguments:
  1794. patqContext - pointer to ATQ context
  1795. hFile - handle of file to read from
  1796. dwBytesInFile - Bytes to transmit
  1797. lpTransmitBuffers - transmit buffer structure
  1798. dwFlags - Transmit file flags
  1799. lpo - overlapped structure
  1800. Returns:
  1801. TRUE on success and FALSE if there is a failure.
  1802. --*/
  1803. {
  1804. BOOL fRes = TRUE;
  1805. PATQ_CONT pContext = (PATQ_CONT) patqContext;
  1806. PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
  1807. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1808. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1809. ATQ_ASSERT( pBandwidthInfo != NULL );
  1810. ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  1811. //
  1812. // For large file sends, the client's default timeout may not be
  1813. // adequte for slow links. Scale based on bytes being sent
  1814. //
  1815. I_SetNextTimeout(pContext);
  1816. pContext->BytesSent = dwBytesInFile;
  1817. if ( dwFlags == 0 ) {
  1818. //
  1819. // If no flags are set, then we can attempt to use the special
  1820. // write-behind flag. This flag can cause the TransmitFile to
  1821. // complete immediately, before the send actually completes.
  1822. // This can be a significant performance improvement inside the
  1823. // system.
  1824. //
  1825. dwFlags = TF_WRITE_BEHIND;
  1826. } else if ( dwFlags & TF_DISCONNECT ) {
  1827. //
  1828. // If the socket is getting disconnected, mark it appropriately
  1829. //
  1830. pContext->MoveState( ( ( dwFlags & TF_REUSE_SOCKET )?
  1831. ACS_SOCK_UNCONNECTED:
  1832. ACS_SOCK_CLOSED
  1833. )
  1834. );
  1835. }
  1836. InterlockedIncrement( &pContext->m_nIO);
  1837. if ( !g_fUseFakeCompletionPort &&
  1838. (StatusAllowOperation == pBandwidthInfo->QueryStatus( AtqIoXmitFile ) ) ) {
  1839. pBandwidthInfo->IncTotalAllowedRequests();
  1840. fRes = (g_pfnTransmitFile( HANDLE_TO_SOCKET(pContext->hAsyncIO),
  1841. hFile,
  1842. dwBytesInFile,
  1843. 0,
  1844. (lpo == NULL) ? &pContext->Overlapped : lpo,
  1845. lpTransmitBuffers,
  1846. dwFlags ) ||
  1847. (GetLastError() == ERROR_IO_PENDING));
  1848. if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
  1849. } else {
  1850. ASSERT(FALSE);
  1851. fRes = FALSE;
  1852. }
  1853. //
  1854. // Restore the socket state if we failed so that the handle gets freed
  1855. //
  1856. if ( !fRes )
  1857. {
  1858. pContext->MoveState( ACS_SOCK_CONNECTED);
  1859. }
  1860. return fRes;
  1861. } // AtqTransmitFileEx()
  1862. BOOL
  1863. AtqReadDirChanges(IN PATQ_CONTEXT patqContext,
  1864. IN LPVOID lpBuffer,
  1865. IN DWORD BytesToRead,
  1866. IN BOOL fWatchSubDir,
  1867. IN DWORD dwNotifyFilter,
  1868. IN OVERLAPPED * lpo
  1869. )
  1870. /*++
  1871. AtqReadDirChanges()
  1872. Description:
  1873. This function submits an Async ReadDirectoryChanges() call for
  1874. the Async handle in the ATQ context supplied.
  1875. It always requires a non-NULL overlapped pointer for processing
  1876. this call.
  1877. Arguments:
  1878. patqContext - pointer to ATQ Context
  1879. lpBuffer - buffer for the data to be read from ReadDirectoryChanges()
  1880. BytesToRead - count of bytes to read into buffer
  1881. fWatchSubDir - should we watch for sub directory changes
  1882. dwNotifyFilter - DWORD containing the flags for Notification
  1883. lpo - pointer to overlapped structure.
  1884. Returns:
  1885. TRUE if ReadDirectoryChanges() is successfully submitted.
  1886. FALSE if there is any failure in submitting IO.
  1887. --*/
  1888. {
  1889. BOOL fRes;
  1890. DWORD cbRead; // discarded after usage ( since this is Async)
  1891. PATQ_CONT pContext = (PATQ_CONT ) patqContext;
  1892. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  1893. ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
  1894. if ( g_pfnReadDirChangesW == NULL ) {
  1895. ATQ_PRINTF((DBG_CONTEXT,"ReadDirChanges entry point NULL\n"));
  1896. SetLastError(ERROR_NOT_SUPPORTED);
  1897. return(FALSE);
  1898. }
  1899. if ( lpo == NULL ) {
  1900. SetLastError(ERROR_INVALID_PARAMETER);
  1901. return FALSE;
  1902. }
  1903. I_SetNextTimeout(pContext);
  1904. pContext->BytesSent = 0;
  1905. InterlockedIncrement( &pContext->m_nIO);
  1906. fRes = g_pfnReadDirChangesW( pContext->hAsyncIO,
  1907. lpBuffer,
  1908. BytesToRead,
  1909. fWatchSubDir,
  1910. dwNotifyFilter,
  1911. &cbRead,
  1912. lpo,
  1913. NULL);
  1914. if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
  1915. return fRes;
  1916. } // AtqReadDirChanges()
  1917. BOOL
  1918. AtqPostCompletionStatus(
  1919. IN PATQ_CONTEXT patqContext,
  1920. IN DWORD BytesTransferred
  1921. )
  1922. /*++
  1923. Routine Description:
  1924. Posts a completion status on the completion port queue
  1925. An IO pending error code is treated as a success error code
  1926. Arguments:
  1927. patqContext - pointer to ATQ context
  1928. Everything else as in the Win32 API
  1929. NOTES:
  1930. Return Value:
  1931. TRUE if successful, FALSE on error (call GetLastError)
  1932. --*/
  1933. {
  1934. BOOL fRes;
  1935. PATQ_CONT pAtqContext = (PATQ_CONT ) patqContext;
  1936. PBANDWIDTH_INFO pBandwidthInfo = pAtqContext->m_pBandwidthInfo;
  1937. ATQ_ASSERT( (pAtqContext)->Signature == ATQ_CONTEXT_SIGNATURE );
  1938. ATQ_ASSERT( pBandwidthInfo != NULL );
  1939. ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  1940. if ( !pAtqContext->IsBlocked()) {
  1941. InterlockedIncrement( &pAtqContext->m_nIO);
  1942. fRes = ( g_pfnPostCompletionStatus( g_hCompPort,
  1943. BytesTransferred,
  1944. (ULONG_PTR)patqContext,
  1945. &pAtqContext->Overlapped ) ||
  1946. (GetLastError() == ERROR_IO_PENDING));
  1947. if (!fRes) { InterlockedDecrement( &pAtqContext->m_nIO); };
  1948. } else {
  1949. //
  1950. // Forcibly remove the context from blocking list.
  1951. //
  1952. fRes = pBandwidthInfo->RemoveFromBlockedList(pAtqContext);
  1953. // There is a possibility of race conditions!
  1954. // If we cant remove an item from blocking list before
  1955. // its IO operation is scheduled.
  1956. // there wont be any call back generated for this case!
  1957. }
  1958. return fRes;
  1959. } // AtqPostCompletionStatus
  1960. DWORD
  1961. I_AtqGetGlobalConfiguration(VOID)
  1962. /*++
  1963. Description:
  1964. This function sets several global config params for the ATQ package.
  1965. It also reads the global configuration from registry for ATQ.
  1966. The values if present will override the defaults
  1967. Returns:
  1968. Win32 Errorcode - NO_ERROR on success and anything else for error
  1969. --*/
  1970. {
  1971. DWORD dwError = NO_ERROR;
  1972. DWORD dwDefaultThreadTimeout = ATQ_REG_DEF_THREAD_TIMEOUT;
  1973. //
  1974. // If this is a NTW, do the right thing
  1975. //
  1976. if ( !TsIsNtServer() ) {
  1977. g_cMaxThreadLimit = ATQ_REG_MIN_POOL_THREAD_LIMIT;
  1978. //
  1979. // chicago does not have transmitfile/acceptex support
  1980. //
  1981. if ( TsIsWindows95() ) {
  1982. g_fUseAcceptEx = FALSE;
  1983. g_fUseFakeCompletionPort = TRUE;
  1984. g_dwNumContextLists = ATQ_NUM_CONTEXT_LIST_W95;
  1985. dwDefaultThreadTimeout = ATQ_REG_DEF_THREAD_TIMEOUT_PWS;
  1986. g_cMaxThreadLimit = ATQ_MAX_THREAD_LIMIT_W95;
  1987. g_cMaxThreads = ATQ_MAX_THREAD_LIMIT_W95;
  1988. }
  1989. } else {
  1990. MEMORYSTATUS ms;
  1991. //
  1992. // get the memory size
  1993. //
  1994. ms.dwLength = sizeof(MEMORYSTATUS);
  1995. GlobalMemoryStatus( &ms );
  1996. //
  1997. // attempt to use driver
  1998. //
  1999. g_fUseDriver = TRUE;
  2000. //
  2001. // Alloc two threads per MB of memory.
  2002. //
  2003. g_cMaxThreadLimit = (LONG)((ms.dwTotalPhys >> 19) + 2);
  2004. if ( g_cMaxThreadLimit < ATQ_REG_MIN_POOL_THREAD_LIMIT ) {
  2005. g_cMaxThreadLimit = ATQ_REG_MIN_POOL_THREAD_LIMIT;
  2006. } else if ( g_cMaxThreadLimit > ATQ_REG_MAX_POOL_THREAD_LIMIT ) {
  2007. g_cMaxThreadLimit = ATQ_REG_MAX_POOL_THREAD_LIMIT;
  2008. }
  2009. }
  2010. //
  2011. // Get entry points for NT
  2012. //
  2013. if ( !TsIsWindows95() ) {
  2014. if ( !I_AtqInitializeNtEntryPoints( ) ) {
  2015. dwError = ERROR_MOD_NOT_FOUND;
  2016. return ( dwError);
  2017. }
  2018. g_pfnCreateCompletionPort = CreateIoCompletionPort;
  2019. g_pfnGetQueuedCompletionStatus = GetQueuedCompletionStatus;
  2020. g_pfnCloseCompletionPort = CloseHandle;
  2021. g_pfnPostCompletionStatus = PostQueuedCompletionStatus;
  2022. } else {
  2023. //
  2024. // win95 entry points
  2025. //
  2026. g_pfnCreateCompletionPort = SIOCreateCompletionPort;
  2027. g_pfnGetQueuedCompletionStatus = SIOGetQueuedCompletionStatus;
  2028. g_pfnCloseCompletionPort = SIODestroyCompletionPort;
  2029. g_pfnPostCompletionStatus = SIOPostCompletionStatus;
  2030. }
  2031. if ( !TsIsWindows95() ) {
  2032. HKEY hkey = NULL;
  2033. DWORD dwVal;
  2034. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2035. g_PSZ_ATQ_CONFIG_PARAMS_REG_KEY,
  2036. 0,
  2037. KEY_READ,
  2038. &hkey);
  2039. if ( dwError == NO_ERROR ) {
  2040. //
  2041. // Read the Concurrency factor per processor
  2042. //
  2043. dwVal = I_AtqReadRegDword( hkey,
  2044. ATQ_REG_PER_PROCESSOR_CONCURRENCY,
  2045. ATQ_REG_DEF_PER_PROCESSOR_CONCURRENCY);
  2046. AtqSetInfo( AtqMaxConcurrency, (ULONG_PTR)dwVal);
  2047. //
  2048. // Read the count of threads to be allowed per processor
  2049. //
  2050. dwVal = I_AtqReadRegDword( hkey,
  2051. ATQ_REG_PER_PROCESSOR_ATQ_THREADS,
  2052. ATQ_REG_DEF_PER_PROCESSOR_ATQ_THREADS
  2053. );
  2054. if ( dwVal != 0 ) {
  2055. AtqSetInfo( AtqMaxPoolThreads, (ULONG_PTR)dwVal);
  2056. }
  2057. //
  2058. // Read the Data transfer rate value for our calculations
  2059. //
  2060. dwVal = I_AtqReadRegDword( hkey,
  2061. ATQ_REG_MIN_KB_SEC,
  2062. ATQ_REG_DEF_MIN_KB_SEC );
  2063. AtqSetInfo( AtqMinKbSec, (ULONG_PTR)dwVal);
  2064. //
  2065. // read the max thread limit
  2066. //
  2067. g_cMaxThreadLimit = I_AtqReadRegDword( hkey,
  2068. ATQ_REG_POOL_THREAD_LIMIT,
  2069. g_cMaxThreadLimit);
  2070. //
  2071. // read the listen backlog
  2072. //
  2073. g_cListenBacklog = I_AtqReadRegDword( hkey,
  2074. ATQ_REG_LISTEN_BACKLOG,
  2075. g_cListenBacklog);
  2076. //
  2077. // Read the time (in seconds) of how long the threads
  2078. // can stay alive when there is no IO operation happening on
  2079. // that thread.
  2080. //
  2081. dwVal = I_AtqReadRegDword( hkey,
  2082. ATQ_REG_THREAD_TIMEOUT,
  2083. dwDefaultThreadTimeout
  2084. );
  2085. AtqSetInfo( AtqThreadTimeout, (ULONG_PTR)dwVal);
  2086. //
  2087. // See if we need to turn off TF_USE_KERNEL_APC flag for
  2088. // TransmitFile
  2089. //
  2090. dwVal = I_AtqReadRegDword( hkey,
  2091. ATQ_REG_USE_KERNEL_APC,
  2092. ATQ_REG_DEF_USE_KERNEL_APC );
  2093. g_fUseKernelApc = dwVal;
  2094. //
  2095. // Whether or not to enable debug thread creator
  2096. //
  2097. dwVal = I_AtqReadRegDword( hkey,
  2098. ATQ_REG_ENABLE_DEBUG_THREADS,
  2099. g_fEnableDebugThreads );
  2100. g_fEnableDebugThreads = !!dwVal;
  2101. //
  2102. // Do we want to run this backlog monitor at all?
  2103. //
  2104. dwVal = I_AtqReadRegDword( hkey,
  2105. ATQ_REG_DISABLE_BACKLOG_MONITOR,
  2106. g_fDisableBacklogMonitor );
  2107. g_fDisableBacklogMonitor = !!dwVal;
  2108. //
  2109. // Read timeout for backlog monitor
  2110. //
  2111. dwVal = I_AtqReadRegDword( hkey,
  2112. ATQ_REG_FORCE_TIMEOUT,
  2113. g_cForceTimeout );
  2114. g_cForceTimeout = dwVal;
  2115. ATQ_REQUIRE( !RegCloseKey( hkey ) );
  2116. hkey = NULL;
  2117. }
  2118. DBG_ASSERT( NULL == hkey);
  2119. } else {
  2120. g_cListenBacklog = 5;
  2121. AtqSetInfo( AtqMaxConcurrency, ATQ_REG_DEF_PER_PROCESSOR_CONCURRENCY);
  2122. AtqSetInfo( AtqUseAcceptEx, ATQ_REG_DEF_USE_ACCEPTEX);
  2123. }
  2124. return ( dwError);
  2125. } // I_AtqGetGlobalConfiguration()
  2126. DWORD
  2127. I_NumAtqEndpointsOpen(VOID)
  2128. /*++
  2129. Description:
  2130. This function counts the number of Enpoints that remain open.
  2131. Arguments:
  2132. None
  2133. Returns:
  2134. DWORD containing the number of endpoints that are open.
  2135. --*/
  2136. {
  2137. DWORD nEPOpen = 0;
  2138. AcquireLock( &AtqEndpointLock);
  2139. PLIST_ENTRY plEP;
  2140. for( plEP = AtqEndpointList.Flink;
  2141. plEP != &AtqEndpointList;
  2142. plEP = plEP->Flink ) {
  2143. nEPOpen++;
  2144. } // for
  2145. ReleaseLock( &AtqEndpointLock);
  2146. return ( nEPOpen);
  2147. } // I_NumAtqEndpointsOpen()
  2148. //
  2149. // Global functions
  2150. //
  2151. DWORD
  2152. I_AtqReadRegDword(
  2153. IN HKEY hkey,
  2154. IN LPCSTR pszValueName,
  2155. IN DWORD dwDefaultValue )
  2156. /*++
  2157. NAME: I_AtqReadRegDword
  2158. SYNOPSIS: Reads a DWORD value from the registry.
  2159. ENTRY: hkey - Openned registry key to read
  2160. pszValueName - The name of the value.
  2161. dwDefaultValue - The default value to use if the
  2162. value cannot be read.
  2163. RETURNS DWORD - The value from the registry, or dwDefaultValue.
  2164. --*/
  2165. {
  2166. DWORD err;
  2167. DWORD dwBuffer;
  2168. DWORD cbBuffer = sizeof(dwBuffer);
  2169. DWORD dwType;
  2170. if( hkey != NULL ) {
  2171. err = RegQueryValueExA( hkey,
  2172. pszValueName,
  2173. NULL,
  2174. &dwType,
  2175. (LPBYTE)&dwBuffer,
  2176. &cbBuffer );
  2177. if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) ) {
  2178. dwDefaultValue = dwBuffer;
  2179. }
  2180. }
  2181. return dwDefaultValue;
  2182. } // I_AtqReadRegDword()
  2183. BOOL
  2184. AtqSetSocketOption(
  2185. IN PATQ_CONTEXT patqContext,
  2186. IN INT optName,
  2187. IN INT optValue
  2188. )
  2189. /*++
  2190. AtqSetSocketOption()
  2191. Routine Description:
  2192. Set socket options. Presently only handles TCP_NODELAY
  2193. Arguments:
  2194. patqContext - pointer to ATQ context
  2195. optName - name of property to change
  2196. optValue - value of property to set
  2197. Return Value:
  2198. TRUE if successful, else FALSE
  2199. --*/
  2200. {
  2201. if (TCP_NODELAY != optName)
  2202. {
  2203. return FALSE;
  2204. }
  2205. PATQ_CONT pContext = (PATQ_CONT)patqContext;
  2206. ATQ_ASSERT( pContext != NULL );
  2207. IF_DEBUG( API_ENTRY) {
  2208. ATQ_PRINTF(( DBG_CONTEXT, "AtqSetSocketOption( %08x (handle=%08x,"
  2209. " nIOs = %d), optName=%d, optValue=%d)\n",
  2210. patqContext, patqContext->hAsyncIO,
  2211. pContext->m_nIO, optName, optValue));
  2212. }
  2213. if ( pContext ) {
  2214. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  2215. if ( ((BOOL)pContext->IsFlag(ACF_TCP_NODELAY)) == ((BOOL)optValue))
  2216. {
  2217. //
  2218. // The flag is already enabled. Return success.
  2219. //
  2220. return TRUE;
  2221. }
  2222. else
  2223. {
  2224. if (NO_ERROR == setsockopt( HANDLE_TO_SOCKET(pContext->hAsyncIO),
  2225. IPPROTO_TCP,
  2226. TCP_NODELAY,
  2227. (char *)&optValue,
  2228. sizeof(INT)))
  2229. {
  2230. if ( optValue)
  2231. {
  2232. pContext->SetFlag(ACF_TCP_NODELAY);
  2233. }
  2234. else
  2235. {
  2236. pContext->ResetFlag(ACF_TCP_NODELAY);
  2237. }
  2238. return TRUE;
  2239. }
  2240. }
  2241. }
  2242. return FALSE;
  2243. }
  2244. PIIS_CAP_TRACE_INFO
  2245. AtqGetCapTraceInfo(
  2246. IN PATQ_CONTEXT patqContext
  2247. )
  2248. {
  2249. PATQ_CONT pContext = (PATQ_CONT)patqContext;
  2250. ATQ_ASSERT( pContext != NULL );
  2251. return pContext->GetCapTraceInfo();
  2252. }
  2253. DWORD
  2254. AtqDebugCreatorThread(
  2255. LPDWORD param
  2256. )
  2257. /*++
  2258. Routine Description:
  2259. For debugging purpose.
  2260. This function will cause another IO thread to be created (regardless of
  2261. the max thread limit). This IO thread will only serve new connections.
  2262. To trigger the creation of a new thread, set isatq!g_fCreateDebugThread=1
  2263. Arguments:
  2264. param - Unused
  2265. Return Value:
  2266. ERROR_SUCCESS
  2267. --*/
  2268. {
  2269. for ( ; !g_fShutdown ; )
  2270. {
  2271. Sleep( 5000 );
  2272. if ( g_fCreateDebugThread )
  2273. {
  2274. OutputDebugString( "Creating DEBUG thread." \
  2275. "Reseting isatq!g_fCreateDebugThread\n" );
  2276. I_AtqCheckThreadStatus( (VOID*) ATQ_DEBUG_THREAD );
  2277. g_fCreateDebugThread = FALSE;
  2278. }
  2279. }
  2280. return ERROR_SUCCESS;
  2281. }
  2282. /************************ End of File ***********************/