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.

935 lines
18 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. wpcontext.cxx
  5. Abstract:
  6. This module defines the member functions of the WP_CONTEXT.
  7. The WP_CONTEXT object embodies an instance of the Worker process
  8. object. It contains a completion port, pool of worker threads,
  9. pool of worker requests, a data channel for the worker process, etc.
  10. It is responsible for setting up the context for processing requests
  11. and handles delegating the processing of requests.
  12. NYI: In the future we should be able to run WP_CONTEXT object as
  13. a COM+ object and be run standalone using a hosting exe.
  14. Author:
  15. Murali R. Krishnan ( MuraliK ) 17-Nov-1998
  16. Project:
  17. IIS Worker Process
  18. --*/
  19. #include "precomp.hxx"
  20. VOID
  21. WINAPI
  22. IdleTimeCheckCallback(
  23. VOID * pvContext,
  24. BOOLEAN
  25. )
  26. /*++
  27. Routine Description:
  28. Callback function provided for TimerQueue. Called every minute
  29. Arguments:
  30. pvContext - Context
  31. Return Value:
  32. None
  33. --*/
  34. {
  35. WP_IDLE_TIMER * pTimer = (WP_IDLE_TIMER *)pvContext;
  36. DBGPRINTF(( DBG_CONTEXT,
  37. "Check Idle Time Callback.\n" ));
  38. DBG_ASSERT( pTimer );
  39. pTimer->IncrementTick();
  40. }
  41. WP_IDLE_TIMER::WP_IDLE_TIMER(
  42. ULONG IdleTime
  43. )
  44. : _BusySignal(0),
  45. _CurrentIdleTick(0),
  46. _IdleTime(IdleTime),
  47. _hIdleTimeExpiredTimer((HANDLE)NULL)
  48. {
  49. }
  50. WP_IDLE_TIMER::~WP_IDLE_TIMER(
  51. VOID
  52. )
  53. {
  54. //
  55. // Cancel IdleTimeExpiredTimer
  56. //
  57. if (_hIdleTimeExpiredTimer)
  58. {
  59. StopTimer();
  60. }
  61. }
  62. HRESULT
  63. WP_IDLE_TIMER::Initialize(
  64. VOID
  65. )
  66. /*++
  67. Routine Description:
  68. Initialize the idle timer. Setup NT thread pool to callback every minute
  69. Arguments:
  70. None
  71. Return Value:
  72. HRESULT
  73. --*/
  74. {
  75. BOOL fRet;
  76. HRESULT hr = NO_ERROR;
  77. //
  78. // IdleTime is stored as in minutes, 1 min = 60*1000 milliseconds.
  79. //
  80. fRet = CreateTimerQueueTimer(
  81. &_hIdleTimeExpiredTimer, // handle to the Timer
  82. NULL, // Default Timer Queue
  83. IdleTimeCheckCallback, // Callback function
  84. this, // Context.
  85. 60000, // Due Time
  86. 60000, // Signal every minute
  87. WT_EXECUTEINIOTHREAD
  88. );
  89. if ( !fRet )
  90. {
  91. hr = HRESULT_FROM_WIN32( GetLastError() );
  92. DBGPRINTF(( DBG_CONTEXT,
  93. "Failed to create idle timer. hr = %x\n",
  94. hr ));
  95. }
  96. return hr;
  97. }
  98. VOID
  99. WP_IDLE_TIMER::IncrementTick(
  100. VOID
  101. )
  102. /*++
  103. Routine Description:
  104. Check every minute whether we've been idle long enough. If so,
  105. tell WAS
  106. Arguments:
  107. None
  108. Return Value:
  109. None
  110. --*/
  111. {
  112. ULONG BusySignal = InterlockedExchange( (PLONG)&_BusySignal, 0 );
  113. if ( !BusySignal && !UL_NATIVE_REQUEST::QueryCurrentRequests() )
  114. {
  115. InterlockedIncrement( (PLONG)&_CurrentIdleTick );
  116. if ( _CurrentIdleTick >= _IdleTime )
  117. {
  118. DBGPRINTF(( DBG_CONTEXT,
  119. "Idle time reached. Send shutdown message to WAS.\n" ));
  120. g_pwpContext->SendMsgToAdminProcess( IPM_WP_IDLE_TIME_REACHED );
  121. }
  122. }
  123. else
  124. {
  125. _CurrentIdleTick = 0;
  126. }
  127. }
  128. VOID
  129. WP_IDLE_TIMER::StopTimer(
  130. VOID
  131. )
  132. /*++
  133. Routine Description:
  134. Remove timer
  135. Arguments:
  136. None
  137. Return Value:
  138. None
  139. --*/
  140. {
  141. BOOL fRet;
  142. DBG_ASSERT( _hIdleTimeExpiredTimer );
  143. fRet = DeleteTimerQueueTimer( NULL,
  144. _hIdleTimeExpiredTimer,
  145. (HANDLE)-1 );
  146. if ( !fRet )
  147. {
  148. DBGPRINTF(( DBG_CONTEXT,
  149. "Failed to delete Timer queue. Win32 = %ld\n",
  150. GetLastError() ));
  151. }
  152. _hIdleTimeExpiredTimer = NULL;
  153. }
  154. VOID
  155. OverlappedCompletionRoutine(
  156. DWORD dwErrorCode,
  157. DWORD dwNumberOfBytesTransfered,
  158. LPOVERLAPPED lpOverlapped
  159. )
  160. /*++
  161. Routine Description:
  162. Main completion routine called on completions for UL app pool handle.
  163. Arguments:
  164. dwErrorCode - Win32 Error code of completion
  165. dwNumberOfBytesTransfered - Bytes completed
  166. lpOverlapped - Overlapped structure passed on async operation
  167. Return Value:
  168. None
  169. --*/
  170. {
  171. ASYNC_CONTEXT * pContext = NULL;
  172. //
  173. // Use the overlapped to get at the async context
  174. //
  175. if ( lpOverlapped != NULL )
  176. {
  177. pContext = CONTAINING_RECORD( lpOverlapped,
  178. ASYNC_CONTEXT,
  179. _Overlapped );
  180. DBG_ASSERT( pContext != NULL );
  181. //
  182. // Call virtual DoWork() to actually handle the completion
  183. // (context can represent a UL_NATIVE_REQUEST or a UL_DISCONNECT)
  184. //
  185. pContext->DoWork( dwNumberOfBytesTransfered,
  186. dwErrorCode,
  187. lpOverlapped );
  188. }
  189. else
  190. {
  191. DBG_ASSERT( lpOverlapped != NULL );
  192. }
  193. }
  194. WP_CONTEXT::WP_CONTEXT(
  195. VOID
  196. ) : _hDoneEvent( NULL ),
  197. _pConfigInfo( NULL ),
  198. _fShutdown( FALSE ),
  199. _pIdleTimer( NULL ),
  200. _fHealthy( TRUE )
  201. {
  202. }
  203. WP_CONTEXT::~WP_CONTEXT(
  204. VOID
  205. )
  206. {
  207. }
  208. HRESULT
  209. WP_CONTEXT::Initialize(
  210. INT argc,
  211. LPWSTR * argv
  212. )
  213. /*++
  214. Routine Description:
  215. Initialize global context
  216. Arguments:
  217. argc - Command argument count
  218. argv - Command arguments
  219. Return Value:
  220. HRESULT
  221. --*/
  222. {
  223. LPCWSTR pwszAppPoolName;
  224. HRESULT hr = NO_ERROR;
  225. DWORD dwErr = NO_ERROR;
  226. BOOL fAppPoolInit = FALSE;
  227. BOOL fNativeRequestInit = FALSE;
  228. BOOL fDisconnectInit = FALSE;
  229. BOOL fIpmInit = FALSE;
  230. BOOL fWpRecyclerInit = FALSE;
  231. _pConfigInfo = new WP_CONFIG();
  232. if ( _pConfigInfo == NULL )
  233. {
  234. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  235. goto Finished;
  236. }
  237. //
  238. // Validate the parameters passed into executable
  239. //
  240. if ( !_pConfigInfo->ParseCommandLine( argc, argv ) )
  241. {
  242. DBGPRINTF(( DBG_CONTEXT,
  243. "Invalid command line arguments.\n" ));
  244. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  245. goto Finished;
  246. }
  247. //
  248. // If we need to establish the control channel to run
  249. // without w3svc interaction do it now.
  250. //
  251. if ( _pConfigInfo->QuerySetupControlChannel() )
  252. {
  253. dwErr = _pConfigInfo->SetupControlChannel();
  254. if ( dwErr != ERROR_SUCCESS )
  255. {
  256. hr = HRESULT_FROM_WIN32( dwErr );
  257. DBGPRINTF(( DBG_CONTEXT,
  258. "SetupControlChannel failed with 0x%8x\n",
  259. hr ));
  260. goto Finished;
  261. }
  262. }
  263. pwszAppPoolName = _pConfigInfo->QueryAppPoolName();
  264. //
  265. // Initialize UL AppPool
  266. //
  267. hr = _ulAppPool.Initialize( pwszAppPoolName );
  268. if ( FAILED( hr ) )
  269. {
  270. DBGPRINTF(( DBG_CONTEXT,
  271. "Failed to initialize AppPool. hr = %x\n",
  272. hr ));
  273. goto Finished;
  274. }
  275. SetEnvironmentVariableW( L"APP_POOL_ID",
  276. pwszAppPoolName );
  277. fAppPoolInit = TRUE;
  278. //
  279. // Initialize UL_NATIVE_REQUEST globals
  280. //
  281. hr = UL_NATIVE_REQUEST::Initialize();
  282. if ( FAILED( hr ) )
  283. {
  284. DBGPRINTF(( DBG_CONTEXT,
  285. "Failed to initialize UL_NATIVE_REQUEST globals. hr = %x\n",
  286. hr ));
  287. goto Finished;
  288. }
  289. fNativeRequestInit = TRUE;
  290. //
  291. // Initialize UL_DISCONNECTs
  292. //
  293. hr = UL_DISCONNECT_CONTEXT::Initialize();
  294. if ( FAILED( hr ) )
  295. {
  296. DBGPRINTF(( DBG_CONTEXT,
  297. "Failed to initialize UL_DISCONNECT_CONTEXT globals. hr = %x\n",
  298. hr ));
  299. goto Finished;
  300. }
  301. fDisconnectInit = TRUE;
  302. DBGPRINTF(( DBG_CONTEXT,
  303. "AppPool '%ws' initialized\n",
  304. pwszAppPoolName ));
  305. //
  306. // Initialize of the shutdown event
  307. //
  308. _hDoneEvent = IIS_CREATE_EVENT( "WP_CONTEXT::_hDoneEvent",
  309. &_hDoneEvent,
  310. TRUE,
  311. FALSE );
  312. if ( _hDoneEvent == NULL )
  313. {
  314. hr = HRESULT_FROM_WIN32( GetLastError() );
  315. DBGPRINTF(( DBG_CONTEXT,
  316. "Failed to create shutdown event. hr = %x\n",
  317. hr ));
  318. goto Finished;
  319. }
  320. //
  321. // Setup all async completions on data channel handle to go thru W3TP
  322. //
  323. if (!ThreadPoolBindIoCompletionCallback( _ulAppPool.QueryAndLockHandle(),
  324. OverlappedCompletionRoutine,
  325. 0 ))
  326. {
  327. hr = HRESULT_FROM_WIN32(GetLastError());
  328. DBGPRINTF(( DBG_CONTEXT,
  329. "Failed to associate handle with thread pool. hr = %x\n",
  330. hr ));
  331. _ulAppPool.UnlockHandle();
  332. goto Finished;
  333. }
  334. hr = _ulAppPool.UnlockHandle();
  335. if ( FAILED( hr ) )
  336. {
  337. goto Finished;
  338. }
  339. //
  340. // Need to init this first as we may start getting callbacks as soon
  341. // as we init IPM
  342. //
  343. hr = WP_RECYCLER::Initialize();
  344. if ( FAILED( hr ) )
  345. {
  346. DBGPRINTF(( DBG_CONTEXT,
  347. "Failed to initialize WP_RECYCLER. hr = %x\n",
  348. hr ));
  349. goto Finished;
  350. }
  351. fWpRecyclerInit = TRUE;
  352. //
  353. // Register with WAS
  354. //
  355. if ( _pConfigInfo->QueryRegisterWithWAS() )
  356. {
  357. hr = _WpIpm.Initialize( this );
  358. if ( FAILED( hr ) )
  359. {
  360. DBGPRINTF(( DBG_CONTEXT,
  361. "Failed to initialize IPM. hr = %x\n",
  362. hr ));
  363. goto Finished;
  364. }
  365. fIpmInit = TRUE;
  366. }
  367. //
  368. // Set the window title to something nice when we're running
  369. // under the debugger.
  370. //
  371. if ( IsDebuggerPresent() )
  372. {
  373. STRU strTitle;
  374. WCHAR buffer[sizeof("w3wp[1234567890] - ")];
  375. WCHAR buffer2[sizeof(" - mm/dd hh:mm:ss")];
  376. wsprintf( buffer, L"w3wp[%lu] - ", GetCurrentProcessId() );
  377. hr = strTitle.Append( buffer );
  378. if (SUCCEEDED(hr))
  379. {
  380. hr = strTitle.Append( _pConfigInfo->QueryAppPoolName() );
  381. }
  382. if (SUCCEEDED(hr))
  383. {
  384. LARGE_INTEGER sysTime;
  385. LARGE_INTEGER localTime;
  386. TIME_FIELDS fields;
  387. NtQuerySystemTime( &sysTime );
  388. RtlSystemTimeToLocalTime( &sysTime, &localTime );
  389. RtlTimeToTimeFields( &localTime, &fields );
  390. wsprintf(
  391. buffer2,
  392. L" - %02u/%02u %02u:%02u:%02u",
  393. fields.Month,
  394. fields.Day,
  395. fields.Hour,
  396. fields.Minute,
  397. fields.Second
  398. );
  399. hr = strTitle.Append( buffer2 );
  400. }
  401. if (SUCCEEDED(hr))
  402. {
  403. SetConsoleTitleW( strTitle.QueryStr() );
  404. }
  405. }
  406. return S_OK;
  407. Finished:
  408. //
  409. // Terminate recycler object
  410. // Dependency warning: _WpIpm must still be valid
  411. //
  412. if ( fWpRecyclerInit )
  413. {
  414. WP_RECYCLER::Terminate();
  415. }
  416. if ( fIpmInit )
  417. {
  418. _WpIpm.Terminate();
  419. }
  420. if ( _hDoneEvent != NULL )
  421. {
  422. CloseHandle( _hDoneEvent );
  423. _hDoneEvent = NULL;
  424. }
  425. if ( fDisconnectInit )
  426. {
  427. UL_DISCONNECT_CONTEXT::Terminate();
  428. }
  429. if ( fNativeRequestInit )
  430. {
  431. UL_NATIVE_REQUEST::Terminate();
  432. }
  433. if ( fAppPoolInit )
  434. {
  435. _ulAppPool.Cleanup();
  436. }
  437. if ( _pConfigInfo != NULL )
  438. {
  439. delete _pConfigInfo;
  440. _pConfigInfo = NULL;
  441. }
  442. return hr;
  443. }
  444. HRESULT
  445. WP_CONTEXT::Start(
  446. VOID
  447. )
  448. /*++
  449. Routine Description:
  450. Start listening for requests by creating UL_NATIVE_REQUESTs
  451. Arguments:
  452. None
  453. Return Value:
  454. HRESULT
  455. --*/
  456. {
  457. HRESULT hr = NO_ERROR;
  458. //
  459. // Create a pool of worker requests
  460. // NYI: Allow the worker requests limit to be configurable.
  461. //
  462. UL_NATIVE_REQUEST::SetRestartCount( _pConfigInfo->QueryRestartCount() );
  463. hr = UL_NATIVE_REQUEST::AddPendingRequests(
  464. (_pConfigInfo->QueryRestartCount() == 0 ||
  465. (_pConfigInfo->QueryRestartCount() >=
  466. NUM_INITIAL_REQUEST_POOL_ITEMS)) ?
  467. NUM_INITIAL_REQUEST_POOL_ITEMS :
  468. _pConfigInfo->QueryRestartCount()
  469. );
  470. if ( FAILED( hr ) )
  471. {
  472. DBGPRINTF(( DBG_CONTEXT,
  473. "Failed to add pending UL_NATIVE_REQUESTs. hr = %x\n",
  474. hr ));
  475. goto Finished;
  476. }
  477. //
  478. // There is a possibility that worker process runs out of pending requests
  479. // in the case when http.sys starts failing HttpReceiveHttpRequest()
  480. // We will avoid the problem by regularly checking the pending count
  481. // adding new requests whenever necessary
  482. //
  483. hr = UL_NATIVE_REQUEST::StartPendingRequestsMonitor();
  484. if ( FAILED( hr ) )
  485. {
  486. DBGPRINTF(( DBG_CONTEXT,
  487. "Failed to start Pending requests monitor. hr = %x\n",
  488. hr ));
  489. goto Finished;
  490. }
  491. //
  492. // If an idle time is set, then set idle timer
  493. //
  494. if ( _pConfigInfo->QueryIdleTime() != 0 )
  495. {
  496. _pIdleTimer = new WP_IDLE_TIMER( _pConfigInfo->QueryIdleTime() );
  497. if ( _pIdleTimer )
  498. {
  499. hr = _pIdleTimer->Initialize();
  500. }
  501. else
  502. {
  503. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  504. }
  505. if ( FAILED( hr ) )
  506. {
  507. goto Finished;
  508. }
  509. }
  510. return S_OK;
  511. Finished:
  512. if ( _pIdleTimer != NULL )
  513. {
  514. delete _pIdleTimer;
  515. _pIdleTimer = NULL;
  516. }
  517. return hr;
  518. }
  519. BOOL
  520. WP_CONTEXT::IndicateShutdown(
  521. BOOL fImmediate
  522. )
  523. /*++
  524. Routine Description:
  525. Set shutdown event which allows StartListen to wake up and
  526. begin cleanup
  527. Arguments:
  528. reason - Reason for shutdown
  529. Return Value:
  530. BOOL
  531. --*/
  532. {
  533. if ( !InterlockedCompareExchange((LONG *)&_fShutdown, TRUE, FALSE ) )
  534. {
  535. _fImmediateShutdown = fImmediate;
  536. return SetEvent( _hDoneEvent );
  537. }
  538. else
  539. {
  540. return TRUE;
  541. }
  542. }
  543. VOID
  544. WP_CONTEXT::Terminate(
  545. VOID
  546. )
  547. /*++
  548. Routine Description:
  549. Cleanup WP_CONTEXT data structures
  550. Arguments:
  551. None
  552. Return Value:
  553. None
  554. --*/
  555. {
  556. HRESULT hr = NO_ERROR;
  557. //
  558. // Cleanup async contexts
  559. //
  560. UL_DISCONNECT_CONTEXT::Terminate();
  561. UL_NATIVE_REQUEST::Terminate();
  562. if ( _pIdleTimer != NULL )
  563. {
  564. delete _pIdleTimer;
  565. _pIdleTimer = NULL;
  566. }
  567. //
  568. // Stop IPM
  569. //
  570. if ( _pConfigInfo->QueryRegisterWithWAS() )
  571. {
  572. hr = _WpIpm.Terminate();
  573. if ( FAILED( hr ) )
  574. {
  575. DBGPRINTF(( DBG_CONTEXT,
  576. "Failed to shutdown IPM. hr = %x\n",
  577. hr ));
  578. }
  579. }
  580. //
  581. // Cleanup the Shutdown Event.
  582. //
  583. DBG_ASSERT( _hDoneEvent != NULL );
  584. CloseHandle( _hDoneEvent );
  585. _hDoneEvent = NULL;
  586. //
  587. // Terminate procerr recycler.
  588. // Dependency warning: _WpIpm must still be valid
  589. //
  590. WP_RECYCLER::Terminate();
  591. //
  592. // Cleanup config object
  593. //
  594. delete _pConfigInfo;
  595. _pConfigInfo = NULL;
  596. }
  597. HRESULT
  598. WP_CONTEXT::CleanupOutstandingRequests(
  599. VOID
  600. )
  601. /*++
  602. Routine Description:
  603. Cleanup WP_CONTEXT data structures
  604. Arguments:
  605. None
  606. Return Value:
  607. HRESULT
  608. --*/
  609. {
  610. HRESULT hr = NO_ERROR;
  611. //
  612. // Stop monitoring the number of pending requests since
  613. // it's time to shutdown
  614. //
  615. UL_NATIVE_REQUEST::StopPendingRequestsMonitor();
  616. //
  617. // If we want to shut-down immediately, then close the AppPool handle now
  618. //
  619. if (_fImmediateShutdown)
  620. {
  621. _ulAppPool.Cleanup();
  622. }
  623. //
  624. // Wait for requests to drain away. If they were pending
  625. // UlReceiveHttpRequest, they will complete with error. If they were
  626. // already processing, then we wait for them to finish
  627. //
  628. hr = UL_NATIVE_REQUEST::ReleaseAllWorkerRequests();
  629. if ( FAILED( hr ) )
  630. {
  631. DBGPRINTF(( DBG_CONTEXT,
  632. "Error draining UL_NATIVE_REQUESTs. hr = %x\n",
  633. hr ));
  634. return hr;
  635. }
  636. //
  637. // If we want to shut-down gracefully, then close the AppPool handle now
  638. //
  639. if (!_fImmediateShutdown)
  640. {
  641. _ulAppPool.Cleanup();
  642. }
  643. //
  644. // Wait for outstanding disconnect requests (i.e. UlWaitForDisconnect)
  645. // to drain
  646. //
  647. UL_DISCONNECT_CONTEXT::WaitForOutstandingDisconnects();
  648. //
  649. // Send WAS final counter data before shutting down
  650. //
  651. hr = _WpIpm.HandleCounterRequest();
  652. return hr;
  653. }
  654. VOID
  655. WP_CONTEXT::RunMainThreadLoop(
  656. VOID
  657. )
  658. /*++
  659. Routine Description:
  660. Wait for the shutdown event
  661. Arguments:
  662. None
  663. Return Value:
  664. None
  665. --*/
  666. {
  667. do
  668. {
  669. DWORD result;
  670. result = WaitForSingleObject( _hDoneEvent, INFINITE );
  671. DBG_ASSERT( result == WAIT_OBJECT_0 );
  672. } while ( !_fShutdown );
  673. }
  674. HANDLE
  675. WP_CONTEXT::GetAndLockAsyncHandle(
  676. VOID
  677. )
  678. /*++
  679. Routine Description:
  680. Read locks and returns the async handle.
  681. Arguments:
  682. None
  683. Return Value:
  684. HANDLE
  685. --*/
  686. {
  687. return _ulAppPool.QueryAndLockHandle();
  688. }
  689. HRESULT
  690. WP_CONTEXT::UnlockAsyncHandle(
  691. VOID
  692. )
  693. /*++
  694. Routine Description:
  695. Read unlocks the async handle.
  696. Arguments:
  697. None
  698. Return Value:
  699. HRESULT
  700. --*/
  701. {
  702. return _ulAppPool.UnlockHandle();
  703. }