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.

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