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.

1995 lines
55 KiB

  1. /*++
  2. Copyright (c) 1994-1996 Microsoft Corporation
  3. Module Name :
  4. atqsupp.cxx
  5. Abstract:
  6. Contains internal support routines for the ATQ package
  7. From atqnew.c
  8. Author:
  9. Murali R. Krishnan (MuraliK) 02-Apr-1996
  10. Project:
  11. Internet Server Common DLL
  12. --*/
  13. #include "isatq.hxx"
  14. #include <iscaptrc.h>
  15. DWORD AtqPoolThread( LPDWORD param );
  16. extern PBANDWIDTH_INFO g_pBandwidthInfo;
  17. extern DWORD IISCapTraceFlag;
  18. extern TRACEHANDLE IISCapTraceLoggerHandle;
  19. DWORD g_fAlwaysReuseSockets = FALSE;
  20. /************************************************************
  21. * Functions for ATQ_CONTEXT
  22. ************************************************************/
  23. PATQ_CONT
  24. I_AtqAllocContextFromCache( VOID);
  25. VOID
  26. I_AtqFreeContextToCache(
  27. IN PATQ_CONT pAtqContext,
  28. IN BOOL UnlinkContext
  29. );
  30. PATQ_CONT
  31. I_AtqAllocContextFromCache( VOID)
  32. /*++
  33. This function attempts to allocate an ATQ context from the allocation cache.
  34. It then initializes the state information in the ATQ context object and
  35. returns the context on success.
  36. Arguments:
  37. None
  38. Returns:
  39. On success a valid pointer to ATQ_CONT. Otherwise NULL.
  40. --*/
  41. {
  42. PATQ_CONT pAtqContext;
  43. DBG_ASSERT( NULL != g_pachAtqContexts);
  44. pAtqContext = (ATQ_CONTEXT * ) g_pachAtqContexts->Alloc();
  45. if ( NULL != pAtqContext ) {
  46. //
  47. // Make sure everything is zeroed out so there is
  48. // no crud from previous use.
  49. //
  50. memset(pAtqContext, 0, sizeof(ATQ_CONTEXT));
  51. pAtqContext->ContextList =
  52. &AtqActiveContextList[(++AtqGlobalContextCount %
  53. g_dwNumContextLists)];
  54. pAtqContext->Signature = ATQ_CONTEXT_SIGNATURE;
  55. }
  56. return (pAtqContext);
  57. } // I_AtqAllocContextFromCache()
  58. VOID
  59. I_AtqFreeContextToCache(
  60. IN PATQ_CONT pAtqContext
  61. )
  62. /*++
  63. This function releases the given context to the allocation cache.
  64. Arguments:
  65. pAtqContext pointer to the ATQ_CONTEXT that is being freed.
  66. Returns:
  67. None
  68. Issues:
  69. This function also performs some other cleanup specific to AtqContexts.
  70. --*/
  71. {
  72. #if 0
  73. ATQ_PRINTF(( DBG_CONTEXT,
  74. "[I_AtqFreeCtxtToCache] Freed up %08x\n",
  75. pAtqContext
  76. ));
  77. #endif
  78. DBG_ASSERT( pAtqContext->Signature == ATQ_FREE_CONTEXT_SIGNATURE);
  79. DBG_ASSERT( pAtqContext->lSyncTimeout ==0);
  80. DBG_ASSERT( pAtqContext->m_nIO ==0);
  81. DBG_ASSERT( pAtqContext->m_acFlags == 0);
  82. DBG_ASSERT( pAtqContext->m_acState == 0);
  83. DBG_ASSERT( pAtqContext->m_leTimeout.Flink == NULL);
  84. DBG_ASSERT( pAtqContext->m_leTimeout.Blink == NULL);
  85. DBG_ASSERT( pAtqContext->pvBuff == NULL);
  86. DBG_ASSERT( pAtqContext->pEndpoint == NULL);
  87. DBG_ASSERT( pAtqContext->hAsyncIO == NULL);
  88. DBG_REQUIRE( g_pachAtqContexts->Free( pAtqContext));
  89. return;
  90. } // I_AtqFreeContextToCache
  91. void
  92. ATQ_CONTEXT::Print( void) const
  93. {
  94. DBGPRINTF(( DBG_CONTEXT,
  95. " ATQ_CONTEXT (%08x)\n"
  96. "\thAsyncIO = %p Signature = %08lx\n"
  97. "\tOverlapped.Internal = %p Overlapped.Offset= %08lx\n"
  98. "\tm_leTimeout.Flink = %p m_leTimeout.Blink= %p\n"
  99. "\tClientContext = %p ContextList = %p\n"
  100. "\tpfnCompletion = %p ()\n"
  101. "\tpEndPoint = %p fAcceptExContext = %s\n"
  102. "\tlSyncTimeout = %8d fInTimeout = %s\n"
  103. "\tTimeOut = %08lx NextTimeout = %08lx\n"
  104. "\tBytesSent = %d (0x%08lx)\n"
  105. "\tpvBuff = %p\n"
  106. "\tfConnectionIndicated= %s fBlocked = %8lx\n"
  107. "\tState = %8lx Flags = %8lx\n",
  108. this,
  109. hAsyncIO,
  110. Signature,
  111. Overlapped.Internal,
  112. Overlapped.Offset,
  113. m_leTimeout.Flink,
  114. m_leTimeout.Blink,
  115. ClientContext,
  116. ContextList,
  117. pfnCompletion,
  118. pEndpoint,
  119. (IsAcceptExRootContext() ? "TRUE" : "FALSE"),
  120. lSyncTimeout,
  121. (IsFlag( ACF_IN_TIMEOUT) ? "TRUE" : "FALSE"),
  122. TimeOut,
  123. NextTimeout,
  124. BytesSent,
  125. BytesSent,
  126. pvBuff,
  127. (IsFlag( ACF_CONN_INDICATED) ? "TRUE" : "FALSE"),
  128. IsBlocked(),
  129. m_acState, m_acFlags
  130. ));
  131. // Print the buffer if necessary.
  132. return;
  133. } // ATQ_CONTEXT::Print()
  134. VOID
  135. ATQ_CONTEXT::HardCloseSocket( VOID)
  136. /*++
  137. Description:
  138. This socket closes the socket by forcibly calling closesocket() on
  139. the socket. This function is used during the endpoint shutdown
  140. stage for an atq context
  141. Arguments:
  142. None
  143. Returns:
  144. None
  145. --*/
  146. {
  147. HANDLE haio = (HANDLE )
  148. InterlockedExchangePointer( (PVOID *)&hAsyncIO, NULL );
  149. DBG_ASSERT( IsState( ACS_SOCK_LISTENING) ||
  150. IsState( ACS_SOCK_CONNECTED) ||
  151. IsState( ACS_SOCK_CLOSED) ||
  152. IsState( ACS_SOCK_UNCONNECTED)
  153. );
  154. MoveState( ACS_SOCK_CLOSED);
  155. //
  156. // Let us do a hard close on the socket (handle).
  157. // This should generate an IO completion which will free this
  158. // ATQ context
  159. //
  160. if ( (haio != NULL) &&
  161. (closesocket( HANDLE_TO_SOCKET(haio) ) == SOCKET_ERROR)
  162. ) {
  163. ATQ_PRINTF(( DBG_CONTEXT,
  164. "Warning - "
  165. " Context=%08x closesocket failed,"
  166. " error %d, socket = %x\n",
  167. this,
  168. GetLastError(),
  169. haio ));
  170. Print();
  171. }
  172. return;
  173. } // ATQ_CONTEXT::HardCloseSocket()
  174. VOID
  175. ATQ_CONTEXT::InitWithDefaults(
  176. IN ATQ_COMPLETION pfnCompletion,
  177. IN DWORD TimeOut,
  178. IN HANDLE hAsyncIO
  179. )
  180. {
  181. DBG_ASSERT( this->Signature == ATQ_CONTEXT_SIGNATURE);
  182. this->InitTimeoutListEntry();
  183. this->Signature = ATQ_CONTEXT_SIGNATURE;
  184. // start life at 1. This ref count will be freed up by AtqFreeContext()
  185. this->m_nIO = 1;
  186. this->pfnCompletion = pfnCompletion;
  187. this->TimeOut = TimeOut;
  188. this->TimeOutScanID = 0;
  189. this->lSyncTimeout = 0;
  190. this->hAsyncIO = hAsyncIO;
  191. this->m_acState = 0;
  192. this->m_acFlags = 0;
  193. // Initialize pbandwidthinfo to point to global object
  194. this->m_pBandwidthInfo = g_pBandwidthInfo;
  195. ZeroMemory(
  196. &this->Overlapped,
  197. sizeof( this->Overlapped )
  198. );
  199. DBG_ASSERT( this->lSyncTimeout == 0);
  200. //
  201. // Following added for bandwidth throttling purposes
  202. //
  203. DBG_ASSERT( !this->IsBlocked());
  204. this->arInfo.atqOp = AtqIoNone;
  205. this->arInfo.lpOverlapped = NULL;
  206. // bandwidth throttling initialization ends here.
  207. //
  208. // Should we force socket closure?
  209. //
  210. this->m_fForceClose = FALSE;
  211. } // ATQ_CONTEXT::InitWithDefaults()
  212. VOID
  213. ATQ_CONTEXT::InitNonAcceptExState(
  214. IN PVOID pClientContext
  215. )
  216. {
  217. //
  218. // Note that if we're not using AcceptEx, then we consider the client
  219. // to have been notified externally (thus ACF_CONN_INDICATED is set).
  220. // Also we set the next timeout to be infinite, which may be reset
  221. // when the next IO is submitted.
  222. //
  223. this->NextTimeout = ATQ_INFINITE;
  224. this->ClientContext = pClientContext;
  225. this->pEndpoint = NULL;
  226. this->SetFlag( ACF_CONN_INDICATED);
  227. this->SetState( ACS_SOCK_CONNECTED);
  228. this->ResetFlag( ACF_ACCEPTEX_ROOT_CONTEXT);
  229. //
  230. // Insert this into the active list - since this is a non-acceptex socket
  231. //
  232. DBG_ASSERT( this->ContextList != NULL);
  233. this->ContextList->InsertIntoActiveList( &this->m_leTimeout );
  234. return;
  235. } // ATQ_CONTEXT::InitNonAcceptExState()
  236. VOID
  237. ATQ_CONTEXT::InitAcceptExState(
  238. IN DWORD NextTimeOut
  239. )
  240. {
  241. this->NextTimeout = NextTimeOut;
  242. this->ClientContext = NULL;
  243. this->lSyncTimeout = 0;
  244. this->ResetFlag( ACF_CONN_INDICATED);
  245. this->SetState( ACS_SOCK_LISTENING);
  246. //
  247. // Add it to the pending accept ex list
  248. //
  249. DBG_ASSERT( this->ContextList != NULL);
  250. this->ContextList->InsertIntoPendingList( &this->m_leTimeout);
  251. return;
  252. } // ATQ_CONTEXT::InitAcceptExState()
  253. BOOL
  254. ATQ_CONTEXT::PrepareAcceptExContext(
  255. PATQ_ENDPOINT pEndpoint
  256. )
  257. /*++
  258. Routine Description:
  259. Initializes the state for completely initializing the state and
  260. hence prepares the context for AcceptEx
  261. It expects the caller to send a AtqContext with certain characteristics
  262. 1) this is not NULL
  263. 2) this->pvBuff has valid values
  264. In the case of failure, caller should call
  265. pAtqContext->CleanupAndRelese() to free the memory associated with
  266. this object.
  267. Arguments:
  268. pEndpoint - pointer to endpoint object for this context
  269. Return Value:
  270. TRUE if successful, FALSE on error (call GetLastError)
  271. The caller should free the object on a failure.
  272. --*/
  273. {
  274. DBG_ASSERT( g_fUseAcceptEx); // only support AcceptEx() cases
  275. DBG_ASSERT( pEndpoint != NULL);
  276. DBG_ASSERT( this != NULL);
  277. DBG_ASSERT( this->pvBuff != NULL);
  278. //
  279. // Make sure that we are adding a AcceptEx() version of AtqContext
  280. //
  281. DBG_ASSERT( pEndpoint->ConnectExCompletion != NULL);
  282. DBG_ASSERT( pEndpoint->UseAcceptEx);
  283. //
  284. // Fill out the context. We set NextTimeout to INFINITE
  285. // so the timeout thread will ignore this entry until an IO
  286. // request is made unless this is an AcceptEx socket, that means
  287. // we're about to submit the IO.
  288. //
  289. this->
  290. InitWithDefaults(
  291. pEndpoint->IoCompletion,
  292. pEndpoint->AcceptExTimeout, // canonical Timeout
  293. this->hAsyncIO
  294. );
  295. //
  296. // TBD: What is the circumstance in which this->pEndpoint!= NULL?
  297. //
  298. if ( this->pEndpoint == NULL ) {
  299. pEndpoint->Reference();
  300. this->pEndpoint = pEndpoint;
  301. }
  302. this->ResetFlag( ACF_ACCEPTEX_ROOT_CONTEXT );
  303. this->InitAcceptExState( AtqGetCurrentTick() + TimeOut);
  304. DBG_ASSERT( this->pvBuff != NULL);
  305. //
  306. // Initialize capacity planning trace info
  307. //
  308. m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Guid = IISCapTraceGuid;
  309. m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Class.Type = EVENT_TRACE_TYPE_START;
  310. m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
  311. m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Size = sizeof (IIS_CAP_TRACE_HEADER);
  312. m_CapTraceInfo.IISCapTraceHeader.TraceContext.Length = sizeof(ULONGLONG);
  313. // Will get over-written
  314. m_CapTraceInfo.IISCapTraceHeader.TraceContext.DataPtr = (ULONGLONG)
  315. (&m_CapTraceInfo.IISCapTraceHeader.TraceContext.DataPtr);
  316. return (TRUE);
  317. } // ATQ_CONTEXT::PrepareAcceptExContext()
  318. VOID
  319. ATQ_CONTEXT::CleanupAndRelease( VOID)
  320. /*++
  321. Routine Description:
  322. This function does the cleanup of the ATQ context. It does not
  323. attempt to do any reuse of the atq context. After cleanup
  324. the context is freed to the ATQ pool. Supplied context
  325. is not valid after calling this function.
  326. Arguments:
  327. None
  328. Returns:
  329. None
  330. --*/
  331. {
  332. DBG_ASSERT( this->m_nIO == 0);
  333. //
  334. // Cleanup and free the ATQ Context entirely
  335. //
  336. if ( this->hAsyncIO != NULL ) {
  337. // It is too dangerous to assume that the handle is a socket!
  338. // But we will do that for fast-pathing IIS operations.
  339. HANDLE hTmp =
  340. (HANDLE)InterlockedExchangePointer( (PVOID *)&this->hAsyncIO,
  341. NULL );
  342. SOCKET hIO = HANDLE_TO_SOCKET(hTmp);
  343. if ( hIO != NULL &&
  344. (closesocket( hIO ) == SOCKET_ERROR ) ) {
  345. ATQ_PRINTF(( DBG_CONTEXT,
  346. "ATQ_CONTEXT(%08x)::CleanupAndRelease() : Warning"
  347. " - Context=%08x, "
  348. " closesocket failed, error %d, socket = %x\n",
  349. this,
  350. GetLastError(),
  351. hIO ));
  352. this->Print();
  353. }
  354. }
  355. DBG_ASSERT( this->hAsyncIO == NULL);
  356. if ( this->pvBuff != NULL ) {
  357. LocalFree( this->pvBuff );
  358. this->pvBuff = NULL;
  359. }
  360. //
  361. // Unlink from the list
  362. //
  363. DBG_ASSERT( this->ContextList != NULL);
  364. // NYI: Can I avoid this comparison?
  365. //
  366. // Check if this context is part of a timeout list.
  367. // If it is then remove it from the list
  368. // Only during shutdown code path, we will see trouble here.
  369. //
  370. if ( this->m_leTimeout.Flink != NULL ) {
  371. this->ContextList->RemoveFromList( &this->m_leTimeout);
  372. }
  373. //
  374. // Deref the listen info if this context is associated with one
  375. //
  376. if ( this->pEndpoint != NULL ) {
  377. this->pEndpoint->Dereference();
  378. this->pEndpoint = NULL;
  379. }
  380. this->Signature = ATQ_FREE_CONTEXT_SIGNATURE;
  381. this->lSyncTimeout = 0;
  382. this->m_acState = 0;
  383. this->m_acFlags = 0;
  384. I_AtqFreeContextToCache( this);
  385. return;
  386. } // ATQ_CONTEXT::CleanupAndRelease()
  387. VOID
  388. AtqpUpdateBandwidth( IN PATQ_CONT pAtqContext,
  389. IN DWORD cbWritten)
  390. {
  391. PBANDWIDTH_INFO pBandwidthInfo = pAtqContext->m_pBandwidthInfo;
  392. DBG_ASSERT( pBandwidthInfo != NULL );
  393. DBG_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
  394. // add the bandwidth info to active list if necessary
  395. pBandwidthInfo->AddToActiveList();
  396. //this will have problems when we use XmitFile for large files.
  397. pBandwidthInfo->UpdateBytesXfered( pAtqContext, cbWritten );
  398. } // AtqpUpdateBandwidth()
  399. VOID
  400. AtqpCallOplockCompletion( IN PATQ_CONT pAtqContext,
  401. IN DWORD cbWritten)
  402. {
  403. ATQ_OPLOCK_COMPLETION pfnOplockCompletion;
  404. PVOID OplockContext;
  405. POPLOCK_INFO pOplock;
  406. //
  407. // The ATQ context object received is a fake one. We actually get
  408. // back POPLOCK_INFO object that is used to extract the callback
  409. // function & context for the callback
  410. //
  411. pOplock = (POPLOCK_INFO)pAtqContext;
  412. pfnOplockCompletion = (ATQ_OPLOCK_COMPLETION)pOplock->pfnOplockCompletion;
  413. OplockContext = (PVOID)pOplock->Context;
  414. LocalFree(pOplock);
  415. (*pfnOplockCompletion)(OplockContext, (DWORD)cbWritten);
  416. return;
  417. } // AtqpCallOplockCompletion()
  418. VOID
  419. AtqpProcessContext( IN PATQ_CONT pAtqContext,
  420. IN DWORD cbWritten,
  421. IN LPOVERLAPPED lpo,
  422. IN BOOL fRet)
  423. {
  424. BOOL fRecvCalled = FALSE;
  425. DWORD dwError;
  426. DBG_ASSERT( pAtqContext != NULL);
  427. DBG_ASSERT( lpo != NULL); // this replaces the SPUD code. get rid of it
  428. if (lpo == NULL) {
  429. return;
  430. }
  431. dwError = (fRet) ? NO_ERROR: GetLastError();
  432. //
  433. // If this is an AcceptEx listen socket atq completion, then the
  434. // client Atq context we really want is keyed from the overlapped
  435. // structure that is stored in the client's Atq context.
  436. //
  437. if ( pAtqContext->IsAcceptExRootContext() ) {
  438. pAtqContext = CONTAINING_RECORD( lpo, ATQ_CONTEXT, Overlapped );
  439. }
  440. #if CC_REF_TRACKING
  441. //
  442. // ATQ notification trace
  443. //
  444. // Notify client context of all non-oplock notification.
  445. // This is for debugging purpose only.
  446. //
  447. pAtqContext->NotifyIOCompletion( cbWritten, (fRet) ? NO_ERROR: GetLastError(), 0xfefefefe );
  448. #endif
  449. DBG_CODE(
  450. if ( ATQ_CONTEXT_SIGNATURE != pAtqContext->Signature) {
  451. pAtqContext->Print();
  452. DBG_ASSERT( FALSE);
  453. });
  454. //
  455. // m_nIO also acts as the reference count for the atq contexts
  456. // So, increment the count now, so that there is no other thread
  457. // that will free up this ATQ context accidentally.
  458. //
  459. InterlockedIncrement( &pAtqContext->m_nIO);
  460. //
  461. // Busy wait for timeout processing to complete!
  462. // This is ugly :( A fix in time for IIS 2.0/Catapult 1.0 release
  463. //
  464. InterlockedIncrement( &pAtqContext->lSyncTimeout);
  465. while ( pAtqContext->IsFlag( ACF_IN_TIMEOUT)) {
  466. AcIncrement( CacAtqWaitsForTimeout);
  467. Sleep( ATQ_WAIT_FOR_TIMEOUT_PROCESSING);
  468. };
  469. //
  470. // We need to make sure the timeout thread doesn't time this
  471. // request out so reset the timeout value
  472. //
  473. InterlockedExchange( (LPLONG )&pAtqContext->NextTimeout,
  474. (LONG ) ATQ_INFINITE);
  475. //
  476. // Update Bandwidth information on successful completion, if needed
  477. //
  478. if ( BANDWIDTH_INFO::GlobalEnabled() && fRet && cbWritten > 0)
  479. {
  480. AtqpUpdateBandwidth( pAtqContext, cbWritten);
  481. }
  482. //
  483. // Since the IO completion means that one of the async operation finished
  484. // decrement our internal ref count appropriately to balance the addition
  485. // when the IO operation was submitted.
  486. //
  487. InterlockedDecrement( &pAtqContext->m_nIO);
  488. //
  489. // Is this a connection indication?
  490. //
  491. if ( !pAtqContext->IsFlag( ACF_CONN_INDICATED) ) {
  492. PATQ_ENDPOINT pEndpoint = pAtqContext->pEndpoint;
  493. if ( NULL == pEndpoint) {
  494. pAtqContext->Print();
  495. OutputDebugString( "Found an ATQ context with bad Endpoint\n");
  496. DBG_ASSERT( FALSE);
  497. DBG_REQUIRE( InterlockedDecrement( &pAtqContext->lSyncTimeout) == 0);
  498. InterlockedDecrement( &pAtqContext->m_nIO); // balance entry count
  499. return;
  500. }
  501. DBG_ASSERT( pEndpoint != NULL );
  502. //
  503. // If the endpoint isn't active it may not be safe to call
  504. // the connection completion function. Setting an error
  505. // state will close the connection below.
  506. //
  507. if ( fRet && !IS_BLOCK_ACTIVE( pEndpoint ) ) {
  508. fRet = FALSE;
  509. dwError = ERROR_OPERATION_ABORTED;
  510. }
  511. //
  512. // Indicate this socket is in use
  513. //
  514. InterlockedDecrement( &pEndpoint->nSocketsAvail );
  515. //
  516. // If we're running low on sockets, add some more now
  517. //
  518. if ( pEndpoint->nSocketsAvail <
  519. (LONG )(pEndpoint->nAcceptExOutstanding >> 2) ) {
  520. AcIncrement( CacAtqPrepareContexts);
  521. (VOID ) I_AtqPrepareAcceptExSockets(pEndpoint,
  522. pEndpoint->nAcceptExOutstanding
  523. );
  524. }
  525. //
  526. // If an error occurred on this completion,
  527. // shutdown the socket
  528. //
  529. if ( !fRet ) {
  530. IF_DEBUG( ERROR) {
  531. if ( dwError != ERROR_OPERATION_ABORTED &&
  532. dwError != ERROR_NETNAME_DELETED ) {
  533. ATQ_PRINTF(( DBG_CONTEXT,
  534. " Free Context(%08x, EP=%08x) to cache. "
  535. "Err=%d, sock=%08x\n",
  536. pAtqContext, pEndpoint,
  537. dwError,
  538. pAtqContext->hAsyncIO));
  539. }
  540. }
  541. DBG_REQUIRE( InterlockedDecrement( &pAtqContext->lSyncTimeout) == 0);
  542. InterlockedDecrement( &pAtqContext->m_nIO); // balance entry count
  543. // balance original count
  544. InterlockedDecrement( &pAtqContext->m_nIO);
  545. // Free up the atq context without Reuse
  546. pAtqContext->CleanupAndRelease();
  547. return;
  548. }
  549. //
  550. // Shutdown may close the socket from underneath us so don't
  551. // assert, just warn.
  552. //
  553. if ( !pAtqContext->IsState( ACS_SOCK_LISTENING) ) {
  554. ATQ_PRINTF(( DBG_CONTEXT,
  555. "[AtqPoolThread] Warning-Socket state not listening\n"
  556. ));
  557. DBG_CODE( pAtqContext->Print());
  558. }
  559. pAtqContext->MoveState( ACS_SOCK_CONNECTED);
  560. //
  561. // Remove the context from the pending list and put
  562. // it on the active list
  563. //
  564. DBG_ASSERT( pAtqContext->ContextList != NULL);
  565. pAtqContext->ContextList->MoveToActiveList( &pAtqContext->m_leTimeout);
  566. //
  567. // Set the connection indicated flag. After we return from
  568. // the connection completion routine we assume it's
  569. // safe to call the IO completion routine
  570. // (or the connection indication routine should do cleanup
  571. // and never issue an IO request). This is primarily for
  572. // the timeout thread.
  573. //
  574. pAtqContext->ConnectionCompletion( cbWritten, lpo);
  575. } else {
  576. //
  577. // Not a connection completion indication. I/O completion.
  578. //
  579. //
  580. // If an error occurred on a TransmitFile (or other IO),
  581. // set the state to connected so the socket will get
  582. // closed on cleanup
  583. //
  584. if ( !fRet &&
  585. pAtqContext->IsState( ACS_SOCK_UNCONNECTED)
  586. ){
  587. pAtqContext->MoveState( ACS_SOCK_CONNECTED);
  588. }
  589. pAtqContext->IOCompletion( cbWritten, dwError, lpo);
  590. }
  591. DBG_ASSERT( pAtqContext->lSyncTimeout > 0);
  592. InterlockedDecrement( &pAtqContext->lSyncTimeout);
  593. //
  594. // We do an interlocked decrement on m_nIO to sync up state
  595. // so that the context is not prematurely deleted.
  596. //
  597. if ( InterlockedDecrement( &pAtqContext->m_nIO) == 0) {
  598. //
  599. // The number of outstanding ref holders is ZERO.
  600. // Free up this ATQ context.
  601. //
  602. // We really do not free up the context - but try to reuse
  603. // it if possible
  604. //
  605. // free the atq context now or reuse if possible.
  606. AtqpReuseOrFreeContext( pAtqContext,
  607. (pAtqContext->
  608. IsFlag( ACF_REUSE_CONTEXT) != 0)
  609. );
  610. }
  611. return;
  612. } // AtqpProcessContext()
  613. DWORD
  614. AtqPoolThread(
  615. LPDWORD param
  616. )
  617. /*++
  618. Routine Description:
  619. This is the pool thread wait and dispatch routine
  620. Arguments:
  621. param : unused.
  622. Return Value:
  623. Thread return value (ignored)
  624. --*/
  625. {
  626. PATQ_CONT pAtqContext = NULL;
  627. BOOL fRet;
  628. LPOVERLAPPED lpo;
  629. DWORD cbWritten;
  630. DWORD returnValue;
  631. DWORD availThreads;
  632. for(;;) {
  633. pAtqContext = NULL;
  634. InterlockedIncrement( &g_cAvailableThreads );
  635. fRet = g_pfnGetQueuedCompletionStatus( g_hCompPort,
  636. &cbWritten,
  637. (PULONG_PTR)&pAtqContext,
  638. &lpo,
  639. g_msThreadTimeout );
  640. availThreads = InterlockedDecrement( &g_cAvailableThreads );
  641. if ( fRet || lpo ) {
  642. if ( pAtqContext == NULL) {
  643. if ( g_fShutdown ) {
  644. //
  645. // This is our signal to exit.
  646. //
  647. returnValue = NO_ERROR;
  648. break;
  649. }
  650. OutputDebugString( "A null context received\n");
  651. continue; // some error in the context has occured.
  652. }
  653. //
  654. // Make sure we're not running out of threads
  655. //
  656. if ( availThreads == 0 ) {
  657. //
  658. // Make sure there are pool threads to service the request
  659. //
  660. (VOID)I_AtqCheckThreadStatus();
  661. }
  662. if ( (ULONG_PTR) param == ATQ_DEBUG_THREAD )
  663. {
  664. //
  665. // If this is a DEBUG thread, we are only concerned with new
  666. // connections. Anything else we ignore.
  667. //
  668. if ( pAtqContext->IsFlag( ACF_CONN_INDICATED ) )
  669. {
  670. continue;
  671. }
  672. }
  673. //
  674. // Capacity planning log
  675. //
  676. if (IISCapTraceFlag && pAtqContext)
  677. {
  678. PIIS_CAP_TRACE_INFO pCapTraceInfo = pAtqContext->GetCapTraceInfo();
  679. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Class.Type = EVENT_TRACE_TYPE_START;
  680. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Size = sizeof (IIS_CAP_TRACE_HEADER);
  681. pCapTraceInfo->IISCapTraceHeader.TraceContext.DataPtr = (ULONGLONG) &pAtqContext;
  682. pCapTraceInfo->IISCapTraceHeader.TraceContext.Length = sizeof(ULONG_PTR);
  683. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
  684. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Guid = IISCapTraceGuid;
  685. if ( ERROR_INVALID_HANDLE == TraceEvent ( IISCapTraceLoggerHandle,
  686. (PEVENT_TRACE_HEADER) pCapTraceInfo))
  687. {
  688. IISCapTraceFlag = FALSE;
  689. }
  690. }
  691. AtqpProcessContext( pAtqContext, cbWritten, lpo, fRet);
  692. //
  693. // Capacity planning log
  694. //
  695. if (IISCapTraceFlag && pAtqContext)
  696. {
  697. PIIS_CAP_TRACE_INFO pCapTraceInfo = pAtqContext->GetCapTraceInfo();
  698. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Class.Type = EVENT_TRACE_TYPE_END;
  699. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Size = sizeof (IIS_CAP_TRACE_HEADER);
  700. pCapTraceInfo->IISCapTraceHeader.TraceContext.DataPtr = (ULONGLONG) &pAtqContext;
  701. pCapTraceInfo->IISCapTraceHeader.TraceContext.Length = sizeof(ULONG_PTR);
  702. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Guid = IISCapTraceGuid;
  703. pCapTraceInfo->IISCapTraceHeader.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
  704. if ( ERROR_INVALID_HANDLE == TraceEvent ( IISCapTraceLoggerHandle,
  705. (PEVENT_TRACE_HEADER) pCapTraceInfo))
  706. {
  707. IISCapTraceFlag = FALSE;
  708. }
  709. }
  710. } else {
  711. //
  712. // don't kill the initial thread - any thread that doesn't have
  713. // pending I/Os go ahead and allow to die
  714. //
  715. if ( ((ULONG_PTR)param == ATQ_INITIAL_THREAD) && !g_fShutdown ) {
  716. continue;
  717. }
  718. if ( !g_fShutdown && GetLastError() == WAIT_TIMEOUT ) {
  719. NTSTATUS status;
  720. ULONG flag;
  721. status = NtQueryInformationThread(
  722. NtCurrentThread(),
  723. ThreadIsIoPending,
  724. &flag,
  725. sizeof(flag),
  726. NULL );
  727. IF_DEBUG( TIMEOUT ) {
  728. ATQ_PRINTF(( DBG_CONTEXT,
  729. "[ATQ Pool Thread] NtQueryInformationThread() returned 0x%08x, flag = %d\n",
  730. status,
  731. flag ));
  732. }
  733. if ( NT_SUCCESS( status ) && flag ) {
  734. //
  735. // There are pending I/Os on this thread so don't exit
  736. //
  737. continue;
  738. }
  739. }
  740. IF_DEBUG( TIMEOUT ) {
  741. ATQ_PRINTF(( DBG_CONTEXT,
  742. "[ATQ Pool Thread] Exiting thread 0x%x\n",
  743. GetCurrentThread() ));
  744. }
  745. //
  746. // An error occurred. Either the thread timed out, the handle
  747. // is going away or something bad happened. Let the thread exit.
  748. //
  749. returnValue = GetLastError();
  750. break;
  751. }
  752. } // for
  753. if ( NULL != g_pfnExitThreadCallback) {
  754. //
  755. // Client wishes to be told when ATQ threads terminate.
  756. //
  757. g_pfnExitThreadCallback();
  758. }
  759. if ( InterlockedDecrement( &g_cThreads ) == 0 ) {
  760. //
  761. // Wake up ATQTerminate()
  762. //
  763. IF_DEBUG( ERROR) {
  764. ATQ_PRINTF(( DBG_CONTEXT,
  765. "AtqPoolThread() - setting shutdown event %08x."
  766. " g_cThreads = %d\n",
  767. g_hShutdownEvent, g_cThreads
  768. ));
  769. }
  770. SetEvent( g_hShutdownEvent );
  771. }
  772. return returnValue;
  773. } // AtqPoolThread
  774. BOOL
  775. I_AtqCheckThreadStatus(
  776. PVOID Context
  777. )
  778. /*++
  779. Routine Description:
  780. This routine makes sure there is at least one thread in
  781. the thread pool. We're fast and loose so a couple of extra
  782. threads may be created.
  783. Arguments:
  784. Return Value:
  785. TRUE if successful, FALSE on error (call GetLastError)
  786. --*/
  787. {
  788. BOOL fRet = TRUE;
  789. BOOL fIsDebugThread = ( (ULONG_PTR) Context == ( ATQ_DEBUG_THREAD ) );
  790. //
  791. // If no threads are available, kick a new one off up to the limit
  792. //
  793. // WE NEED TO CHANGE THE CONDITIONS FOR STARTING ANOTHER THREAD
  794. // IT SHOULD NOT BE VERY EASY TO START A THREAD ....
  795. //
  796. if ( ( (g_cAvailableThreads == 0) &&
  797. (g_cThreads < g_cMaxThreads) &&
  798. (g_cThreads < g_cMaxThreadLimit) ) ||
  799. fIsDebugThread )
  800. {
  801. HANDLE hThread;
  802. DWORD dwThreadID;
  803. if ( !fIsDebugThread )
  804. {
  805. InterlockedIncrement( &g_cThreads );
  806. }
  807. hThread = CreateThread( NULL,
  808. 0,
  809. (LPTHREAD_START_ROUTINE)AtqPoolThread,
  810. Context,
  811. 0,
  812. &dwThreadID );
  813. if ( hThread ) {
  814. CloseHandle( hThread ); // Free system resources
  815. } else if ( !fIsDebugThread ) {
  816. //
  817. // We fail if there are no threads running
  818. //
  819. if ( InterlockedDecrement( &g_cThreads ) == 0) {
  820. ATQ_PRINTF(( DBG_CONTEXT,
  821. "AtqCheckThread: Cannot create ATQ threads\n"));
  822. fRet = FALSE;
  823. }
  824. }
  825. }
  826. return fRet;
  827. } // I_AtqCheckThreadStatus()
  828. /************************************************************
  829. * Functions to Add/Delete Atq Contexts
  830. ************************************************************/
  831. BOOL
  832. I_AtqAddAsyncHandle(
  833. IN OUT PATQ_CONT * ppAtqContext,
  834. IN PATQ_ENDPOINT pEndpoint,
  835. PVOID ClientContext,
  836. ATQ_COMPLETION pfnCompletion,
  837. DWORD TimeOut,
  838. HANDLE hAsyncIO
  839. )
  840. /*++
  841. Description:
  842. This functio adds creates a new NON-AcceptEx() based Atq Context,
  843. and includes it in proper lists fo ATQ Context management.
  844. Note:
  845. The client should call this after the IO handle is openned
  846. and before the first IO request is made
  847. Even in the case of failure, client should call AtqFreeContext() and
  848. free the memory associated with this object.
  849. --*/
  850. {
  851. BOOL fReturn = TRUE;
  852. DBG_ASSERT( ppAtqContext != NULL);
  853. DBG_ASSERT( ClientContext != NULL);
  854. *ppAtqContext = NULL; // initialize
  855. if ( g_fShutdown) {
  856. SetLastError( ERROR_NOT_READY);
  857. return (FALSE);
  858. } else {
  859. PATQ_CONT pAtqContext;
  860. //
  861. // Note we take and release the lock here as we're
  862. // optimizing for the reuseable context case
  863. //
  864. pAtqContext = I_AtqAllocContextFromCache();
  865. if ( pAtqContext == NULL) {
  866. return (FALSE);
  867. }
  868. //
  869. // Fill out the context. We set NextTimeout to INFINITE
  870. // so the timeout thread will ignore this entry until an IO
  871. // request is made unless this is an AcceptEx socket, that means
  872. // we're about to submit the IO.
  873. //
  874. pAtqContext->InitWithDefaults(pfnCompletion,
  875. CanonTimeout( TimeOut ), hAsyncIO);
  876. //
  877. // These data members are used if we're doing AcceptEx processing
  878. //
  879. pAtqContext->SetAcceptExBuffer( NULL);
  880. pAtqContext->InitNonAcceptExState(ClientContext);
  881. //
  882. // If an endpoint is provided, reference it
  883. //
  884. if ( pEndpoint != NULL ) {
  885. pEndpoint->Reference();
  886. pAtqContext->pEndpoint = pEndpoint;
  887. }
  888. *ppAtqContext = pAtqContext;
  889. }
  890. return (TRUE);
  891. } // I_AtqAddAsyncHandle()
  892. BOOL
  893. I_AtqAddListenEndpointToPort(
  894. IN OUT PATQ_CONT * ppAtqContext,
  895. IN PATQ_ENDPOINT pEndpoint
  896. )
  897. /*++
  898. Description:
  899. This function creates a new AtqContext for the given ListenSocket.
  900. It uses the listen socket as the AcceptEx() socket too for adding
  901. the atq context to the completion port.
  902. It assumes
  903. TimeOut to be INFINITE, with no Endpoint structure.
  904. Arguments:
  905. ppAtqContext - pointer to location that will contain the atq context
  906. on successful return.
  907. pEndpoint - pointer to the endpoint.
  908. Returns:
  909. TRUE on success
  910. FALSE if there is a failure.
  911. Note:
  912. The caller should free the *ppAtqContext if there is a failure.
  913. --*/
  914. {
  915. BOOL fReturn = TRUE;
  916. PATQ_CONT pAtqContext;
  917. DBG_ASSERT( g_fUseAcceptEx); // only support AcceptEx() cases
  918. *ppAtqContext = NULL; // initialize
  919. if ( g_fShutdown) {
  920. SetLastError( ERROR_NOT_READY);
  921. return (FALSE);
  922. } else {
  923. //
  924. // Note we take and release the lock here as we're
  925. // optimizing for the reuseable context case
  926. //
  927. pAtqContext = I_AtqAllocContextFromCache();
  928. if ( pAtqContext == NULL) {
  929. return (FALSE);
  930. }
  931. //
  932. // Fill out the context.
  933. // We set the TimeOut for this object to be ATQ_INFINITE,
  934. // since we do not want any interference from the Timeout loop.
  935. //
  936. pAtqContext->InitWithDefaults(
  937. pEndpoint->IoCompletion,
  938. ATQ_INFINITE,
  939. SOCKET_TO_HANDLE(pEndpoint->ListenSocket)
  940. );
  941. //
  942. // These data members are used if we're doing AcceptEx processing
  943. //
  944. pAtqContext->SetAcceptExBuffer( NULL);
  945. //
  946. // Among AcceptEx ATQ Contexts,
  947. // only the listen ATQ context will have the Endpoint field as NULL
  948. //
  949. pAtqContext->pEndpoint = NULL;
  950. pAtqContext->SetFlag( ACF_ACCEPTEX_ROOT_CONTEXT );
  951. //
  952. // We set NextTimeout to INFINITE
  953. // so the timeout thread will ignore this entry until an IO
  954. // request is made unless this is an AcceptEx socket, that means
  955. // we're about to submit the IO.
  956. DBG_ASSERT( g_fUseAcceptEx && pEndpoint->ConnectExCompletion != NULL);
  957. pAtqContext->InitAcceptExState( ATQ_INFINITE);
  958. *ppAtqContext = pAtqContext;
  959. }
  960. fReturn = I_AddAtqContextToPort( pAtqContext);
  961. return (fReturn);
  962. } // I_AtqAddListenEndpointToPort()
  963. BOOL
  964. I_AtqAddAcceptExSocket(
  965. IN PATQ_ENDPOINT pEndpoint,
  966. IN PATQ_CONT pAtqContext
  967. )
  968. /*++
  969. Routine Description:
  970. Adds the AtqContext to the AcceptEx() waiters list,
  971. after allocating a new socket, since pAtqContext->hAsyncIO = NULL.
  972. Arguments:
  973. pEndpoint - Information about this listenning socket
  974. patqReusedContext - optional context to use
  975. Return Value:
  976. TRUE on success, FALSE on failure.
  977. On failure the caller should free the pAtqContext
  978. --*/
  979. {
  980. BOOL fAddToPort = FALSE;
  981. BOOL fSuccess = TRUE;
  982. DBG_ASSERT( pAtqContext != NULL);
  983. DBG_ASSERT( g_pfnAcceptEx != NULL);
  984. DBG_ASSERT( pAtqContext->pvBuff != NULL);
  985. //
  986. // If this listen socket isn't accepting new connections, just return
  987. //
  988. if ( !IS_BLOCK_ACTIVE(pEndpoint) ) {
  989. SetLastError( ERROR_NOT_READY );
  990. return ( FALSE);
  991. }
  992. //
  993. // Use the supplied socket if any.
  994. // Otherwise create a new socket
  995. //
  996. if ( pAtqContext->hAsyncIO == NULL) {
  997. SOCKET sAcceptSocket;
  998. #if WINSOCK11
  999. sAcceptSocket = socket(
  1000. AF_INET,
  1001. SOCK_STREAM,
  1002. IPPROTO_TCP
  1003. );
  1004. #else
  1005. sAcceptSocket = WSASocketW(
  1006. AF_INET,
  1007. SOCK_STREAM,
  1008. IPPROTO_TCP,
  1009. NULL, // protocol info
  1010. 0, // Group ID = 0 => no constraints
  1011. WSA_FLAG_OVERLAPPED // completion port notifications
  1012. );
  1013. #endif // WINSOCK11
  1014. if ( sAcceptSocket == INVALID_SOCKET ) {
  1015. fSuccess = FALSE;
  1016. sAcceptSocket = NULL;
  1017. //
  1018. // no need to unlink from any list, since we did not add it to any
  1019. //
  1020. } else {
  1021. //
  1022. // Setup the accept ex socket in the atq context.
  1023. //
  1024. pAtqContext->hAsyncIO = SOCKET_TO_HANDLE(sAcceptSocket);
  1025. fAddToPort = TRUE;
  1026. DBG_ASSERT( fSuccess);
  1027. }
  1028. }
  1029. if ( fSuccess) {
  1030. DWORD cbRecvd;
  1031. if ( g_fShutdown) {
  1032. //
  1033. // no need to unlink from any list, since we did not add it to any
  1034. //
  1035. SetLastError( ERROR_NOT_READY);
  1036. return (FALSE);
  1037. }
  1038. DBG_ASSERT( pAtqContext->hAsyncIO != NULL);
  1039. //
  1040. // 1. Call I_AtqAddAsyncHandleEx() to establish the links with
  1041. // proper AcceptEx & AtqContext processing lists.
  1042. //
  1043. // After 1, the atqcontext will be in the lists, so
  1044. // cleanup should remove the context from proper lists.
  1045. //
  1046. // 2. Add the socket to Completion Port (if new),
  1047. // i.e. if fAddToPort is true)
  1048. //
  1049. // 3. Submit the new socket to AcceptEx() so that it may be
  1050. // used for processing about the new connections.
  1051. //
  1052. // 1.
  1053. DBG_REQUIRE( pAtqContext->PrepareAcceptExContext(pEndpoint));
  1054. // increment outstanding async io operations before AcceptEx() call
  1055. InterlockedIncrement( &pAtqContext->m_nIO);
  1056. fSuccess = (// 2.
  1057. ( !fAddToPort || I_AddAtqContextToPort( pAtqContext))
  1058. &&
  1059. // 3.
  1060. (
  1061. g_pfnAcceptEx( pEndpoint->ListenSocket,
  1062. HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
  1063. pAtqContext->pvBuff,
  1064. pEndpoint->InitialRecvSize,
  1065. MIN_SOCKADDR_SIZE,
  1066. MIN_SOCKADDR_SIZE,
  1067. &cbRecvd,
  1068. &pAtqContext->Overlapped )
  1069. ||
  1070. (GetLastError() == ERROR_IO_PENDING)
  1071. )
  1072. );
  1073. if ( fSuccess) {
  1074. //
  1075. // We've successfully added this socket, increment the count
  1076. //
  1077. InterlockedIncrement( &pEndpoint->nSocketsAvail );
  1078. } else {
  1079. ATQ_PRINTF(( DBG_CONTEXT,
  1080. "[AtqAddAcceptExSocket] Reusing an old context (%08x)"
  1081. " failed; error %d:%d, sAcceptSocket = %x, "
  1082. " pEndpoint = %lx, parm4 = %d, parm7 = %lx,"
  1083. " parm8 = %lx\n",
  1084. pAtqContext,
  1085. GetLastError(),
  1086. WSAGetLastError(),
  1087. pAtqContext->hAsyncIO,
  1088. pEndpoint,
  1089. pEndpoint->InitialRecvSize,
  1090. &cbRecvd,
  1091. &pAtqContext->Overlapped ));
  1092. //
  1093. // Unlink from the current list, where it was added as a result of
  1094. // step 1 above.
  1095. //
  1096. DBG_ASSERT( pAtqContext->ContextList != NULL);
  1097. // balance the increment of the async operations outstanding
  1098. DBG_REQUIRE( InterlockedDecrement( &pAtqContext->m_nIO) > 0);
  1099. DBG_ASSERT( pAtqContext->m_leTimeout.Flink != NULL);
  1100. pAtqContext->ContextList->
  1101. RemoveFromList( &pAtqContext->m_leTimeout);
  1102. //
  1103. // balance the increment done
  1104. // by pAtqContext->PrepareAcceptExContext()
  1105. //
  1106. DBG_REQUIRE( InterlockedDecrement( &pAtqContext->m_nIO) == 0);
  1107. DBG_ASSERT( !fSuccess);
  1108. //
  1109. // the caller will free the Atq context on failure
  1110. //
  1111. }
  1112. }
  1113. return ( fSuccess);
  1114. } // I_AtqAddAcceptExSocket()
  1115. VOID
  1116. AtqpReuseContext( PATQ_CONT pAtqContext)
  1117. /*++
  1118. Description:
  1119. This function attempts to reuse the ATQ context.
  1120. It first cleans up the state and then uses the function
  1121. I_AtqAddAccetpEx() socket to re-add the context to acceptex pool
  1122. Arguments:
  1123. pAtqContext - pointer to ATQ context that can be reused
  1124. Returns:
  1125. None
  1126. --*/
  1127. {
  1128. PATQ_ENDPOINT pEndpoint = pAtqContext->pEndpoint;
  1129. DBG_ASSERT( pEndpoint != NULL);
  1130. DBG_ASSERT( pEndpoint->UseAcceptEx);
  1131. //
  1132. // Complete connection has been processed prior to coming here
  1133. //
  1134. DBG_ASSERT(pAtqContext->IsFlag( ACF_CONN_INDICATED));
  1135. //
  1136. // Remove from the current active list
  1137. //
  1138. if ( pAtqContext->m_leTimeout.Flink != NULL ) {
  1139. pAtqContext->ContextList->RemoveFromList( &pAtqContext->m_leTimeout );
  1140. }
  1141. DBG_ASSERT( pAtqContext->m_leTimeout.Flink == NULL);
  1142. DBG_ASSERT( pAtqContext->m_leTimeout.Blink == NULL);
  1143. DBG_ASSERT( pEndpoint->Signature == ATQ_ENDPOINT_SIGNATURE );
  1144. //
  1145. // Either there is no socket or the socket must be in the
  1146. // unconnected state (meaning reused after TransmitFile)
  1147. //
  1148. if ( !(!pAtqContext->hAsyncIO ||
  1149. (pAtqContext->hAsyncIO &&
  1150. pAtqContext->IsState( ACS_SOCK_UNCONNECTED |
  1151. ACS_SOCK_TOBE_FREED)
  1152. )
  1153. )) {
  1154. ATQ_PRINTF(( DBG_CONTEXT,
  1155. "[AtqReuseContext] Warning:"
  1156. " state = %08x, socket = %x (context %lx), "
  1157. " was Free called w/o close?\n",
  1158. pAtqContext->m_acState,
  1159. pAtqContext->hAsyncIO,
  1160. pAtqContext ));
  1161. DBG_ASSERT( FALSE);
  1162. }
  1163. //
  1164. // Need to make sure that the state information is cleaned up
  1165. // before re-adding the context to the list. Also reset socket options.
  1166. //
  1167. if ( pAtqContext->hAsyncIO && pAtqContext->IsFlag(ACF_TCP_NODELAY))
  1168. {
  1169. INT optValue = 0;
  1170. setsockopt( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
  1171. IPPROTO_TCP,
  1172. TCP_NODELAY,
  1173. (char *)&optValue,
  1174. sizeof(INT)
  1175. );
  1176. //
  1177. // No need to reset the flag. It will be 0'd out during the Add
  1178. //
  1179. }
  1180. if ( !I_AtqAddAcceptExSocket(pEndpoint, pAtqContext) ) {
  1181. //
  1182. // Failed to add the socket, free up the context without reuse
  1183. //
  1184. ATQ_PRINTF(( DBG_CONTEXT,
  1185. "[AtqpReuseContext] for (%08x) failed with "
  1186. " Error = %d; Now freeing the context ...\n",
  1187. pAtqContext, GetLastError()
  1188. ));
  1189. DBG_ASSERT( pAtqContext->m_nIO == 0);
  1190. // free without reuse
  1191. pAtqContext->CleanupAndRelease();
  1192. }
  1193. return;
  1194. } // AtqpReuseContext()
  1195. VOID
  1196. AtqpReuseOrFreeContext(
  1197. PATQ_CONT pAtqContext,
  1198. BOOL fReuseContext
  1199. )
  1200. /*++
  1201. Routine Description:
  1202. This function does a free-up of the ATQ contexts. During the free-up
  1203. path, we also attempt to reuse the ATQ context if the fReuseContext is
  1204. set.
  1205. Arguments:
  1206. pAtqContext - pointer to the ATQ context that needs to be freedup
  1207. fReuseContext - BOOLEAN flag indicating if this context should be reused
  1208. Returns:
  1209. None
  1210. --*/
  1211. {
  1212. //
  1213. // Get this object out of the Blocked Requests List.
  1214. //
  1215. if ( pAtqContext->IsBlocked()) {
  1216. ATQ_REQUIRE( pAtqContext->m_pBandwidthInfo
  1217. ->RemoveFromBlockedList( pAtqContext ));
  1218. DBG_ASSERT( !pAtqContext->IsBlocked());
  1219. }
  1220. DBG_ASSERT( pAtqContext->m_pBandwidthInfo != NULL);
  1221. pAtqContext->m_pBandwidthInfo->Dereference();
  1222. //
  1223. // Conditions for Reuse:
  1224. // 1) fReuseContext == TRUE => caller wants us to reuse context
  1225. // 2) pAtqContext->pEndpoint != NULL => valid endpoint exists
  1226. // 3) pEndpoint->UseAcceptEx => AcceptEx is enabled
  1227. // 4) pEndpoint->nSocketsAvail < nAcceptExOutstanding * 2 =>
  1228. // We do not have lots of outstanding idle sockets
  1229. // Condition (4) ensures that we do not flood the system
  1230. // with too many AcceptEx sockets as a result of some spike.
  1231. // AcceptEx sockets once added to the pool are hard to
  1232. // remove, because of various timing problems.
  1233. // Hence we want to prevent arbitrarily adding AcceptEx sockets.
  1234. //
  1235. // In condition (4) I use a fudge factor of "2", so that
  1236. // we do continue to prevent reuse of sockets prematurely.
  1237. //
  1238. if ( fReuseContext &&
  1239. (pAtqContext->pEndpoint != NULL) &&
  1240. (pAtqContext->pEndpoint->UseAcceptEx)
  1241. &&
  1242. ( g_fAlwaysReuseSockets ||
  1243. ((DWORD )pAtqContext->pEndpoint->nSocketsAvail <
  1244. pAtqContext->pEndpoint->nAcceptExOutstanding * 2)
  1245. )
  1246. ) {
  1247. //
  1248. // Call the function to reuse context. On failure
  1249. // the AtqpReuseContext will free up the context
  1250. //
  1251. AcIncrement( CacAtqContextsReused);
  1252. AtqpReuseContext( pAtqContext);
  1253. } else {
  1254. AcIncrement( CacAtqContextsCleanedup);
  1255. pAtqContext->CleanupAndRelease();
  1256. }
  1257. return;
  1258. } // AtqpReuseOrFreeContext()
  1259. BOOL
  1260. I_AtqPrepareAcceptExSockets(
  1261. IN PATQ_ENDPOINT pEndpoint,
  1262. IN DWORD nSockets
  1263. )
  1264. /*++
  1265. Routine Description:
  1266. Prepare specified number of AcceptEx sockets for the given
  1267. ListenSocket in [pEndpoint]
  1268. Arguments:
  1269. pEndpoint - Information about this listenning socket
  1270. nSockets - number of AcceptEx() sockets to be created.
  1271. Return Value:
  1272. TRUE on success, FALSE on failure.
  1273. --*/
  1274. {
  1275. BOOL fReturn;
  1276. DWORD cbBuffer;
  1277. DWORD i;
  1278. if ( !g_fUseAcceptEx ) {
  1279. SetLastError( ERROR_NOT_SUPPORTED );
  1280. return FALSE;
  1281. }
  1282. //
  1283. // If this listen socket isn't accepting new connections, just return
  1284. //
  1285. if ( pEndpoint->State != AtqStateActive ) {
  1286. SetLastError( ERROR_NOT_READY );
  1287. return(FALSE);
  1288. }
  1289. if ( pEndpoint->fAddingSockets) {
  1290. //
  1291. // Someone is already adding sockets. Do not add more
  1292. // Just return success
  1293. //
  1294. return ( TRUE);
  1295. }
  1296. pEndpoint->fAddingSockets = TRUE;
  1297. // calculate the buffer size
  1298. cbBuffer = pEndpoint->InitialRecvSize + 2* MIN_SOCKADDR_SIZE;
  1299. for ( fReturn = TRUE, i = 0 ; fReturn && i++ < nSockets; ) {
  1300. PVOID pvBuff;
  1301. PATQ_CONT pAtqContext = NULL;
  1302. //
  1303. // Alloc a buffer for receive data
  1304. // TBD: Pool all these buffers into one large buffer.
  1305. //
  1306. pvBuff = LocalAlloc( LPTR, cbBuffer);
  1307. //
  1308. // Get the ATQ context now because we need its overlapped structure
  1309. //
  1310. if (pvBuff != NULL)
  1311. pAtqContext = I_AtqAllocContextFromCache();
  1312. //
  1313. // Now check if allocations are valid and do proper cleanup on failure
  1314. //
  1315. if ( pvBuff == NULL || pAtqContext == NULL) {
  1316. if ( pvBuff ) {
  1317. LocalFree( pvBuff );
  1318. pvBuff = NULL;
  1319. }
  1320. if ( pAtqContext ) {
  1321. pAtqContext->Signature = ATQ_FREE_CONTEXT_SIGNATURE;
  1322. I_AtqFreeContextToCache( pAtqContext );
  1323. pAtqContext = NULL;
  1324. }
  1325. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  1326. fReturn = FALSE;
  1327. break;
  1328. } else {
  1329. //
  1330. // Add this socket to AtqContext lists & completion ports
  1331. // From now on the called function will take care of freeing up
  1332. // pAtqContext, if there is a failure.
  1333. //
  1334. pAtqContext->SetAcceptExBuffer( pvBuff);
  1335. pAtqContext->hAsyncIO = NULL;
  1336. if ( !I_AtqAddAcceptExSocket(pEndpoint, pAtqContext) ) {
  1337. //
  1338. // Failed to add the socket, free up the context without reuse
  1339. //
  1340. ATQ_PRINTF(( DBG_CONTEXT,
  1341. "[I_AtqPrepareAcceptExSockets] for Endpoint %08x"
  1342. " and AtqContext (%08x) failed with "
  1343. " Error = %d; Now freeing the context ...\n",
  1344. pEndpoint, pAtqContext, GetLastError()
  1345. ));
  1346. DWORD dwError = GetLastError();
  1347. // free without reuse
  1348. DBG_ASSERT( pAtqContext->m_nIO == 0);
  1349. pAtqContext->CleanupAndRelease();
  1350. SetLastError(dwError);
  1351. fReturn = FALSE;
  1352. }
  1353. }
  1354. } // for
  1355. //
  1356. // Finished Adding sockets. Indicate that by resetting the flab
  1357. //
  1358. pEndpoint->fAddingSockets = FALSE;
  1359. ATQ_PRINTF(( DBG_CONTEXT,
  1360. "PrepareAcceptExSockets( Endpoint[%08x], nSockets = %d)==>"
  1361. " avail = %d; Total Refs = %d.\n",
  1362. pEndpoint,
  1363. nSockets,
  1364. pEndpoint->nSocketsAvail,
  1365. pEndpoint->m_refCount
  1366. ));
  1367. return ( fReturn);
  1368. } // I_AtqPrepareAcceptExSockets()
  1369. BOOL
  1370. I_AtqInitializeNtEntryPoints(
  1371. VOID
  1372. )
  1373. {
  1374. HINSTANCE tmpInstance;
  1375. //
  1376. // load kernel32 and get NT specific entry points
  1377. //
  1378. tmpInstance = LoadLibrary("kernel32.dll");
  1379. if ( tmpInstance != NULL ) {
  1380. g_pfnReadDirChangesW = (PFN_READ_DIR_CHANGES_W)
  1381. GetProcAddress( tmpInstance, "ReadDirectoryChangesW");
  1382. DBG_ASSERT(g_pfnReadDirChangesW != NULL);
  1383. //
  1384. // We can free this because we are statically linked to it
  1385. //
  1386. FreeLibrary(tmpInstance);
  1387. }
  1388. g_hMSWsock = LoadLibrary( "mswsock.dll" );
  1389. if ( g_hMSWsock != NULL ) {
  1390. SOCKET sTempSocket = INVALID_SOCKET;
  1391. GUID guidTransmitFile = WSAID_TRANSMITFILE;
  1392. GUID guidAcceptEx = WSAID_ACCEPTEX;
  1393. GUID guidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
  1394. DWORD cbReturned;
  1395. //
  1396. // Lets create a temporary socket so that we can use WSAIoctl to
  1397. // get the direct function pointers for AcceptEx(), TransmitFile(),
  1398. // etc.
  1399. //
  1400. sTempSocket = WSASocketW( AF_INET,
  1401. SOCK_STREAM,
  1402. IPPROTO_TCP,
  1403. NULL,
  1404. 0,
  1405. 0 );
  1406. if ( sTempSocket == INVALID_SOCKET )
  1407. {
  1408. ATQ_PRINTF(( DBG_CONTEXT,
  1409. "Failed to create temp socket "
  1410. "for determining WinSock provider. Error = %d",
  1411. WSAGetLastError() ));
  1412. goto cleanup;
  1413. }
  1414. WSAIoctl( sTempSocket,
  1415. SIO_GET_EXTENSION_FUNCTION_POINTER,
  1416. (LPVOID) &guidTransmitFile,
  1417. sizeof( guidTransmitFile ),
  1418. &g_pfnTransmitFile,
  1419. sizeof( g_pfnTransmitFile ),
  1420. &cbReturned,
  1421. NULL,
  1422. NULL );
  1423. WSAIoctl( sTempSocket,
  1424. SIO_GET_EXTENSION_FUNCTION_POINTER,
  1425. (LPVOID) &guidAcceptEx,
  1426. sizeof( guidAcceptEx ),
  1427. &g_pfnAcceptEx,
  1428. sizeof( g_pfnAcceptEx ),
  1429. &cbReturned,
  1430. NULL,
  1431. NULL );
  1432. WSAIoctl( sTempSocket,
  1433. SIO_GET_EXTENSION_FUNCTION_POINTER,
  1434. (LPVOID) &guidGetAcceptExSockAddrs,
  1435. sizeof( guidGetAcceptExSockAddrs ),
  1436. &g_pfnGetAcceptExSockaddrs,
  1437. sizeof( g_pfnGetAcceptExSockaddrs ),
  1438. &cbReturned,
  1439. NULL,
  1440. NULL );
  1441. //
  1442. // Close temporary socket
  1443. //
  1444. closesocket( sTempSocket );
  1445. if ( !g_pfnAcceptEx ||
  1446. !g_pfnGetAcceptExSockaddrs ||
  1447. !g_pfnTransmitFile ) {
  1448. //
  1449. // This is bad.
  1450. //
  1451. DBG_ASSERT(FALSE);
  1452. ATQ_PRINTF(( DBG_CONTEXT,
  1453. "Failed to get entry points AE %x TF %x GAE %x\n",
  1454. g_pfnAcceptEx, g_pfnTransmitFile,
  1455. g_pfnGetAcceptExSockaddrs));
  1456. goto cleanup;
  1457. }
  1458. } else {
  1459. ATQ_PRINTF((DBG_CONTEXT,
  1460. "Error %d in LoadLibrary[mswsock.dll]\n",
  1461. GetLastError()));
  1462. goto cleanup;
  1463. }
  1464. return(TRUE);
  1465. cleanup:
  1466. if ( g_hMSWsock != NULL ) {
  1467. FreeLibrary( g_hMSWsock );
  1468. g_hMSWsock = NULL;
  1469. }
  1470. return(FALSE);
  1471. } // I_AtqInitializeEntryPoints