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

819 lines
16 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 fUnused
  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 = _BusySignal;
  113. InterlockedIncrement( (PLONG)&_CurrentIdleTick );
  114. _BusySignal = 0;
  115. if ( !BusySignal && _CurrentIdleTick >= _IdleTime )
  116. {
  117. DBGPRINTF(( DBG_CONTEXT,
  118. "Idle time reached. Send shutdown message to WAS.\n" ));
  119. g_pwpContext->SendMsgToAdminProcess( IPM_WP_IDLE_TIME_REACHED );
  120. }
  121. }
  122. VOID
  123. WP_IDLE_TIMER::StopTimer(
  124. VOID
  125. )
  126. /*++
  127. Routine Description:
  128. Remove timer
  129. Arguments:
  130. None
  131. Return Value:
  132. None
  133. --*/
  134. {
  135. BOOL fRet;
  136. DBG_ASSERT( _hIdleTimeExpiredTimer );
  137. fRet = DeleteTimerQueueTimer( NULL,
  138. _hIdleTimeExpiredTimer,
  139. (HANDLE)-1 );
  140. if ( !fRet )
  141. {
  142. DBGPRINTF(( DBG_CONTEXT,
  143. "Failed to delete Timer queue. Win32 = %ld\n",
  144. GetLastError() ));
  145. }
  146. _hIdleTimeExpiredTimer = NULL;
  147. }
  148. VOID
  149. OverlappedCompletionRoutine(
  150. DWORD dwErrorCode,
  151. DWORD dwNumberOfBytesTransfered,
  152. LPOVERLAPPED lpOverlapped
  153. )
  154. /*++
  155. Routine Description:
  156. Main completion routine called on completions for UL app pool handle.
  157. Arguments:
  158. dwErrorCode - Win32 Error code of completion
  159. dwNumberOfBytesTransfered - Bytes completed
  160. lpOverlapped - Overlapped structure passed on async operation
  161. Return Value:
  162. None
  163. --*/
  164. {
  165. ASYNC_CONTEXT * pContext = NULL;
  166. //
  167. // Use the overlapped to get at the async context
  168. //
  169. if (lpOverlapped != NULL)
  170. {
  171. pContext = CONTAINING_RECORD( lpOverlapped,
  172. ASYNC_CONTEXT,
  173. _Overlapped );
  174. }
  175. DBG_ASSERT( pContext != NULL );
  176. //
  177. // Call virtual DoWork() to actually handle the completion
  178. // (context can represent a UL_NATIVE_REQUEST or a UL_DISCONNECT)
  179. //
  180. pContext->DoWork( dwNumberOfBytesTransfered,
  181. dwErrorCode,
  182. lpOverlapped );
  183. }
  184. WP_CONTEXT::WP_CONTEXT(
  185. VOID
  186. ) : _hDoneEvent( NULL ),
  187. _pConfigInfo( NULL ),
  188. _fShutdown( FALSE ),
  189. _pIdleTimer( NULL )
  190. {
  191. }
  192. WP_CONTEXT::~WP_CONTEXT(
  193. VOID
  194. )
  195. {
  196. }
  197. HRESULT
  198. WP_CONTEXT::Initialize(
  199. INT argc,
  200. LPWSTR * argv
  201. )
  202. /*++
  203. Routine Description:
  204. Initialize global context
  205. Arguments:
  206. argc - Command argument count
  207. argv - Command arguments
  208. Return Value:
  209. HRESULT
  210. --*/
  211. {
  212. LPCWSTR pwszAppPoolName;
  213. HRESULT hr = NO_ERROR;
  214. BOOL fAppPoolInit = FALSE;
  215. BOOL fNativeRequestInit = FALSE;
  216. BOOL fDisconnectInit = FALSE;
  217. BOOL fIpmInit = FALSE;
  218. BOOL fWpRecyclerInit = FALSE;
  219. _pConfigInfo = new WP_CONFIG();
  220. if ( _pConfigInfo == NULL )
  221. {
  222. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  223. goto Finished;
  224. }
  225. //
  226. // Validate the parameters passed into executable
  227. //
  228. if ( !_pConfigInfo->ParseCommandLine( argc, argv ) )
  229. {
  230. DBGPRINTF(( DBG_CONTEXT,
  231. "Invalid command line arguments.\n" ));
  232. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  233. goto Finished;
  234. }
  235. pwszAppPoolName = _pConfigInfo->QueryAppPoolName();
  236. //
  237. // Initialize UL AppPool
  238. //
  239. hr = _ulAppPool.Initialize( pwszAppPoolName );
  240. if ( FAILED( hr ) )
  241. {
  242. DBGPRINTF(( DBG_CONTEXT,
  243. "Failed to initialize AppPool. hr = %x\n",
  244. hr ));
  245. goto Finished;
  246. }
  247. fAppPoolInit = TRUE;
  248. //
  249. // Initialize UL_NATIVE_REQUEST globals
  250. //
  251. hr = UL_NATIVE_REQUEST::Initialize();
  252. if ( FAILED( hr ) )
  253. {
  254. DBGPRINTF(( DBG_CONTEXT,
  255. "Failed to initialize UL_NATIVE_REQUEST globals. hr = %x\n",
  256. hr ));
  257. goto Finished;
  258. }
  259. fNativeRequestInit = TRUE;
  260. //
  261. // Initialize UL_DISCONNECTs
  262. //
  263. hr = UL_DISCONNECT_CONTEXT::Initialize();
  264. if ( FAILED( hr ) )
  265. {
  266. DBGPRINTF(( DBG_CONTEXT,
  267. "Failed to initialize UL_DISCONNECT_CONTEXT globals. hr = %x\n",
  268. hr ));
  269. goto Finished;
  270. }
  271. fDisconnectInit = TRUE;
  272. DBGPRINTF(( DBG_CONTEXT,
  273. "AppPool '%ws' initialized\n",
  274. pwszAppPoolName ));
  275. //
  276. // Initialize of the shutdown event
  277. //
  278. _hDoneEvent = IIS_CREATE_EVENT( "WP_CONTEXT::_hDoneEvent",
  279. &_hDoneEvent,
  280. TRUE,
  281. FALSE );
  282. if ( _hDoneEvent == NULL )
  283. {
  284. hr = HRESULT_FROM_WIN32( GetLastError() );
  285. DBGPRINTF(( DBG_CONTEXT,
  286. "Failed to create shutdown event. hr = %x\n",
  287. hr ));
  288. goto Finished;
  289. }
  290. //
  291. // If an idle time is set, then set idle timer
  292. //
  293. if ( _pConfigInfo->QueryIdleTime() != 0 )
  294. {
  295. _pIdleTimer = new WP_IDLE_TIMER( _pConfigInfo->QueryIdleTime() );
  296. if ( _pIdleTimer )
  297. {
  298. hr = _pIdleTimer->Initialize();
  299. }
  300. else
  301. {
  302. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  303. }
  304. if ( FAILED( hr ) )
  305. {
  306. goto Finished;
  307. }
  308. }
  309. //
  310. // Setup all async completions on data channel handle to go thru W3TP
  311. //
  312. if (!ThreadPoolBindIoCompletionCallback( _ulAppPool.QueryHandle(),
  313. OverlappedCompletionRoutine,
  314. 0 ))
  315. {
  316. hr = HRESULT_FROM_WIN32(GetLastError());
  317. DBGPRINTF(( DBG_CONTEXT,
  318. "Failed to associate handle with thread pool. hr = %x\n",
  319. hr ));
  320. goto Finished;
  321. }
  322. //
  323. // Need to init this first as we may start getting callbacks as soon
  324. // as we init IPM
  325. //
  326. hr = WP_RECYCLER::Initialize();
  327. if ( FAILED( hr ) )
  328. {
  329. DBGPRINTF(( DBG_CONTEXT,
  330. "Failed to initialize WP_RECYCLER. hr = %x\n",
  331. hr ));
  332. goto Finished;
  333. }
  334. fWpRecyclerInit = TRUE;
  335. //
  336. // Register with WAS
  337. //
  338. if ( _pConfigInfo->QueryRegisterWithWAS() )
  339. {
  340. hr = _WpIpm.Initialize( this );
  341. if ( FAILED( hr ) )
  342. {
  343. DBGPRINTF(( DBG_CONTEXT,
  344. "Failed to initialize IPM. hr = %x\n",
  345. hr ));
  346. goto Finished;
  347. }
  348. fIpmInit = TRUE;
  349. }
  350. //
  351. // Set the window title to something nice when we're running
  352. // under the debugger.
  353. //
  354. if ( IsDebuggerPresent() )
  355. {
  356. STRU strTitle;
  357. WCHAR buffer[sizeof("w3wp[1234567890] - ")];
  358. WCHAR buffer2[sizeof(" - wp1234567890 - mm/dd hh:mm:ss")];
  359. wsprintf( buffer, L"w3wp[%lu] - ", GetCurrentProcessId() );
  360. hr = strTitle.Append( buffer );
  361. if (SUCCEEDED(hr))
  362. {
  363. hr = strTitle.Append( _pConfigInfo->QueryAppPoolName() );
  364. }
  365. if (SUCCEEDED(hr))
  366. {
  367. LARGE_INTEGER sysTime;
  368. LARGE_INTEGER localTime;
  369. TIME_FIELDS fields;
  370. NtQuerySystemTime( &sysTime );
  371. RtlSystemTimeToLocalTime( &sysTime, &localTime );
  372. RtlTimeToTimeFields( &localTime, &fields );
  373. wsprintf(
  374. buffer2,
  375. L" - wp%lu - %02u/%02u %02u:%02u:%02u",
  376. _pConfigInfo->QueryNamedPipeId(),
  377. fields.Month,
  378. fields.Day,
  379. fields.Hour,
  380. fields.Minute,
  381. fields.Second
  382. );
  383. hr = strTitle.Append( buffer2 );
  384. }
  385. if (SUCCEEDED(hr))
  386. {
  387. SetConsoleTitleW( strTitle.QueryStr() );
  388. }
  389. }
  390. return NO_ERROR;
  391. Finished:
  392. //
  393. // Terminate recycler object
  394. // Dependency warning: _WpIpm must still be valid
  395. //
  396. if ( fWpRecyclerInit )
  397. {
  398. WP_RECYCLER::Terminate();
  399. }
  400. if ( fIpmInit )
  401. {
  402. _WpIpm.Terminate();
  403. }
  404. if ( _pIdleTimer != NULL )
  405. {
  406. delete _pIdleTimer;
  407. _pIdleTimer = NULL;
  408. }
  409. if ( _hDoneEvent != NULL )
  410. {
  411. CloseHandle( _hDoneEvent );
  412. _hDoneEvent = NULL;
  413. }
  414. if ( fDisconnectInit )
  415. {
  416. UL_DISCONNECT_CONTEXT::Terminate();
  417. }
  418. if ( fNativeRequestInit )
  419. {
  420. UL_NATIVE_REQUEST::Terminate();
  421. }
  422. if ( fAppPoolInit )
  423. {
  424. _ulAppPool.Cleanup();
  425. }
  426. if ( _pConfigInfo != NULL )
  427. {
  428. delete _pConfigInfo;
  429. _pConfigInfo = NULL;
  430. }
  431. return hr;
  432. }
  433. HRESULT
  434. WP_CONTEXT::Start(
  435. VOID
  436. )
  437. /*++
  438. Routine Description:
  439. Start listening for requests by creating UL_NATIVE_REQUESTs
  440. Arguments:
  441. None
  442. Return Value:
  443. HRESULT
  444. --*/
  445. {
  446. HRESULT hr = NO_ERROR;
  447. //
  448. // Create a pool of worker requests
  449. // NYI: Allow the worker requests limit to be configurable.
  450. //
  451. UL_NATIVE_REQUEST::SetRestartCount( _pConfigInfo->QueryRestartCount() );
  452. hr = UL_NATIVE_REQUEST::AddPendingRequests(
  453. (_pConfigInfo->QueryRestartCount() == 0 ||
  454. (_pConfigInfo->QueryRestartCount() >=
  455. NUM_INITIAL_REQUEST_POOL_ITEMS)) ?
  456. NUM_INITIAL_REQUEST_POOL_ITEMS :
  457. _pConfigInfo->QueryRestartCount()
  458. );
  459. if ( FAILED( hr ) )
  460. {
  461. DBGPRINTF(( DBG_CONTEXT,
  462. "Failed to add pending UL_NATIVE_REQUESTs. hr = %x\n",
  463. hr ));
  464. }
  465. return hr;
  466. }
  467. BOOL
  468. WP_CONTEXT::IndicateShutdown(
  469. BOOL fImmediate
  470. )
  471. /*++
  472. Routine Description:
  473. Set shutdown event which allows StartListen to wake up and
  474. begin cleanup
  475. Arguments:
  476. reason - Reason for shutdown
  477. Return Value:
  478. BOOL
  479. --*/
  480. {
  481. _fImmediateShutdown = fImmediate;
  482. if ( !InterlockedCompareExchange((LONG *)&_fShutdown, TRUE, FALSE ) )
  483. {
  484. return SetEvent( _hDoneEvent );
  485. }
  486. else
  487. {
  488. return TRUE;
  489. }
  490. }
  491. VOID
  492. WP_CONTEXT::Terminate(
  493. VOID
  494. )
  495. /*++
  496. Routine Description:
  497. Cleanup WP_CONTEXT data structures
  498. Arguments:
  499. None
  500. Return Value:
  501. None
  502. --*/
  503. {
  504. HRESULT hr = NO_ERROR;
  505. //
  506. // Cleanup async contexts
  507. //
  508. UL_DISCONNECT_CONTEXT::Terminate();
  509. UL_NATIVE_REQUEST::Terminate();
  510. if ( _pIdleTimer != NULL )
  511. {
  512. delete _pIdleTimer;
  513. _pIdleTimer = NULL;
  514. }
  515. //
  516. // Cleanup the Shutdown Event.
  517. //
  518. DBG_ASSERT( _hDoneEvent != NULL );
  519. CloseHandle( _hDoneEvent );
  520. _hDoneEvent = NULL;
  521. //
  522. // Terminate procerr recycler.
  523. // Dependency warning: _WpIpm must still be valid
  524. //
  525. WP_RECYCLER::Terminate();
  526. //
  527. // Stop IPM
  528. //
  529. if ( _pConfigInfo->QueryRegisterWithWAS() )
  530. {
  531. hr = _WpIpm.Terminate();
  532. if ( FAILED( hr ) )
  533. {
  534. DBGPRINTF(( DBG_CONTEXT,
  535. "Failed to shutdown IPM. hr = %x\n",
  536. hr ));
  537. }
  538. }
  539. //
  540. // Cleanup config object
  541. //
  542. delete _pConfigInfo;
  543. _pConfigInfo = NULL;
  544. }
  545. HRESULT
  546. WP_CONTEXT::CleanupOutstandingRequests(
  547. VOID
  548. )
  549. /*++
  550. Routine Description:
  551. Cleanup WP_CONTEXT data structures
  552. Arguments:
  553. None
  554. Return Value:
  555. HRESULT
  556. --*/
  557. {
  558. HRESULT hr = NO_ERROR;
  559. //
  560. // If we want to shut-down immediately, then close the AppPool handle now
  561. //
  562. if (_fImmediateShutdown)
  563. {
  564. _ulAppPool.Cleanup();
  565. }
  566. //
  567. // Wait for requests to drain away. If they were pending
  568. // UlReceiveHttpRequest, they will complete with error. If they were
  569. // already processing, then we wait for them to finish
  570. //
  571. hr = UL_NATIVE_REQUEST::ReleaseAllWorkerRequests();
  572. if ( FAILED( hr ) )
  573. {
  574. DBGPRINTF(( DBG_CONTEXT,
  575. "Error draining UL_NATIVE_REQUESTs. hr = %x\n",
  576. hr ));
  577. return hr;
  578. }
  579. //
  580. // If we want to shut-down gracefully, then close the AppPool handle now
  581. //
  582. if (!_fImmediateShutdown)
  583. {
  584. _ulAppPool.Cleanup();
  585. }
  586. //
  587. // Wait for outstanding disconnect requests (i.e. UlWaitForDisconnect)
  588. // to drain
  589. //
  590. UL_DISCONNECT_CONTEXT::WaitForOutstandingDisconnects();
  591. //
  592. // Send WAS final counter data before shutting down
  593. //
  594. hr = _WpIpm.HandleCounterRequest();
  595. return hr;
  596. }
  597. VOID
  598. WP_CONTEXT::RunMainThreadLoop(
  599. VOID
  600. )
  601. /*++
  602. Routine Description:
  603. Wait for the shutdown event
  604. Arguments:
  605. None
  606. Return Value:
  607. None
  608. --*/
  609. {
  610. do
  611. {
  612. DWORD result;
  613. result = WaitForSingleObject( _hDoneEvent, INFINITE );
  614. DBG_ASSERT( result == WAIT_OBJECT_0 );
  615. } while ( !_fShutdown );
  616. }