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.

1054 lines
30 KiB

  1. /*++
  2. Copyright (c) 1995-1997 Microsoft Corporation
  3. Module Name :
  4. timeout.cxx
  5. Abstract:
  6. This module contains code for timeout processing of ATQ contexts
  7. Author:
  8. Murali R. Krishnan ( MuraliK ) 16-July-1997
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Internet Server - Asynchronous Thread Queue Module
  13. Functions Exported:
  14. Revision History:
  15. --*/
  16. /************************************************************
  17. * Include Headers
  18. ************************************************************/
  19. #include "isatq.hxx"
  20. /************************************************************
  21. * Globals
  22. ************************************************************/
  23. DWORD g_dwTimeoutCookie = 0; // Scheduler Cookie for timeout processing
  24. DWORD g_AtqCurrentTick = 1;
  25. DWORD g_dwTimeout = ATQ_TIMEOUT_INTERVAL; // active timeout value
  26. /************************************************************
  27. * Functions
  28. ************************************************************/
  29. BOOL
  30. I_TimeOutContext(
  31. PATQ_CONT pAtqContext
  32. )
  33. /*++
  34. Routine Description:
  35. This function does the actual timeout for a particular context.
  36. Note: The Context list lock is held while processing this function
  37. Arguments:
  38. Context - Pointer to the context to be timed out
  39. Return value:
  40. TRUE, if the completion routine was called
  41. FALSE, otherwise
  42. --*/
  43. {
  44. DWORD timeout;
  45. //
  46. // Call client after re-checking that this item
  47. // really has timed out
  48. //
  49. // Fake timeout
  50. //
  51. if ( pAtqContext->TimeOut == ATQ_INFINITE ) {
  52. pAtqContext->NextTimeout = ATQ_INFINITE;
  53. return(FALSE);
  54. }
  55. //
  56. // Was our timeout long enough?
  57. //
  58. // NYI: Optimize: CanonTimeout should be called only once per IO submitted
  59. timeout = CanonTimeout( pAtqContext->BytesSent/g_cbMinKbSec);
  60. if ( timeout > pAtqContext->TimeOut ) {
  61. //
  62. // Reset the Timeout value based on the bytes to be sent
  63. // as well as update the time when this pAtqContext be timedout
  64. //
  65. pAtqContext->TimeOut = timeout;
  66. pAtqContext->NextTimeout = AtqGetCurrentTick( ) + timeout;
  67. return(FALSE);
  68. }
  69. //
  70. // If this is on blocked list, remove it.
  71. //
  72. if ( pAtqContext->IsBlocked()) {
  73. PBANDWIDTH_INFO pBandwidthInfo = pAtqContext->m_pBandwidthInfo;
  74. ATQ_ASSERT( pBandwidthInfo != NULL );
  75. ATQ_REQUIRE( pBandwidthInfo->RemoveFromBlockedList(pAtqContext));
  76. }
  77. //
  78. // If we've already indicated this connection to the client,
  79. // then we abort them by calling their IO completion routine
  80. // and letting them cleanup. Otherwise we close the socket
  81. // which will generally cause an IO aborted completion that
  82. // we will cleanup. Note there is a window where we may
  83. // close the socket out from under a client in their
  84. // connection completion routine but that should be ok.
  85. //
  86. if ( pAtqContext->pfnCompletion &&
  87. pAtqContext->IsFlag( ACF_CONN_INDICATED)) {
  88. //
  89. // TransmitFile socket state will be unconnected because
  90. // we're expecting it to complete successfully. Reset the
  91. // state so the socket gets cleaned up properly
  92. //
  93. if ( pAtqContext->IsState( ACS_SOCK_UNCONNECTED) ) {
  94. pAtqContext->MoveState( ACS_SOCK_CONNECTED);
  95. }
  96. AcIncrement( CacAtqContextsTimedOut);
  97. pAtqContext->NextTimeout = ATQ_INFINITE;
  98. pAtqContext->IOCompletion( 0, ERROR_SEM_TIMEOUT, NULL);
  99. //
  100. // We can't touch any items on the list after notifying
  101. // the client as the client may have re-entered
  102. // and freed some items from the list
  103. //
  104. return(TRUE);
  105. } else {
  106. HANDLE hIO;
  107. hIO = (HANDLE ) InterlockedExchangePointer(
  108. (PVOID *)&pAtqContext->hAsyncIO,
  109. NULL
  110. );
  111. IF_DEBUG( TIMEOUT) {
  112. ATQ_PRINTF(( DBG_CONTEXT,
  113. "Timeout: closesocket(%d) Context=%08x\n",
  114. hIO, pAtqContext));
  115. }
  116. closesocket( HANDLE_TO_SOCKET(hIO) );
  117. }
  118. return(FALSE); // we can touch the items on current list.
  119. } // I_TimeOutContext
  120. VOID
  121. AtqProcessTimeoutOfRequests(
  122. PATQ_CONTEXT_LISTHEAD ContextList
  123. )
  124. /*++
  125. Routine Description:
  126. Walks the list of Atq clients looking for any item that has timed out and
  127. notifies the client if it has.
  128. TimeOutScanID is used as a serial number to prevent evaluating the same
  129. context twice. We start from the beginning of the list everytime we
  130. notify a client an Atq context has timed out. We do this because the
  131. client timeout processing may remove any number of Items from the
  132. list (including the next couple of items in the list).
  133. This routine also checks to make sure outstanding AcceptEx sockets
  134. haven't been exhausted (if less then 25% available, adds some more).
  135. --*/
  136. {
  137. DWORD newLatest = ATQ_INFINITE;
  138. BOOL fRescan;
  139. //
  140. // See if the latest one is timed-out
  141. //
  142. if ( ContextList->LatestTimeout > AtqGetCurrentTick( ) ) {
  143. return;
  144. }
  145. // set the latest timeout in the context list,
  146. // to avoid races with IO being started.
  147. ContextList->LatestTimeout = ATQ_INFINITE;
  148. //
  149. // Scan the timeout list looking for items that have timed out
  150. // and adjust the timeout values
  151. //
  152. do {
  153. LIST_ENTRY * pentry;
  154. LIST_ENTRY * pentryNext;
  155. DWORD scanId = AtqGetCurrentTick( );
  156. ContextList->Lock( );
  157. fRescan = FALSE;
  158. for ( pentry = ContextList->ActiveListHead.Flink;
  159. pentry != &ContextList->ActiveListHead;
  160. pentry = pentryNext ) {
  161. PATQ_CONT pContext;
  162. pentryNext = pentry->Flink;
  163. pContext = CONTAINING_RECORD(
  164. pentry,
  165. ATQ_CONTEXT,
  166. m_leTimeout
  167. );
  168. if ( pContext->Signature != ATQ_CONTEXT_SIGNATURE ) {
  169. ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  170. break;
  171. }
  172. //
  173. // Ignore items we've already processed
  174. //
  175. if ( pContext->TimeOutScanID == scanId ) {
  176. continue;
  177. }
  178. pContext->TimeOutScanID = scanId;
  179. //
  180. // If there is an IO which has popped up now,
  181. // we have to do nothing. This code was added to protect catapult!
  182. //
  183. pContext->SetFlag( ACF_IN_TIMEOUT);
  184. if ( !pContext->lSyncTimeout) {
  185. // no body is using this context. Check and synchronize
  186. // the timeout state.
  187. //
  188. // The client specifies the IO doesn't timeout if
  189. // INFINITE is in the TimeOut field of the ATQ context
  190. // If we've timed out, then notify the client.
  191. //
  192. DWORD nextTimeout = pContext->NextTimeout;
  193. if ( nextTimeout > AtqGetCurrentTick() ) {
  194. // pick up the latest "low" value for
  195. // firing next timeout thread
  196. if ( nextTimeout < newLatest ) {
  197. newLatest = nextTimeout;
  198. }
  199. } else if ( I_TimeOutContext(pContext) ) {
  200. // we are done checking and processing timeout.
  201. // reset the In Timeout flag
  202. pContext->ResetFlag( ACF_IN_TIMEOUT);
  203. fRescan = TRUE;
  204. break;
  205. } else {
  206. //
  207. // It is possible that the timeout got reset
  208. // Check for the latest "low" value
  209. //
  210. nextTimeout = pContext->NextTimeout;
  211. if ( nextTimeout < newLatest ) {
  212. newLatest = nextTimeout;
  213. }
  214. }
  215. } else {
  216. AcIncrement( CacAtqProcWhenTimeout);
  217. }
  218. // we are done checkin and processing timeouts.
  219. // reset the In Timeout flag
  220. pContext->ResetFlag( ACF_IN_TIMEOUT);
  221. } // scan list
  222. // let other system threads also run happily for a while
  223. ContextList->Unlock( );
  224. } while (fRescan);
  225. if ( newLatest != ATQ_INFINITE) {
  226. // We picked up the latest timeout. store it.
  227. ContextList->LatestTimeout = newLatest;
  228. }
  229. return;
  230. } // AtqProcessTimeoutOfRequests
  231. //
  232. // ACCEPT_EX_TIMEOUT_STATS collects statistics for the
  233. // timeout processing in the Pending AcceptEx List//
  234. //
  235. struct ACCEPT_EX_TIMEOUT_STATS {
  236. DWORD m_nScanned;
  237. DWORD m_nTimedOut;
  238. DWORD m_nSkipped;
  239. DWORD m_nConnected;
  240. DWORD m_nNotConnected;
  241. };
  242. BOOL
  243. I_TimeOutPendingAcceptExContext(
  244. PATQ_CONT pAtqContext
  245. )
  246. /*++
  247. Routine Description:
  248. This function does the actual timeout for a pending AcceptEx context.
  249. Note: The Context list lock is held while processing this function
  250. Arguments:
  251. pAtqContext - Pointer to the context to be timed out
  252. Return value:
  253. TRUE, if a tmieout operation was conducted.
  254. FALSE, otherwise
  255. --*/
  256. {
  257. DBG_ASSERT( pAtqContext != NULL);
  258. //
  259. // in the shutdown case it is possible that someone closed this socket already
  260. // so don't worry about it.
  261. //
  262. if ( pAtqContext->hAsyncIO == NULL ) {
  263. return TRUE;
  264. }
  265. //
  266. // Validate our assumptions about this Pending AcceptEx Context
  267. // there is an endpoint => AcceptEx context
  268. DBG_ASSERT( pAtqContext->pEndpoint != NULL);
  269. DBG_ASSERT( pAtqContext->IsState( ACS_SOCK_LISTENING));
  270. DBG_ASSERT( !pAtqContext->IsFlag( ACF_CONN_INDICATED));
  271. DBG_ASSERT( pAtqContext->TimeOut != ATQ_INFINITE);
  272. //
  273. // We will obtain the socket handle stored inside the AcceptEx Context
  274. // and free up the context.
  275. // Warning:
  276. // The AcceptEx socket did not have a connection when this function
  277. // was called. However now between the time when the state was checked
  278. // and the time this timeout operation completes, it is possible that
  279. // a new connection is bound to this AcceptEx context => we can get IO completion.
  280. // I need to handle this case
  281. //
  282. HANDLE hIO;
  283. hIO = (HANDLE ) InterlockedExchangePointer(
  284. (PVOID *)&pAtqContext->hAsyncIO,
  285. NULL
  286. );
  287. IF_DEBUG( TIMEOUT) {
  288. ATQ_PRINTF(( DBG_CONTEXT,
  289. "TimeoutPendingAcceptExContext(%08x): closesocket(%d)\n",
  290. pAtqContext, hIO));
  291. }
  292. closesocket( HANDLE_TO_SOCKET(hIO) );
  293. return ( TRUE);
  294. } // I_TimeOutPendingAcceptExContext()
  295. BOOL
  296. I_IsTimeoutForAcceptExContext(
  297. IN OUT PATQ_CONT pAtqContext,
  298. IN OUT ACCEPT_EX_TIMEOUT_STATS * pAetStats,
  299. OUT BOOL * pfIsConnected
  300. )
  301. /*++
  302. Routine Description:
  303. This function checks to see if timeout operation has to be performed
  304. for a given AtqContext. It bases the decision on the a variety of
  305. details maintained in Atq Context and the Endpoint.
  306. Note: The Context list lock is held while processing this function
  307. Arguments:
  308. pAtqContext - Pointer to the context to be timed out
  309. pAetStats - pointer to AcceptEx Timeout Statistics structure
  310. pfIsConnected - Set to TRUE if socket is connected to, but we're still
  311. waiting for data. Such contexts are prime candidated
  312. to be forcibly timed out by backlog monitor
  313. Return value:
  314. TRUE, if a tmieout operation has to be conducted.
  315. FALSE, when no timeout is required.
  316. --*/
  317. {
  318. DBG_ASSERT( pAtqContext);
  319. DBG_ASSERT( pAetStats);
  320. PATQ_ENDPOINT pEndpoint;
  321. pEndpoint = pAtqContext->pEndpoint;
  322. *pfIsConnected = FALSE;
  323. if ( pEndpoint != NULL) {
  324. //
  325. // We will use getsockopt() to query the connection status
  326. // for the socket inside the Atq context.
  327. // If Socket is not connected => leave it in the pool
  328. // If Socket is connected and waiting for receive operation =>
  329. // do timeout processing
  330. //
  331. // The goal is to maintain a pool of sockets in listening state
  332. // so that any new connection will be picked up quickly.
  333. //
  334. // getsockopt() is a very costly function.
  335. // We check to see if we have enough sockets available
  336. // for an endpoint. If they are, then we bypass calling getsockopt
  337. // "enough" is defined as # of available sockets is at least
  338. // 25% of the total # of accept ex sockets outstanding.
  339. // Optimize calling getsockopt() based on
  340. // current count in pEndpoint->nAvailDuringTimeOut
  341. //
  342. if ( pEndpoint->nAvailDuringTimeOut >
  343. ( pEndpoint->nAcceptExOutstanding >> 2)
  344. ) {
  345. // Already enough Contexts are available.
  346. // Do nothing
  347. pAetStats->m_nSkipped++;
  348. return (FALSE); // Do not timeout
  349. }
  350. DWORD dwConnect;
  351. int cbOptLen = sizeof( dwConnect );
  352. //
  353. // Query the socket layer if the current socket has a valid connection
  354. // An AcceptEx socket can be connected and waiting for new request to
  355. // be read. If we are in such state we should not blow away context.
  356. //
  357. if ( getsockopt(HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
  358. SOL_SOCKET,
  359. SO_CONNECT_TIME,
  360. (char *) &dwConnect,
  361. &cbOptLen ) != SOCKET_ERROR
  362. ) {
  363. //
  364. // A return value of 0xFFFFFFFF indicates that the given
  365. // AcceptEx socket is not connected yet.
  366. // Otherwise the socket is connected and is probably wating
  367. // for request to be read or maybe a completion is already
  368. // on its way.
  369. //
  370. if ( dwConnect == (DWORD) 0xFFFFFFFF ) {
  371. //
  372. // Ignore the "Listen" socket context
  373. //
  374. pAetStats->m_nNotConnected++;
  375. DBG_ASSERT( NULL != pEndpoint);
  376. pEndpoint->nAvailDuringTimeOut++;
  377. // Update timeout values to give a breather interval
  378. pAtqContext->NextTimeout =
  379. AtqGetCurrentTick() + pAtqContext->TimeOut;
  380. return ( FALSE); // Do not timeout
  381. }
  382. else if ( !pAtqContext->IsFlag(ACF_WINSOCK_CONNECTED) ) {
  383. *pfIsConnected = TRUE;
  384. //
  385. // Mark that this context has connection indicated.
  386. // If this context waits around in connected state for
  387. // long-time we need to blow the context away.
  388. //
  389. pAetStats->m_nConnected++;
  390. // Update timeout values to give a breather interval
  391. pAtqContext->NextTimeout =
  392. AtqGetCurrentTick() + pAtqContext->TimeOut;
  393. pAtqContext->SetFlag(ACF_WINSOCK_CONNECTED);
  394. return (FALSE); // do not timeout now
  395. }
  396. }
  397. } // if Endpoint exists
  398. return (TRUE); // yes timeout this context
  399. } // I_IsTimeoutForAcceptExContext()
  400. VOID
  401. I_AtqProcessPendingListens(
  402. IN PATQ_CONTEXT_LISTHEAD pContextList,
  403. IN PATQ_ENDPOINT pEndpoint,
  404. OUT PDWORD pcForced
  405. )
  406. /*++
  407. Routine Description:
  408. Walks the list of Pending accept ex and makes sure none has timed out.
  409. Also checks to see if we need to allocate more AcceptEx sockets.
  410. Arguments:
  411. pContextList - pointer to ATQ_CONTEXT_LISTHEAD object
  412. pEndpoint - pointer to ATQ_ENDPOINT object. If set, then only those
  413. ATQ_CONTEXTs whose endpoint matches will be timed out.
  414. If pEndpoint=NULL, then all ATQ_CONTEXTs will be timed out.
  415. pcForced - If pEndpoint!=NULL, then this is set to # of forced sockets
  416. Returns:
  417. None
  418. --*/
  419. {
  420. BOOL fRescan;
  421. BOOL fForceTimeout = ( pEndpoint != NULL );
  422. BOOL fIsConnected = FALSE;
  423. if ( fForceTimeout )
  424. {
  425. *pcForced = 0;
  426. }
  427. ACCEPT_EX_TIMEOUT_STATS AetStats;
  428. //
  429. // Initialize Statistics block
  430. //
  431. AetStats.m_nScanned = 0;
  432. AetStats.m_nTimedOut = 0;
  433. AetStats.m_nSkipped = 0;
  434. AetStats.m_nConnected = 0;
  435. AetStats.m_nNotConnected = 0;
  436. //
  437. // Look through the listening sockets to make sure the AcceptEx sockets
  438. // haven't been exhausted
  439. //
  440. do {
  441. LIST_ENTRY * pentry;
  442. LIST_ENTRY * pentryNext;
  443. DWORD scanId = AtqGetCurrentTick( );
  444. fRescan = FALSE;
  445. pContextList->Lock();
  446. for ( pentry = pContextList->PendingAcceptExListHead.Flink;
  447. pentry != &pContextList->PendingAcceptExListHead;
  448. pentry = pentryNext ) {
  449. PATQ_CONT pContext;
  450. pentryNext = pentry->Flink;
  451. pContext = CONTAINING_RECORD(
  452. pentry,
  453. ATQ_CONTEXT,
  454. m_leTimeout
  455. );
  456. if ( pContext->Signature != ATQ_CONTEXT_SIGNATURE ) {
  457. DBG_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
  458. break;
  459. }
  460. //
  461. // Use PATQ_ENDPOINT filter if necessary
  462. //
  463. if ( pEndpoint && ( pEndpoint != pContext->pEndpoint ) )
  464. {
  465. continue;
  466. }
  467. //
  468. // Ignore items we've already processed
  469. //
  470. if ( pContext->TimeOutScanID == scanId ) {
  471. continue;
  472. }
  473. AetStats.m_nScanned++;
  474. pContext->TimeOutScanID = scanId;
  475. //
  476. // If the context has Timeout value smaller than the one in our global tick
  477. // then examine if this context can be timedout
  478. //
  479. DBG_CODE( if ( pContext->IsAcceptExRootContext())
  480. {
  481. DBG_ASSERT( pContext->TimeOut == ATQ_INFINITE);
  482. DBG_ASSERT( pContext->NextTimeout == ATQ_INFINITE);
  483. }
  484. );
  485. if ( pContext->NextTimeout <= AtqGetCurrentTick() ||
  486. fForceTimeout ) {
  487. //
  488. // Protect against the race with the normal IO completion
  489. //
  490. pContext->SetFlag( ACF_IN_TIMEOUT);
  491. if ( !pContext->lSyncTimeout ) {
  492. if ( !I_IsTimeoutForAcceptExContext( pContext,
  493. &AetStats,
  494. &fIsConnected )) {
  495. if ( !fForceTimeout || !fIsConnected )
  496. {
  497. pContext->ResetFlag( ACF_IN_TIMEOUT);
  498. continue;
  499. }
  500. }
  501. if ( I_TimeOutPendingAcceptExContext(pContext)) {
  502. AetStats.m_nTimedOut++;
  503. pContext->ResetFlag(ACF_IN_TIMEOUT);
  504. if ( fForceTimeout )
  505. {
  506. (*pcForced)++;
  507. }
  508. fRescan = TRUE;
  509. break;
  510. }
  511. } // if (!pContext->lSyncTimeout)
  512. pContext->ResetFlag( ACF_IN_TIMEOUT);
  513. } // if the context's timeout value <= CurrentTick
  514. else {
  515. //
  516. // Timeout value has not been reached. Skip this context
  517. //
  518. AetStats.m_nSkipped++;
  519. }
  520. } // scan list
  521. pContextList->Unlock();
  522. } while (fRescan);
  523. IF_DEBUG( TIMEOUT) {
  524. DBGPRINTF(( DBG_CONTEXT,
  525. "TimeoutPendingListens( CtxtList[%d], AtqTick=%d)\n"
  526. " Contexts Scanned=%d, Skipped=%d, TimedOut=%d,"
  527. " Connected=%d, NotConnected=%d\n",
  528. pContextList - AtqActiveContextList, AtqGetCurrentTick(),
  529. AetStats.m_nScanned, AetStats.m_nSkipped,
  530. AetStats.m_nTimedOut, AetStats.m_nConnected,
  531. AetStats.m_nNotConnected
  532. ));
  533. }
  534. # ifdef IIS_AUX_COUNTERS
  535. g_AuxCounters[CacAtqPendingAcceptExScans] += AetStats.m_nScanned;
  536. # endif // IIS_AUX_COUNTERS
  537. return;
  538. } // I_AtqProcessPendingListens()
  539. VOID
  540. I_AtqCheckEndpoints(
  541. VOID
  542. )
  543. /*++
  544. Description:
  545. This function checks all the listen info objects and adds appropriate
  546. number of accept ex sockets as necessary.
  547. Arguments:
  548. None
  549. Returns:
  550. None
  551. --*/
  552. {
  553. LIST_ENTRY * pEntry;
  554. PATQ_ENDPOINT pEndpoint;
  555. AcquireLock( &AtqEndpointLock);
  556. for ( pEntry = AtqEndpointList.Flink;
  557. pEntry != &AtqEndpointList;
  558. pEntry = pEntry->Flink ) {
  559. pEndpoint = CONTAINING_RECORD(
  560. pEntry,
  561. ATQ_ENDPOINT,
  562. ListEntry
  563. );
  564. DBG_ASSERT( pEndpoint->Signature == ATQ_ENDPOINT_SIGNATURE );
  565. DBG_ASSERT( pEndpoint->nSocketsAvail >= 0);
  566. //
  567. // Check to make sure outstanding AcceptEx sockets
  568. // haven't been exhausted (if less then 25% available, adds some more).
  569. //
  570. if ( ((DWORD ) pEndpoint->nSocketsAvail) <
  571. (pEndpoint->nAcceptExOutstanding >> 2) ) {
  572. IF_DEBUG( TIMEOUT ) {
  573. DBGPRINTF(( DBG_CONTEXT,
  574. "[Timeout] Adding AcceptEx Contexts for EP=%08x; nAvail = %d;\n",
  575. pEndpoint, pEndpoint->nSocketsAvail));
  576. }
  577. (VOID ) I_AtqPrepareAcceptExSockets(pEndpoint,
  578. pEndpoint->nAcceptExOutstanding
  579. );
  580. }
  581. //
  582. // set to zero, so recount will be done during next timeout loop
  583. //
  584. pEndpoint->nAvailDuringTimeOut = 0;
  585. }
  586. ReleaseLock( &AtqEndpointLock);
  587. return;
  588. } // I_AtqCheckEndpoints
  589. VOID
  590. I_AtqTimeOutWorker(VOID)
  591. /*++
  592. Description:
  593. This function handles timeout processing using the simple
  594. clock algorithm, wherein partial set of lists are scanned
  595. during each timeout processing call.
  596. Arguments:
  597. None
  598. Returns:
  599. None
  600. --*/
  601. {
  602. DWORD start;
  603. PATQ_CONTEXT_LISTHEAD pContextList;
  604. IF_DEBUG(TIMEOUT) {
  605. DBGPRINTF((DBG_CONTEXT, "TimeoutWorker: entered\n"));
  606. }
  607. start = (AtqGetCurrentTick() & 0x1);
  608. for ( pContextList = AtqActiveContextList + start;
  609. pContextList < (AtqActiveContextList + g_dwNumContextLists) ;
  610. pContextList += 2 ) {
  611. IF_DEBUG(TIMEOUT) {
  612. DBGPRINTF((DBG_CONTEXT,
  613. "TimeoutWorker: Processing list[%d] = %08x\n",
  614. (pContextList - AtqActiveContextList),
  615. pContextList));
  616. }
  617. AtqProcessTimeoutOfRequests( pContextList );
  618. I_AtqProcessPendingListens( pContextList, NULL, NULL );
  619. } // for
  620. if ( start != 0 ) {
  621. I_AtqCheckEndpoints( );
  622. }
  623. return;
  624. } // I_AtqTimeOutWorker()
  625. VOID
  626. WINAPI
  627. I_AtqTimeoutCompletion(
  628. IN PVOID Context
  629. )
  630. /*++
  631. Routine Description:
  632. Callback routine for the scheduled version of the timeout thread.
  633. The callback assumes timeouts are rounded to ATQ_TIMEOUT_INTERVAL
  634. In addition to timing out requests when necessary, the timeout thread
  635. also performs the job of bandwidth calculation and tuning the bandwidth
  636. throttle operation (which works on feedback mechanism).
  637. At every sampling interval the scheduled callback comes in and it updates
  638. the bandwidth.
  639. Arguments:
  640. Context - Context returned by the scheduler thread.
  641. Return Value:
  642. none.
  643. --*/
  644. {
  645. DWORD Timeout = ATQ_TIMEOUT_INTERVAL;
  646. BOOL fDoContextTimeout = TRUE;
  647. if ( g_fShutdown ) {
  648. ATQ_PRINTF(( DBG_CONTEXT,
  649. "Detected a shutdown while entering timeout callback\n"));
  650. return;
  651. }
  652. InterlockedIncrement( (PLONG)&g_AtqCurrentTick );
  653. //
  654. // Perform necessary steps to handle Bandwidth throttling.
  655. //
  656. ATQ_ASSERT( BANDWIDTH_INFO::sm_cSamplesForTimeout >= 1);
  657. IF_DEBUG(TIMEOUT) {
  658. DBGPRINTF((DBG_CONTEXT,
  659. "Timeout: BANDWIDTH_INFO::cSamplesForTimeout=%d\n",
  660. BANDWIDTH_INFO::sm_cSamplesForTimeout ));
  661. }
  662. if ( BANDWIDTH_INFO::GlobalActive() ) {
  663. --(BANDWIDTH_INFO::sm_cSamplesForTimeout);
  664. // Perform a sampling to update measured bandwidth +
  665. // apply feedback policy
  666. BANDWIDTH_INFO::UpdateAllBandwidths();
  667. Timeout = ATQ_SAMPLE_INTERVAL_IN_SECS;
  668. if ( BANDWIDTH_INFO::sm_cSamplesForTimeout != 0) {
  669. // We have not reached timeout yet. So skip context timeouts
  670. fDoContextTimeout = FALSE;
  671. } else {
  672. // We had reached the timeout interval for requests.
  673. // Examine and release requests.
  674. ATQ_ASSERT( BANDWIDTH_INFO::sm_cSamplesForTimeout == 0);
  675. // reset the count of samples before proceeding.
  676. BANDWIDTH_INFO::sm_cSamplesForTimeout = NUM_SAMPLES_PER_TIMEOUT_INTERVAL;
  677. }
  678. } else {
  679. BANDWIDTH_INFO::sm_cSamplesForTimeout = 1;
  680. }
  681. //
  682. // We are at a Timeout Interval. Examine and timeout requests.
  683. //
  684. if ( fDoContextTimeout ) {
  685. I_AtqTimeOutWorker();
  686. }
  687. if ( Timeout != g_dwTimeout) {
  688. // the scheduled interval is different from this current interval
  689. // Inidicate the changed timeout value to the scheduler
  690. ScheduleAdjustTime( g_dwTimeoutCookie, TimeToWait(Timeout));
  691. g_dwTimeout = Timeout;
  692. }
  693. return;
  694. } // I_AtqTimeoutCompletion
  695. BOOL
  696. I_AtqStartTimeoutProcessing(
  697. IN PVOID Context
  698. )
  699. /*++
  700. Routine Description:
  701. Starts the timeout processing. It always uses the scheduler to schedule
  702. a timeout operation.
  703. Note: The scheduler should be initialized before getting to this function.
  704. Arguments:
  705. Context - Context passed to the thread creation or scheduler thread.
  706. Return Value:
  707. TRUE, if ok
  708. FALSE, otherwise
  709. --*/
  710. {
  711. ATQ_ASSERT( ATQ_SAMPLE_INTERVAL_IN_SECS < ATQ_TIMEOUT_INTERVAL );
  712. if ( BANDWIDTH_INFO::GlobalEnabled() ) {
  713. g_dwTimeout = ATQ_SAMPLE_INTERVAL_IN_SECS;
  714. BANDWIDTH_INFO::sm_cSamplesForTimeout =
  715. NUM_SAMPLES_PER_TIMEOUT_INTERVAL;
  716. } else {
  717. g_dwTimeout = ATQ_TIMEOUT_INTERVAL;
  718. BANDWIDTH_INFO::sm_cSamplesForTimeout = 1;
  719. }
  720. g_dwTimeoutCookie =
  721. ScheduleWorkItem(
  722. I_AtqTimeoutCompletion,
  723. Context,
  724. TimeToWait(g_dwTimeout)
  725. , TRUE // ask for periodic timeout
  726. );
  727. if ( g_dwTimeoutCookie == 0 ) {
  728. ATQ_PRINTF(( DBG_CONTEXT,
  729. "Error %d scheduling timeout\n",GetLastError()));
  730. return(FALSE);
  731. }
  732. return(TRUE);
  733. } // I_AtqStartTimeoutProcessing()
  734. BOOL
  735. I_AtqStopTimeoutProcessing(
  736. VOID
  737. )
  738. /*++
  739. Routine Description:
  740. Stops the timeout processing. It terminates the scheduled workitem and
  741. cleans up any state.
  742. Note: The scheduler should be terminated only after this call
  743. Arguments:
  744. Context - Context passed to the thread creation or scheduler thread.
  745. Return Value:
  746. TRUE, if ok
  747. FALSE, otherwise
  748. --*/
  749. {
  750. DBGPRINTF(( DBG_CONTEXT, "I_AtqStopTimeoutProcessing\n"));
  751. if ( 0 != g_dwTimeoutCookie) {
  752. DBG_REQUIRE( RemoveWorkItem( g_dwTimeoutCookie ));
  753. g_dwTimeoutCookie = 0;
  754. }
  755. return ( TRUE);
  756. } // I_AtqStopTimeoutProcessing()
  757. /************************ End of File ***********************/