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.

2668 lines
74 KiB

  1. /*-----------------------------------------------------------------------------
  2. Copyright (c) 1995-1997 Microsoft Corporation
  3. Module Name :
  4. wamexec.cxx
  5. Abstract:
  6. This module executes a wam request
  7. Author:
  8. David Kaplan ( DaveK ) 11-Mar-1997
  9. Lei Jin (leijin) 24-Apr-1997 (WamInfo & WamDictator)
  10. Environment:
  11. User Mode - Win32
  12. Project:
  13. W3 services DLL
  14. -----------------------------------------------------------------------------*/
  15. #include <w3p.hxx>
  16. #include "wamexec.hxx"
  17. #include "waminfo.hxx"
  18. #include "wamreq.hxx"
  19. // MIDL-generated include files
  20. #include "wam.h"
  21. #include "wam_i.c"
  22. #include <wmrgexp.h>
  23. #include <issched.hxx>
  24. #include "mtacb.h"
  25. #include "gip.h"
  26. #include <malloc.h>
  27. #include "wrcstids.hxx"
  28. #include "wrcfixed.hxx"
  29. #define SZ_FAILED_OOP_REQUEST_LOG_MESSAGE \
  30. "Out-of-process ISAPI extension request failed."
  31. #define SZ_FAILED_OOP_EXCEEDED_CRASH_LOG_MESSAGE \
  32. "Out-of-process ISAPI extension has exceeded crash limit."
  33. /*-----------------------------------------------------------------------------
  34. Globals
  35. -----------------------------------------------------------------------------*/
  36. extern PFN_INTERLOCKED_COMPARE_EXCHANGE g_pfnInterlockedCompareExchange;
  37. //
  38. // This number is in ms, 10 (min) * 60 (sec/min) * 1000 (ms/s).
  39. // DCOM/RPC level holds some reference(up to 2) to a WAM_REQUESTs when an OOP MTS process crashes,
  40. // In order to clean the Wam_Request completely, w3svc needs to wait for 10 min(enough for 6 min DCOM
  41. // refenrence timeout).
  42. //
  43. #define DEFAULT_CLEANUP_WAIT 600000
  44. /*-----------------------------------------------------------------------------
  45. WamInfo State Description
  46. State Valid(Can Serving requests)
  47. WIS_START F (sould not get into this state at runtime)
  48. WIS_RUNNING T
  49. WIS_REPAIR T (Yes, although requests is going to be waiting or rejected).
  50. WIS_PAUSE F (NYI)
  51. WIS_CPUPAUSE T
  52. WIS_ERROR T (Yes, the requests is going to be rejected).
  53. WIS_SHUTDOWN T
  54. WIS_END F (should not get into this state at runtime)
  55. WIS_MAX_STATE F (should not get into this state at runtime)
  56. Please refer to waminfo.hxx for detailed description of waminfo state.
  57. -----------------------------------------------------------------------------*/
  58. BOOL CWamInfo::m_rgValidState[WIS_MAX_STATE] =
  59. {
  60. FALSE,
  61. TRUE,
  62. TRUE,
  63. FALSE,
  64. FALSE,
  65. TRUE,
  66. FALSE,
  67. FALSE
  68. };
  69. #define WAMINFO_SIGNATURE (DWORD)'FIMW'
  70. #define FREED_WAMINFO_SIGNATURE (DWORD)'fIMW'
  71. /*-----------------------------------------------------------------------------
  72. CWamInfo::CWamInfo
  73. Constructor
  74. Arguments:
  75. pstrMetabasePath [in] a pointer to Metabase Path.
  76. fInProcess [in] in-proc/out-proc // remote is not a choice
  77. fEnableTryExcept [in] enable try/except flag
  78. clsidWam [in] the WAM CLSID
  79. -----------------------------------------------------------------------------*/
  80. CWamInfo::CWamInfo
  81. (
  82. const STR &strMetabasePath,
  83. BOOL fInProcess,
  84. BOOL fInPool,
  85. BOOL fEnableTryExcept,
  86. REFGUID clsidWam
  87. )
  88. : m_strApplicationPath(strMetabasePath),
  89. m_dwSignature(WAMINFO_SIGNATURE),
  90. m_pIWam(NULL),
  91. m_dwIWamGipCookie( NULL_GIP_COOKIE ),
  92. m_cRef(0),
  93. m_cCurrentRequests(0),
  94. m_cTotalRequests(0),
  95. m_dwState(WIS_START),
  96. m_fInProcess(fInProcess),
  97. m_fEnableTryExcept(fEnableTryExcept),
  98. m_fShuttingDown(FALSE),
  99. m_pProcessEntry(NULL),
  100. m_clsidWam(clsidWam),
  101. m_fInPool(fInPool),
  102. m_dwRecycleSchedulerCookie(0),
  103. m_fRecycled(0),
  104. m_cMaxRequests(0)
  105. {
  106. InitializeListHead(&leProcess);
  107. INITIALIZE_CRITICAL_SECTION(&m_csRecycleLock);
  108. }
  109. /*-----------------------------------------------------------------------------
  110. CWamInfo::~CWamInfo
  111. Destructor
  112. -----------------------------------------------------------------------------*/
  113. CWamInfo::~CWamInfo
  114. (
  115. void
  116. )
  117. {
  118. DeleteCriticalSection(&m_csRecycleLock);
  119. m_dwSignature = FREED_WAMINFO_SIGNATURE;
  120. DBG_ASSERT(m_cRef == 0);
  121. DBG_ASSERT(m_cCurrentRequests == 0);
  122. }
  123. HRESULT
  124. CWamInfo::PreProcessWamRequest
  125. (
  126. IN WAM_REQUEST* pWamRequest,
  127. IN HTTP_REQUEST* pHttpRequest,
  128. OUT IWam** ppIWam,
  129. OUT BOOL* pfHandled
  130. )
  131. {
  132. IWam *pIWam = NULL;
  133. DBG_ASSERT( m_dwIWamGipCookie != NULL_GIP_COOKIE );
  134. if( FAILED( g_GIPAPI.Get( m_dwIWamGipCookie, IID_IWam, (void **)&pIWam )))
  135. {
  136. //
  137. // bad news - GIP refused to provide a pointer
  138. // we'll let higher levels to deal with it and return NULL here
  139. //
  140. pIWam = NULL;
  141. }
  142. *ppIWam = pIWam;
  143. return NOERROR;
  144. }
  145. HRESULT
  146. CWamInfo::PreProcessAsyncIO
  147. (
  148. IN WAM_REQUEST * pWamRequest,
  149. OUT IWam ** ppIWam
  150. )
  151. {
  152. IWam *pIWam = NULL;
  153. DBG_ASSERT( m_dwIWamGipCookie != NULL_GIP_COOKIE );
  154. if( FAILED( g_GIPAPI.Get( m_dwIWamGipCookie, IID_IWam, (void **)&pIWam )))
  155. {
  156. //
  157. // bad news - GIP refused to provide a pointer
  158. // we'll let higher levels to deal with it and return NULL here
  159. //
  160. pIWam = NULL;
  161. }
  162. *ppIWam = pIWam;
  163. return NOERROR;
  164. }
  165. /*-----------------------------------------------------------------------------
  166. CWamInfo::ProcessWamRequest
  167. Process a Wam Request(of this application path).
  168. The request must ask for the same application path with this WamInfo. This function will create a
  169. wam request, process the request and finish the request.
  170. Arguments:
  171. pHttpRequest [in] pointer to HTTP Request
  172. pExec [in] pointer to Exec descriptor
  173. pstrPath, [in] physical path of the dll???
  174. pfHandled, [out]
  175. pfFinished [out]
  176. Return:
  177. HRESULT
  178. -----------------------------------------------------------------------------*/
  179. HRESULT
  180. CWamInfo::ProcessWamRequest
  181. (
  182. HTTP_REQUEST * pHttpRequest,
  183. EXEC_DESCRIPTOR * pExec,
  184. const STR * pstrPath,
  185. BOOL * pfHandled,
  186. BOOL * pfFinished
  187. )
  188. {
  189. HRESULT hr = NOERROR;
  190. WAM_REQUEST * pWamRequest = NULL;
  191. IWam * pIWam = NULL;
  192. if ( !FCurrentStateValid() || m_fShuttingDown)
  193. {
  194. if ( QueryState() == WIS_CPUPAUSE )
  195. {
  196. //
  197. // Since WAM_INFO is going to send some message to the browser, we need
  198. // to set pfHandled = TRUE here.
  199. //
  200. *pfHandled = TRUE;
  201. if ( !pExec->IsChild() )
  202. {
  203. pHttpRequest->SetLogStatus( HT_SVC_UNAVAILABLE,
  204. ERROR_NOT_ENOUGH_QUOTA );
  205. pHttpRequest->Disconnect(HT_SVC_UNAVAILABLE,
  206. IDS_SITE_RESOURCE_BLOCKED,
  207. TRUE,
  208. pfFinished);
  209. }
  210. Dereference();
  211. //
  212. // Since we already called Disconnect, WAM_INFO should return TRUE to high level.
  213. //
  214. return NOERROR;
  215. }
  216. IF_DEBUG( WAM_ISA_CALLS )
  217. {
  218. DBGPRINTF((
  219. DBG_CONTEXT
  220. , "CWamInfo(%08x) shutting down. "
  221. "Http-request(%08x) will be aborted.\n"
  222. , this
  223. , pHttpRequest
  224. ));
  225. }
  226. Dereference();
  227. return HRESULT(ERROR_SERVER_DISABLED);
  228. }
  229. // Create the wam req NOTE CreateWamRequestInstance addref's
  230. hr = CreateWamRequestInstance( pHttpRequest,
  231. pExec,
  232. pstrPath,
  233. this,
  234. &pWamRequest);
  235. if ( FAILED(hr))
  236. {
  237. DBGPRINTF(( DBG_CONTEXT,
  238. "WAMInfo::ProcessWamRequest: CreateWamRequestInstance failed, hr = %08x\n",
  239. hr
  240. ));
  241. // The Dereference balances the reference in FindWamInfo.
  242. Dereference();
  243. goto LExit;
  244. }
  245. DBG_ASSERT(pWamRequest != NULL);
  246. // Temporarily allow the number of pool threads to grow while we're processing the request
  247. AtqSetInfo( AtqIncMaxPoolThreads, 0 );
  248. InterlockedIncrement(&m_cTotalRequests);
  249. InterlockedIncrement(&m_cCurrentRequests);
  250. IF_DEBUG( WAM_ISA_CALLS )
  251. DBGPRINTF((
  252. DBG_CONTEXT,
  253. "CWamInfo 0x%08x: serviced %d requests of maximum %d.\r\n",
  254. this,
  255. m_cTotalRequests,
  256. m_cMaxRequests
  257. ));
  258. if ( m_cMaxRequests && m_cMaxRequests <= m_cTotalRequests )
  259. {
  260. Recycle( 0 );
  261. }
  262. hr = PreProcessWamRequest(pWamRequest, pHttpRequest, &pIWam, pfHandled);
  263. if (SUCCEEDED(hr))
  264. {
  265. DBG_ASSERT(pIWam != NULL);
  266. hr = DoProcessRequestCall( pIWam, pWamRequest, pfHandled );
  267. pIWam->Release();
  268. //
  269. // got rid of it in Avalanche
  270. //
  271. *pfFinished = FALSE;
  272. AtqSetInfo( AtqDecMaxPoolThreads, 0 );
  273. if( WIN32_FROM_HRESULT( hr ) == ERROR_ACCESS_DENIED )
  274. {
  275. DBGPRINTF(( DBG_CONTEXT,"Process wam request-access denied\n",hr));
  276. pWamRequest->SetDeniedFlags( SF_DENIED_RESOURCE );
  277. pWamRequest->SetExpectCleanup( TRUE );
  278. }
  279. else if( RPC_S_CALL_FAILED == WIN32_FROM_HRESULT(hr) ||
  280. RPC_S_CALL_FAILED_DNE == WIN32_FROM_HRESULT(hr) ||
  281. RPC_S_SERVER_UNAVAILABLE == WIN32_FROM_HRESULT(hr)
  282. )
  283. {
  284. // 1> If the OOP call failed during the call, RPC returns RPC_S_CALL_FAILED.
  285. // ie. OOP process crashed during the call
  286. // 2> If OOP process crashed before the call, RPC returns RPC_S_CALL_FAILED_DNE.
  287. //
  288. // Subsequest requests to the crashed OOP Process will get RPC_S_SERVER_UNAVAILABLE.
  289. //
  290. PostProcessRequest(hr, pWamRequest);
  291. }
  292. else
  293. {
  294. DBG_ASSERT( HRESULT_FACILITY(hr) != FACILITY_RPC );
  295. // Success
  296. pWamRequest->SetExpectCleanup( TRUE );
  297. }
  298. }
  299. else
  300. {
  301. if (TRUE == *pfHandled)
  302. {
  303. DBG_ASSERT(pIWam == NULL && hr == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED));
  304. hr = NOERROR;
  305. }
  306. AtqSetInfo( AtqDecMaxPoolThreads, 0 );
  307. }
  308. InterlockedDecrement(&m_cCurrentRequests);
  309. LExit:
  310. // NOTE this release balances with addref in CreateWamRequestInstance()
  311. if (pWamRequest)
  312. {
  313. pWamRequest->Release();
  314. pWamRequest = NULL;
  315. }
  316. return hr;
  317. }//CWamInfo::ProcessWamRequest
  318. HRESULT
  319. CWamInfo::DoProcessRequestCall
  320. (
  321. IN IWam * pIWam,
  322. IN WAM_REQUEST * pWamRequest,
  323. OUT BOOL * pfHandled
  324. )
  325. {
  326. return pIWam->ProcessRequest( pWamRequest,
  327. pWamRequest->CbWrcStrings( m_fInProcess ),
  328. NULL,
  329. pfHandled
  330. );
  331. }
  332. /*-----------------------------------------------------------------------------
  333. CWamInfo::ProcessAsyncIO
  334. ProcessAsyncIO.
  335. -----------------------------------------------------------------------------*/
  336. HRESULT CWamInfo::ProcessAsyncIO
  337. (
  338. WAM_REQUEST* pWamRequest,
  339. #ifdef _WIN64
  340. UINT64 pWamExecInfoIn,
  341. #else
  342. ULONG_PTR pWamExecInfoIn,
  343. #endif
  344. DWORD dwStatus,
  345. DWORD cbWritten,
  346. LPBYTE lpOopReadData // Default == NULL
  347. )
  348. {
  349. HRESULT hr = NOERROR;
  350. IWam *pIWam = NULL;
  351. hr = PreProcessAsyncIO(pWamRequest, &pIWam);
  352. if (SUCCEEDED(hr))
  353. {
  354. DBG_ASSERT(pIWam != NULL);
  355. if( lpOopReadData == NULL )
  356. {
  357. hr = pIWam->ProcessAsyncIO(
  358. pWamExecInfoIn,
  359. dwStatus,
  360. cbWritten
  361. );
  362. }
  363. else
  364. {
  365. DBG_ASSERT( !m_fInProcess );
  366. hr = pIWam->ProcessAsyncReadOop(
  367. pWamExecInfoIn,
  368. dwStatus,
  369. cbWritten,
  370. lpOopReadData
  371. );
  372. }
  373. pIWam->Release();
  374. if( WIN32_FROM_HRESULT( hr ) == ERROR_ACCESS_DENIED )
  375. {
  376. DBGPRINTF(( DBG_CONTEXT,"Process wam request-access denied\n",hr));
  377. pWamRequest->SetDeniedFlags( SF_DENIED_RESOURCE );
  378. }
  379. PostProcessRequest(hr, pWamRequest);
  380. }
  381. return hr;
  382. }//CWamInfo::ProcessAsyncIO
  383. /*-----------------------------------------------------------------------------
  384. CWamInfo::GetStatistics
  385. This code is used internally for iisprobe.dll only.
  386. -----------------------------------------------------------------------------*/
  387. HRESULT CWamInfo::GetStatistics
  388. (
  389. DWORD Level,
  390. LPWAM_STATISTICS_INFO pWamStatsInfo
  391. )
  392. {
  393. IWam *pIWam = NULL;
  394. HRESULT hr;
  395. //
  396. // both case - go through GIP
  397. //
  398. DBG_ASSERT(m_dwIWamGipCookie != NULL_GIP_COOKIE);
  399. hr = g_GIPAPI.Get( m_dwIWamGipCookie, IID_IWam, (void **) &pIWam );
  400. if( SUCCEEDED( hr ) )
  401. {
  402. hr = pIWam->GetStatistics(Level, pWamStatsInfo);
  403. pIWam->Release();
  404. }
  405. return hr;
  406. } // CWamInfo::GetStatistics
  407. //
  408. // aux class, used temporarily by WamInfo::CreateWamInstance()
  409. //
  410. struct WAM_CREATOR {
  411. REFCLSID m_clsidWam;
  412. DWORD m_dwContext;
  413. IWam * m_pIWam;
  414. DWORD m_dwIWamCookie;
  415. BOOL m_fOutOfProc;
  416. WAM_CREATOR( REFCLSID clsidWam, DWORD dwContext, BOOL fOutOfProc );
  417. };
  418. //
  419. // initialize WAM_CREATOR instance
  420. //
  421. WAM_CREATOR::WAM_CREATOR(
  422. REFCLSID clsidWam,
  423. DWORD dwContext,
  424. BOOL fOutOfProc )
  425. : m_clsidWam( clsidWam ),
  426. m_dwContext( dwContext ),
  427. m_pIWam( 0 ),
  428. m_dwIWamCookie( 0 ),
  429. m_fOutOfProc( fOutOfProc )
  430. {
  431. }
  432. //
  433. // this conforms to MTA callback format
  434. //
  435. HRESULT __stdcall WamCreatorCallback(void *v1, void *v2)
  436. {
  437. HRESULT hr;
  438. WAM_CREATOR * pwc = (WAM_CREATOR *) v1;
  439. hr = CoCreateInstance( pwc->m_clsidWam,
  440. NULL,
  441. pwc->m_dwContext,
  442. IID_IWam,
  443. (void **)&pwc->m_pIWam
  444. );
  445. DBGPRINTF( (DBG_CONTEXT, "CoCreated WAM: %x(%x)\n", pwc->m_pIWam, hr ) );
  446. if(SUCCEEDED(hr) && pwc->m_fOutOfProc) {
  447. //
  448. // BUG: 609457
  449. // Set the proxy blanket so that the client can't
  450. // impersonate us.
  451. //
  452. hr = CoSetProxyBlanket(
  453. pwc->m_pIWam,
  454. RPC_C_AUTHN_DEFAULT,
  455. RPC_C_AUTHZ_DEFAULT,
  456. COLE_DEFAULT_PRINCIPAL,
  457. RPC_C_AUTHN_LEVEL_DEFAULT,
  458. RPC_C_IMP_LEVEL_IDENTIFY,
  459. COLE_DEFAULT_AUTHINFO,
  460. EOAC_DEFAULT
  461. );
  462. if ( FAILED( hr ) )
  463. {
  464. DBGPRINTF((DBG_CONTEXT, "Failed to CoSetProxyBlanket WAM.\r\n"));
  465. }
  466. }
  467. if(SUCCEEDED(hr)) {
  468. //
  469. // OOP case needs GIPs
  470. //
  471. hr = g_GIPAPI.Register(pwc->m_pIWam, IID_IWam, &pwc->m_dwIWamCookie);
  472. if(FAILED(hr)) {
  473. DBGPRINTF( (DBG_CONTEXT, "Failed to register w GIP:%x (%x)\n", pwc->m_pIWam, hr ) );
  474. pwc->m_pIWam->Release();
  475. pwc->m_pIWam = NULL;
  476. }else{
  477. DBGPRINTF( (DBG_CONTEXT, "Registered w GIP OK\n" ) );
  478. }
  479. }
  480. return hr;
  481. }
  482. /*-----------------------------------------------------------------------------
  483. CWamInfo::CreateWamInstance
  484. Creates WAM instance. For complete detail, see BUG #137647.
  485. In short, the purpose of this is to make sure that CoCreateInstance()
  486. is called from MTA thread and not from one of ATQ threads, converted to
  487. STA by a random ISAPI extension.
  488. The real work is done in WamCreatorCallback() above.
  489. Returns:
  490. HRESULT of CoCreateInstance(), and, in OOP case,
  491. HRESULT of GIPAPI.Register
  492. -----------------------------------------------------------------------------*/
  493. HRESULT CWamInfo::CreateWamInstance( DWORD dwContext )
  494. {
  495. HRESULT hr = 0;
  496. WAM_CREATOR wc( m_clsidWam, CLSCTX_ALL, !m_fInProcess );
  497. //
  498. // do the actual work on MTA thread
  499. //
  500. hr = CallMTACallback( WamCreatorCallback, &wc, NULL );
  501. //
  502. // store resulting IWAM pointer and GIP cookie
  503. //
  504. m_pIWam = (IWam *) wc.m_pIWam;
  505. m_dwIWamGipCookie = wc.m_dwIWamCookie;
  506. return hr;
  507. }
  508. /*-----------------------------------------------------------------------------
  509. CWamInfo::Init
  510. Create the WAM object. One-to-One mapping between a WamInfo and a Wam.
  511. Arguments:
  512. None.
  513. Return:
  514. HRESULT
  515. -----------------------------------------------------------------------------*/
  516. HRESULT
  517. CWamInfo::Init
  518. (
  519. WCHAR* wszPackageId,
  520. DWORD pidInetInfo
  521. )
  522. {
  523. HRESULT hr = NOERROR;
  524. IWam * pIWam = NULL;
  525. // If WamInfo on start or
  526. // WamInfo is out of proc and it is in either repair or cuppause sate.
  527. // Init the WamInfo
  528. DBG_ASSERT(((m_dwState == WIS_REPAIR || m_dwState == WIS_CPUPAUSE) && !m_fInProcess)
  529. || m_dwState == WIS_START );
  530. DWORD dwContext = ((m_fInProcess) ?
  531. CLSCTX_INPROC_SERVER : CLSCTX_LOCAL_SERVER
  532. );
  533. IF_DEBUG( ERROR )
  534. {
  535. Print();
  536. }
  537. DBG_ASSERT( NULL == m_pIWam);
  538. DBGPRINTF( (DBG_CONTEXT, "Creating WAM Instance dwContext = %x\n", dwContext ) );
  539. hr = CreateWamInstance( dwContext );
  540. //DBGPRINTF( (DBG_CONTEXT, "Created WAM Instance %x (%x)\n", m_pIWam, hr ) );
  541. if (SUCCEEDED(hr))
  542. {
  543. DWORD pidWamProcess = 0;
  544. // Use GIP in inproc case as well.
  545. DBG_ASSERT( m_dwIWamGipCookie != NULL_GIP_COOKIE);
  546. hr = g_GIPAPI.Get( m_dwIWamGipCookie, IID_IWam, (void **) &pIWam );
  547. if( SUCCEEDED( hr ) )
  548. {
  549. hr = pIWam->InitWam(
  550. m_fInProcess,
  551. m_fInPool,
  552. m_fEnableTryExcept,
  553. W3PlatformType,
  554. &pidWamProcess
  555. );
  556. pIWam->Release();
  557. }
  558. if( SUCCEEDED(hr) && !m_fInProcess )
  559. {
  560. //
  561. // Under some circumstances com svcs my get hosed. This causes
  562. // object activation to happen in process even if the object
  563. // is registered to be launched in the surrogate. In order to
  564. // prevent inetinfo from AV'ing we want to prevent these
  565. // applications from running.
  566. //
  567. DBG_ASSERT( pidInetInfo != pidWamProcess );
  568. if( pidInetInfo == pidWamProcess )
  569. {
  570. // Log the failure.
  571. // W3_EVENT_FAIL_OOP_ACTIVATION - in W3SVC.dll
  572. const CHAR * pszEventLog[2];
  573. CHAR szAppId[40];
  574. *szAppId = 0;
  575. WideCharToMultiByte( CP_ACP, 0, wszPackageId, -1, szAppId, 40, NULL, NULL );
  576. pszEventLog[0] = szAppId;
  577. pszEventLog[1] = m_strApplicationPath.QueryStr();
  578. g_pInetSvc->LogEvent( W3_EVENT_FAIL_OOP_ACTIVATION,
  579. 2,
  580. pszEventLog,
  581. 0
  582. );
  583. // We need to return an error code to prevent further
  584. // processing. Since this will get looked up and event
  585. // logged, by the WAM_DICTATOR we want to return something
  586. // innocuous, but understandable.
  587. hr = W3_EVENT_FAIL_OOP_ACTIVATION;
  588. }
  589. else
  590. {
  591. //
  592. // Schedule recycling if configured to do so
  593. //
  594. if ( m_dwRecycleTime )
  595. {
  596. Recycle( m_dwRecycleTime * 60 * 1000 );
  597. }
  598. }
  599. }
  600. if (SUCCEEDED(hr))
  601. {
  602. m_pProcessEntry = g_pWamDictator->m_PTable.AddWamInfoToProcessTable(
  603. this,
  604. wszPackageId,
  605. pidWamProcess);
  606. if (m_pProcessEntry != NULL)
  607. {
  608. // Make WamInfo Alive. This reference is balanced
  609. // by CWamInfo::Uninit if the CWamInfo has an associated
  610. // process entry.
  611. Reference();
  612. DBGPRINTF(( DBG_CONTEXT, "New WamInfo(%08x))\n", this));
  613. }
  614. else
  615. {
  616. DWORD err = GetLastError();
  617. DBGPRINTF(( DBG_CONTEXT, "Dup Handle, error %08x\n", err ));
  618. hr = HRESULT_FROM_WIN32( err);
  619. }
  620. }
  621. else
  622. {
  623. DBGPRINTF(( DBG_CONTEXT,
  624. "WAM(%08x)::InitWam() failed, error %08x\n",
  625. m_dwIWamGipCookie, hr ));
  626. }
  627. }
  628. else
  629. {
  630. /*
  631. some possible failures:
  632. CO_E_NOTINITIALIZED 0x800401f0
  633. REGDB_E_CLASSNOTREG 0x80040154
  634. */
  635. DBG_ASSERT( hr != CO_E_NOTINITIALIZED );
  636. DBG_ASSERT( hr != REGDB_E_CLASSNOTREG );
  637. DBGPRINTF(( DBG_CONTEXT,
  638. "CoCreateInstance failed to create WAM, error %08x\n",
  639. hr ));
  640. DBG_ASSERT( m_pIWam == NULL);
  641. }
  642. // Rely on the correct call to UnInit to free things up. If uninit
  643. // isn't correctly called, then the WAM object is leaked anyway.
  644. /*
  645. if ( FAILED( hr) && (m_pIWam != NULL))
  646. {
  647. m_pIWam->Release(); // release the Wam
  648. m_pIWam = NULL;
  649. }
  650. */
  651. return hr;
  652. } // CWamInfo::Init()
  653. VOID
  654. CWamInfo::NotifyGetInfoForName
  655. (
  656. IN LPCSTR pszServerVariable
  657. )
  658. {
  659. // Only functional oop
  660. return;
  661. }
  662. /*-----------------------------------------------------------------------------
  663. CWamInfo::StartShutdown
  664. Phase 1 of shutdown process.
  665. Return:
  666. HRESULT
  667. -----------------------------------------------------------------------------*/
  668. HRESULT
  669. CWamInfo::StartShutdown
  670. (
  671. INT cIgnoreRefs
  672. )
  673. {
  674. IF_DEBUG( WAM_ISA_CALLS )
  675. DBGPRINTF((DBG_CONTEXT, "CWamInfo::StartShutdown\n"));
  676. HRESULT hr = NOERROR;
  677. LONG fShuttingDown;
  678. fShuttingDown = InterlockedExchange((LPLONG)&m_fShuttingDown, (LONG)TRUE);
  679. if ((BOOL)fShuttingDown == FALSE && m_pIWam != NULL)
  680. {
  681. IWam *pIWam = NULL;
  682. DBG_ASSERT( m_dwIWamGipCookie != NULL_GIP_COOKIE );
  683. if( FAILED( g_GIPAPI.Get( m_dwIWamGipCookie, IID_IWam, (void **)&pIWam )))
  684. {
  685. //
  686. // bad news - GIP refused to provide a pointer
  687. // we'll let higher levels to deal with it and return NULL here
  688. //
  689. pIWam = NULL;
  690. }
  691. else
  692. {
  693. hr = pIWam->StartShutdown();
  694. pIWam->Release();
  695. pIWam = NULL;
  696. }
  697. }
  698. LoopWaitForActiveRequests(cIgnoreRefs);
  699. return hr;
  700. } // CWamInfo::StartShutdown()
  701. /*-----------------------------------------------------------------------------
  702. CWamInfo::ClearMembers
  703. Clears the members that are not cleared by unit, so that
  704. it can be reused after a CPU Pause.
  705. Return:
  706. HRESULT
  707. -----------------------------------------------------------------------------*/
  708. void
  709. CWamInfo::ClearMembers
  710. (
  711. void
  712. )
  713. {
  714. m_fShuttingDown = FALSE;
  715. DBG_ASSERT(m_pIWam == NULL
  716. && m_pProcessEntry == NULL
  717. && m_dwIWamGipCookie == NULL_GIP_COOKIE
  718. );
  719. }
  720. /*-----------------------------------------------------------------------------
  721. CWamInfo::LoopWaitForActiveRequests
  722. Loop wait until all active requests are gone. Upon return from this call, there
  723. should be no more active requests for this WamInfo.
  724. Return:
  725. HRESULT
  726. -----------------------------------------------------------------------------*/
  727. void
  728. CWamInfo::LoopWaitForActiveRequests
  729. (
  730. INT cIgnoreRefs
  731. )
  732. {
  733. INT cBaseRef = 1 + cIgnoreRefs;
  734. //
  735. // Allow all other references to WAMINFO to drain away.
  736. // This loop is especially important in Unload scenario.
  737. // Unload Application while w3svc is still running, a busy application might
  738. // still have some outstanding WAMREQUEST unfinished which hold the reference to
  739. // this WAMINFO.
  740. //
  741. // m_cRef should be 1 in normal condition. However, m_cRef could be 0.
  742. // TODO: Update Comments.
  743. //
  744. //cBaseRef = (fIgnoreHashTableRef) ? 2;
  745. //while (m_cRef > cBaseRef)
  746. while(m_cCurrentRequests > 0)
  747. {
  748. DBGPRINTF((DBG_CONTEXT, "Still have out-standing reference(%d) to this WamInfo %08x\n",
  749. //m_cRef,
  750. m_cCurrentRequests,
  751. this));
  752. Sleep(20);
  753. }
  754. } // CWamInfo::LoopWaitForActiveRequests
  755. /*-----------------------------------------------------------------------------
  756. CWamInfo::UnInit
  757. Phase 2 of shutdown process.
  758. Unload WAM. Release the WAM object. Also release the Process handle.
  759. Return:
  760. HRESULT
  761. -----------------------------------------------------------------------------*/
  762. HRESULT
  763. CWamInfo::UnInit
  764. (
  765. void
  766. )
  767. {
  768. HRESULT hr = NOERROR;
  769. DBG_ASSERT( m_dwState == WIS_SHUTDOWN ||
  770. m_dwState == WIS_CPUPAUSE ||
  771. m_dwState == WIS_START
  772. );
  773. //
  774. // If there is a scheduled recycle pending, delete it here.
  775. //
  776. if ( m_dwRecycleSchedulerCookie && !m_fRecycled )
  777. {
  778. RemoveWorkItem( m_dwRecycleSchedulerCookie );
  779. }
  780. if (m_pIWam != NULL)
  781. {
  782. IWam * pIWam = NULL;
  783. if ( m_dwIWamGipCookie != NULL_GIP_COOKIE )
  784. {
  785. //
  786. // use a thread-valid pIWam from GIP
  787. //
  788. hr = g_GIPAPI.Get( m_dwIWamGipCookie, IID_IWam, (void **)&pIWam );
  789. if( SUCCEEDED( hr ) )
  790. {
  791. DBG_ASSERT( pIWam );
  792. pIWam->UninitWam();
  793. pIWam->Release();
  794. }
  795. //
  796. // get rid of the GIP entry
  797. // note - this hr will be the return result
  798. //
  799. hr = g_GIPAPI.Revoke( m_dwIWamGipCookie );
  800. m_dwIWamGipCookie = NULL_GIP_COOKIE;
  801. }
  802. // balance the AddRef in CWamInfo::Init.
  803. m_pIWam->Release();
  804. m_pIWam = NULL;
  805. }
  806. // If we got a handle for an Out Of Proc Wam, release it
  807. if( m_pProcessEntry != NULL)
  808. {
  809. // Process entry remains in the table, but this reference is
  810. // no longer valid
  811. g_pWamDictator->m_PTable.RemoveWamInfoFromProcessTable(this);
  812. m_pProcessEntry = NULL;
  813. // balance the AddRef in WamInfo::Init.
  814. Dereference();
  815. }
  816. return hr;
  817. }
  818. void
  819. CWamInfo::Print(void) const
  820. {
  821. if (m_pProcessEntry) {
  822. DBGPRINTF(( DBG_CONTEXT,
  823. " WAM_INFO(%08x) Refs=%d; TotalReqs=%d; dwState=%d;"
  824. " AppPath=%s;"
  825. " WamGipCookie=%d; clsid=" GUID_FORMAT " pidWam=%d; hWam=%08x\n"
  826. ,
  827. this, m_cRef, m_cTotalRequests, m_dwState,
  828. m_strApplicationPath.QueryStr(),
  829. m_dwIWamGipCookie, GUID_EXPAND( &m_clsidWam),
  830. m_pProcessEntry->QueryProcessId(),
  831. m_pProcessEntry->QueryProcessHandle()
  832. ));
  833. }
  834. else {
  835. DBGPRINTF(( DBG_CONTEXT,
  836. " WAM_INFO(%08x) Refs=%d; TotalReqs=%d; dwState=%d;"
  837. " AppPath=%s;"
  838. " WamGipCookie=%d; clsid=" GUID_FORMAT " No WAM Object\n"
  839. ,
  840. this, m_cRef, m_cTotalRequests, m_dwState,
  841. m_strApplicationPath.QueryStr(),
  842. m_dwIWamGipCookie, GUID_EXPAND( &m_clsidWam)
  843. ));
  844. }
  845. } // CWamInfo::Print()
  846. void CWamInfo::Recycle
  847. (
  848. DWORD dwTimeInSec
  849. )
  850. {
  851. RecycleLock();
  852. if ( m_fRecycled )
  853. {
  854. goto Done;
  855. }
  856. //
  857. // If dwTimeInSec is zero, set m_fRecycled to TRUE now. This
  858. // will prevent races within the scheduler when a CWamInfo
  859. // overshoots it's target number of requests and every request
  860. // wants to recycle.
  861. //
  862. if ( !dwTimeInSec )
  863. {
  864. m_fRecycled = TRUE;
  865. }
  866. //
  867. // Do the recycle on a different thread so that the current
  868. // request is not delayed.
  869. //
  870. if ( m_dwRecycleSchedulerCookie )
  871. {
  872. if ( ScheduleAdjustTime( m_dwRecycleSchedulerCookie, 0 ) != NO_ERROR )
  873. {
  874. //
  875. // Too late. We had a previously scheduled thread,
  876. // and it's not here anymore. It must have run.
  877. //
  878. goto Done;
  879. }
  880. else
  881. {
  882. goto Done;
  883. }
  884. }
  885. //
  886. // This reference is balanced by the callback function or the
  887. // zero m_dwRecycleSchedulerCookie check below.
  888. //
  889. Reference();
  890. m_dwRecycleSchedulerCookie = ScheduleWorkItem(
  891. RecycleCallback,
  892. this,
  893. dwTimeInSec,
  894. FALSE
  895. );
  896. if ( !m_dwRecycleSchedulerCookie )
  897. {
  898. Dereference();
  899. }
  900. Done:
  901. RecycleUnlock();
  902. }
  903. /*-----------------------------------------------------------------------------
  904. CWamInfo::QueryKey
  905. Query strMetabasePath.
  906. Return:
  907. HRESULT
  908. -----------------------------------------------------------------------------*/
  909. LPCSTR CWamInfo::QueryKey() const
  910. {
  911. return m_strApplicationPath.QueryStr();
  912. }
  913. /*-----------------------------------------------------------------------------
  914. COOPWamReqList::COOPWamReqList
  915. Constructor for COOPWamReqList
  916. -----------------------------------------------------------------------------*/
  917. COOPWamReqList::COOPWamReqList()
  918. : m_dwTimeStamp(0),
  919. m_fActive(TRUE)
  920. {
  921. InitializeListHead(&m_leRecoverListLink);
  922. InitializeListHead(&m_leOOPWamReqListHead);
  923. }
  924. /*-----------------------------------------------------------------------------
  925. COOPWamReqList::~COOPWamReqList
  926. Destructor for COOPWamReqList
  927. -----------------------------------------------------------------------------*/
  928. COOPWamReqList::~COOPWamReqList(VOID)
  929. {
  930. DBG_ASSERT(IsListEmpty(&m_leOOPWamReqListHead));
  931. // Reset link with m_rgOOPWamReqLists...
  932. InitializeListHead(&m_leRecoverListLink);
  933. }
  934. /*-----------------------------------------------------------------------------
  935. COOPWamReqList::FTimeToCleanup
  936. Check whether it is the time to cleanup the CleanupList. Must meet the condition that
  937. the time since the corresponding instance of WAM crashed elapsed is more that 10 min.
  938. -----------------------------------------------------------------------------*/
  939. BOOL COOPWamReqList::FTimeToCleanup(DWORD dwCurrentTime)
  940. {
  941. //
  942. // iF THIS WamReqList is an active list, return FALSE immediately, because
  943. // an ActiveOne have timestamp = 0, the following check most likely will trigger
  944. // a TRUE value.
  945. //
  946. if (m_fActive)
  947. {
  948. return FALSE;
  949. }
  950. // CASE 1
  951. // After the DEFAULT_CLEANUP_WAIT period, it is OK to cleanup this list.
  952. //
  953. if ((dwCurrentTime > m_dwTimeStamp) &&
  954. (dwCurrentTime - m_dwTimeStamp) > DEFAULT_CLEANUP_WAIT)
  955. {
  956. return TRUE;
  957. }
  958. // CASE 2, the value returned from GetTickCount() gets wraped, needs to take care
  959. // the special condition.
  960. //
  961. if ((dwCurrentTime < m_dwTimeStamp) &&
  962. ((dwCurrentTime - 0) + (0xFFFFFFFF - m_dwTimeStamp) > DEFAULT_CLEANUP_WAIT))
  963. {
  964. return TRUE;
  965. }
  966. return FALSE;
  967. }//CWamInfoOutProc::FTimeToCleanup
  968. /*-----------------------------------------------------------------------------
  969. CWamInfoOutProc::CWamInfoOutProc
  970. Arguments:
  971. pstrMetabasePath [in] a pointer to Metabase Path.
  972. fInProcess [in] in-proc/out-proc // remote is not a choice
  973. fEnableTryExcept [in] enable try/except flag
  974. clsidWam [in] the WAM CLSID
  975. -----------------------------------------------------------------------------*/
  976. CWamInfoOutProc::CWamInfoOutProc
  977. (
  978. IN const STR &strMetabasePath,
  979. IN BOOL fInProcess,
  980. IN BOOL fInPool,
  981. IN BOOL fEnableTryExcept,
  982. IN REFGUID clsidWam,
  983. IN DWORD dwThreshold,
  984. IN PW3_SERVER_INSTANCE pwsiInstance,
  985. IN BOOL fJobEnabled,
  986. IN DWORD dwPeriodicRestartRequests,
  987. IN DWORD dwPeriodicRestartTime,
  988. IN DWORD dwShutdownTimeLimit
  989. )
  990. :
  991. CWamInfo(strMetabasePath, fInProcess, fInPool, fEnableTryExcept, clsidWam),
  992. m_fInRepair(FALSE),
  993. m_fNoMoreRecovery(FALSE),
  994. m_hPermitOOPEvent((HANDLE)NULL),
  995. m_dwThreshold(dwThreshold),
  996. m_dwWamVersion(0),
  997. m_cRecoverList(0),
  998. m_idScheduled(0),
  999. m_pCurrentListHead(NULL),
  1000. m_pwsiInstance(pwsiInstance),
  1001. m_fJobEnabled(fJobEnabled)
  1002. {
  1003. InitializeListHead(&m_rgRecoverListHead);
  1004. INITIALIZE_CRITICAL_SECTION(&m_csList);
  1005. m_cMaxRequests = dwPeriodicRestartRequests;
  1006. m_dwRecycleTime = dwPeriodicRestartTime;
  1007. m_dwShutdownTimeLimit = dwShutdownTimeLimit;
  1008. }
  1009. /*-----------------------------------------------------------------------------
  1010. CWamInfoOutProc::~CWamInfoOutProc
  1011. Destructor for CWamInfoOutProc
  1012. -----------------------------------------------------------------------------*/
  1013. CWamInfoOutProc::~CWamInfoOutProc
  1014. (
  1015. VOID
  1016. )
  1017. {
  1018. DBG_ASSERT(IsListEmpty(&m_rgRecoverListHead));
  1019. DBG_ASSERT(m_hPermitOOPEvent == (HANDLE)NULL);
  1020. DeleteCriticalSection(&m_csList);
  1021. }//CWamInfoOutProc::~CWamInfoOutProc
  1022. /*-----------------------------------------------------------------------------
  1023. CWamInfoOutProc::InitOutProc
  1024. Init the CWamInfoOutProc data structure.
  1025. Argument:
  1026. fRepair - TRUE, if Init() is called to recreate an WAM after a OOP crash
  1027. Return:
  1028. HRESULT
  1029. -----------------------------------------------------------------------------*/
  1030. HRESULT
  1031. CWamInfoOutProc::Init
  1032. (
  1033. IN WCHAR* wszPackageId,
  1034. IN DWORD pidInetInfo
  1035. )
  1036. {
  1037. HRESULT hr = NOERROR;
  1038. COOPWamReqList *pList = NULL;
  1039. //
  1040. // Init a COOPWamReqList for the first OOP Wam instance.
  1041. //
  1042. pList = new COOPWamReqList();
  1043. if (NULL == pList)
  1044. {
  1045. hr = HRESULT_FROM_WIN32(GetLastError());
  1046. return hr;
  1047. }
  1048. else
  1049. {
  1050. //
  1051. // Insert to Tail of the LinkList, this action usually is done inside
  1052. // of a CS, however, since this is the Init time, it is already in a CS,
  1053. // there could only be one thread doing the Init part of CWamInfoOutProc.
  1054. // therefore, no CS is needed.
  1055. //
  1056. LockList();
  1057. InsertTailList(&m_rgRecoverListHead, &(pList->m_leRecoverListLink));
  1058. m_pCurrentListHead = &(pList->m_leOOPWamReqListHead);
  1059. m_cRecoverList++;
  1060. UnLockList();
  1061. }
  1062. hr = CWamInfo::Init(wszPackageId, pidInetInfo);
  1063. if (SUCCEEDED(hr))
  1064. {
  1065. if (m_fJobEnabled)
  1066. {
  1067. DBG_ASSERT(m_pwsiInstance != NULL && m_pProcessEntry->QueryProcessId() != NULL);
  1068. m_pwsiInstance->AddProcessToJob(m_pProcessEntry->QueryProcessHandle(), TRUE);
  1069. }
  1070. //
  1071. // m_hPermitOOPEvent can served as m_fInited.
  1072. //
  1073. m_hPermitOOPEvent = IIS_CREATE_EVENT(
  1074. "CWamInfoOutProc::m_hPermitOOPEvent",
  1075. this,
  1076. TRUE,
  1077. TRUE
  1078. );
  1079. if (NULL == m_hPermitOOPEvent)
  1080. {
  1081. hr = HRESULT_FROM_WIN32(GetLastError());
  1082. }
  1083. }
  1084. //
  1085. // If either CWamInfo::Init or Create Event failed, we need to
  1086. // remove the pList from CleanupListHead.
  1087. //
  1088. if (FAILED(hr) && pList)
  1089. {
  1090. RemoveHeadList(&m_rgRecoverListHead);
  1091. delete pList;
  1092. pList = NULL;
  1093. m_cRecoverList--;
  1094. }
  1095. return hr;
  1096. }//CWamInfoOutProc::Init
  1097. /*-----------------------------------------------------------------------------
  1098. CWamInfoOutProc::UnInit
  1099. Phase 2 of shutdown process.
  1100. Uninit the CWamInfoOutProc data structure.
  1101. Argument:
  1102. VOID
  1103. Return:
  1104. HRESULT
  1105. -----------------------------------------------------------------------------*/
  1106. HRESULT
  1107. CWamInfoOutProc::UnInit
  1108. (
  1109. void
  1110. )
  1111. {
  1112. HRESULT hr = NOERROR;
  1113. hr = CWamInfo::UnInit();
  1114. FinalCleanup();
  1115. if (m_hPermitOOPEvent)
  1116. {
  1117. CloseHandle(m_hPermitOOPEvent);
  1118. m_hPermitOOPEvent = (HANDLE)NULL;
  1119. }
  1120. return hr;
  1121. }//CWamInfoOutProc::UnInit
  1122. /*-----------------------------------------------------------------------------
  1123. CWamInfoOutProc::ClearMembers
  1124. Clears the members that are not cleared by unit, so that
  1125. it can be reused after a CPU Pause.
  1126. Return:
  1127. HRESULT
  1128. -----------------------------------------------------------------------------*/
  1129. VOID
  1130. CWamInfoOutProc::ClearMembers()
  1131. {
  1132. CWamInfo::ClearMembers();
  1133. m_cRecoverList = 0;
  1134. }
  1135. /*-----------------------------------------------------------------------------
  1136. CWamInfo::LoopWaitForActiveRequests
  1137. Loop wait until all active requests are gone. Upon return from this call, there
  1138. should be no more active requests for this WamInfo.
  1139. Return:
  1140. HRESULT
  1141. -----------------------------------------------------------------------------*/
  1142. void
  1143. CWamInfoOutProc::LoopWaitForActiveRequests
  1144. (
  1145. INT cIgnoreRefs
  1146. )
  1147. {
  1148. INT cCount = 0;
  1149. BOOL fWaitforActiveReq = TRUE;
  1150. BOOL fDidForceKill = FALSE;
  1151. BOOL fForceRequestCleanup;
  1152. //
  1153. // No need to unlock/lock the OOPWamRequestList in order to check list is empty or not.
  1154. //
  1155. while(m_pCurrentListHead && fWaitforActiveReq)
  1156. {
  1157. if (IsListEmpty(m_pCurrentListHead))
  1158. {
  1159. fWaitforActiveReq = FALSE;
  1160. }
  1161. Sleep(20);
  1162. cCount++;
  1163. // 2 sec = 20 ms * 100 times
  1164. if (cCount > 100 && fDidForceKill == FALSE)
  1165. {
  1166. BOOL fRet;
  1167. UINT uExitCode = 0;
  1168. fRet = TerminateProcess(m_pProcessEntry->QueryProcessHandle(), uExitCode);
  1169. if (!fRet)
  1170. {
  1171. DBGPRINTF((DBG_CONTEXT, "Unable to TerminateProcess, error %d\n", GetLastError()));
  1172. }
  1173. fDidForceKill = TRUE;
  1174. //
  1175. // Walk the list of current WAM_REQUESTs
  1176. //
  1177. while(!IsListEmpty(m_pCurrentListHead))
  1178. {
  1179. LockList();
  1180. LIST_ENTRY * ple = RemoveHeadList(m_pCurrentListHead);
  1181. DBG_ASSERT(ple);
  1182. WAM_REQUEST * pWamRequest = CONTAINING_RECORD(
  1183. ple,
  1184. WAM_REQUEST,
  1185. m_leOOP);
  1186. // Completely disassociate this wamreq from the list before
  1187. // leaving the critical section.
  1188. InitializeListHead( &pWamRequest->m_leOOP );
  1189. // Is wamreq about to be cleaned up by itself?
  1190. fForceRequestCleanup =
  1191. !( pWamRequest->InterlockedNonZeroAddRef() == 0 );
  1192. UnLockList();
  1193. if( fForceRequestCleanup )
  1194. {
  1195. //
  1196. // Invalidate any references that COM might have
  1197. //
  1198. CoDisconnectObject( static_cast<IWamRequest *>(pWamRequest), NULL );
  1199. //
  1200. // This triggers WAM_REQUEST destructor
  1201. //
  1202. pWamRequest->Release();
  1203. }
  1204. }
  1205. fWaitforActiveReq = FALSE;
  1206. }
  1207. }
  1208. } // CWamInfoOutProc::LoopWaitForActiveRequests
  1209. /*-----------------------------------------------------------------------------
  1210. CWamInfoOutProc::EnterOOPZone
  1211. Enter the OOP Zone. If there is a WAM crash-recovery in place, the newer coming requests are blocked
  1212. until the new WAM construction finished. And after the number of crashes reaches a threshold, the
  1213. function will assigned a NULL pointer to IWam return value. Means WamInfoOutProc will no longer serve
  1214. any new requests.
  1215. Parameter:
  1216. pWamRequest a pointer to WamRequest.
  1217. pdwWamVersion pointer to a DWORD buffer holds the current WamVersion upon return.
  1218. fRecord TRUE, add WamRequest to the Current OOPWamReqList.
  1219. FALSE, no-op.
  1220. NOTE:
  1221. EnterOOPZone is called by ProcessAsyncIO(). However, the time a WamRequest in the ProcessAsynIO() call,
  1222. the WamRequest is already recorded on the OOPWamRequest, therefore, there is no need to add the WamRequest
  1223. to OOPWamRequest again.
  1224. -----------------------------------------------------------------------------*/
  1225. IWam *
  1226. CWamInfoOutProc::EnterOOPZone
  1227. (
  1228. WAM_REQUEST * pWamRequest,
  1229. DWORD *pdwWamVersion,
  1230. BOOL fRecord
  1231. )
  1232. {
  1233. IWam * pIWam = NULL;
  1234. //
  1235. // In Repair()
  1236. // We first ReSet the PermitOOPEvent, and then raise the flag.
  1237. // Therefore, if m_fInRepair, all incoming threads will now wait.
  1238. //
  1239. LWait:
  1240. if (m_fInRepair)
  1241. {
  1242. DWORD dwReturn = WaitForSingleObject(m_hPermitOOPEvent, INFINITE);
  1243. DBG_ASSERT(dwReturn == WAIT_OBJECT_0);
  1244. }
  1245. LockList();
  1246. if (m_fInRepair)
  1247. {
  1248. UnLockList();
  1249. goto LWait;
  1250. }
  1251. else
  1252. {
  1253. if (!FExceedCrashLimit() && !m_fShuttingDown)
  1254. {
  1255. if (fRecord)
  1256. {
  1257. InsertHeadList(m_pCurrentListHead, &pWamRequest->m_leOOP);
  1258. }
  1259. //
  1260. // get thread-valid IWam (see #122711)
  1261. //
  1262. DBG_ASSERT( m_dwIWamGipCookie != NULL_GIP_COOKIE );
  1263. if( FAILED( g_GIPAPI.Get( m_dwIWamGipCookie, IID_IWam, (void **)&pIWam )))
  1264. {
  1265. //
  1266. // bad news - GIP refused to provide a pointer
  1267. // we'll let higher levels to deal with it and return NULL here
  1268. //
  1269. pIWam = NULL;
  1270. }
  1271. *pdwWamVersion = m_dwWamVersion;
  1272. }
  1273. else
  1274. {
  1275. if (fRecord)
  1276. {
  1277. InitializeListHead(&pWamRequest->m_leOOP);
  1278. }
  1279. pIWam = NULL;
  1280. *pdwWamVersion = m_dwWamVersion;
  1281. }
  1282. }
  1283. UnLockList();
  1284. //DBG_ASSERT(pIWam != NULL);
  1285. return pIWam;
  1286. } //CWamInfo::EnterOOPZone
  1287. /*-----------------------------------------------------------------------------
  1288. CWamInfoOutProc::LeaveOOPZone
  1289. Leave OOP zone. take itself from the OOPWamReqList.
  1290. Parameter:
  1291. pWamRequest a pointer to a WamRequest that is going to be taken out from OOPWamReqList.
  1292. fRecord FALSE, NO-OP. Please see comments in EnterOOPZone.
  1293. FALSE, only used in ProcessAsyncIO.
  1294. Note:
  1295. -----------------------------------------------------------------------------*/
  1296. VOID
  1297. CWamInfoOutProc::LeaveOOPZone
  1298. (
  1299. WAM_REQUEST * pWamRequest,
  1300. BOOL fRecord
  1301. )
  1302. {
  1303. if (fRecord)
  1304. {
  1305. //
  1306. // Remove the wamreq from the list of active requests
  1307. //
  1308. LockList();
  1309. if (!IsListEmpty(&pWamRequest->m_leOOP))
  1310. {
  1311. RemoveEntryList(&pWamRequest->m_leOOP);
  1312. }
  1313. InitializeListHead(&pWamRequest->m_leOOP);
  1314. UnLockList();
  1315. }
  1316. return;
  1317. }
  1318. HRESULT CWamInfoOutProc::PreProcessWamRequest
  1319. (
  1320. IN WAM_REQUEST* pWamRequest,
  1321. IN HTTP_REQUEST* pHttpRequest,
  1322. OUT IWam ** ppIWam,
  1323. OUT BOOL * pfHandled
  1324. )
  1325. {
  1326. DWORD dwVersion = 0;
  1327. HRESULT hrReturn = NOERROR;
  1328. IWam* pIWam = NULL;
  1329. BOOL fFinished;
  1330. DBG_ASSERT(pWamRequest);
  1331. pIWam = EnterOOPZone(pWamRequest, &dwVersion, TRUE);
  1332. // stamp the wamreq with wam's current version
  1333. pWamRequest->SetWamVersion( dwVersion );
  1334. if (NULL == pIWam)
  1335. {
  1336. // We reach this state only if we exceed the crash limit.
  1337. DBG_ASSERT(FExceedCrashLimit());
  1338. //
  1339. // write log message for the failed wam request
  1340. //
  1341. if ( !pWamRequest->IsChild() )
  1342. {
  1343. pWamRequest->WriteLogInfo(
  1344. SZ_FAILED_OOP_EXCEEDED_CRASH_LOG_MESSAGE,
  1345. HT_SERVER_ERROR,
  1346. RPC_S_CALL_FAILED);
  1347. pHttpRequest->Disconnect(
  1348. HT_SERVER_ERROR,
  1349. IDS_WAM_NOMORERECOVERY_ERROR,
  1350. TRUE,
  1351. &fFinished);
  1352. }
  1353. DBGPRINTF((DBG_CONTEXT, "Exceed crash limit, Wam Request %08x quits\n",
  1354. pWamRequest));
  1355. *pfHandled = TRUE;
  1356. hrReturn = HRESULT_FROM_WIN32( RPC_S_CALL_FAILED );
  1357. }
  1358. *ppIWam = pIWam;
  1359. return hrReturn;
  1360. }//CWamInfoOutProc::PreProcessRequest
  1361. HRESULT
  1362. CWamInfoOutProc::PostProcessRequest
  1363. (
  1364. IN HRESULT hrIn,
  1365. IN WAM_REQUEST * pWamRequest
  1366. )
  1367. {
  1368. // 1> If the OOP call failed during the call, RPC returns RPC_S_CALL_FAILED.
  1369. // ie. OOP process crashed during the call
  1370. // 2> If OOP process crashed before the call, RPC returns RPC_S_CALL_FAILED_DNE.
  1371. //
  1372. // Subsequest requests to the crashed OOP Process will get RPC_S_SERVER_UNAVAILABLE.
  1373. //
  1374. if (RPC_S_CALL_FAILED == WIN32_FROM_HRESULT(hrIn) ||
  1375. RPC_S_CALL_FAILED_DNE == WIN32_FROM_HRESULT(hrIn) ||
  1376. RPC_S_SERVER_UNAVAILABLE == WIN32_FROM_HRESULT(hrIn))
  1377. {
  1378. const BOOL F_INRECOVERY = TRUE;
  1379. const BOOL F_HEALTHY = FALSE;
  1380. HRESULT hrCleanup;
  1381. DBGPRINTF((
  1382. DBG_CONTEXT
  1383. , "CWamInfoOutProc(%08x) "
  1384. "calling CoDisconnectObject on pWamRequest(%08x)\n"
  1385. , this
  1386. , pWamRequest
  1387. ));
  1388. //
  1389. // write log message for the failed wam request
  1390. //
  1391. if (pWamRequest && !pWamRequest->IsChild() )
  1392. {
  1393. DWORD dwHTHeader = (!m_fShuttingDown) ? HT_SERVER_ERROR : HT_SVC_UNAVAILABLE;
  1394. pWamRequest->DisconnectOnServerError(dwHTHeader, WIN32_FROM_HRESULT(hrIn));
  1395. pWamRequest->WriteLogInfo(
  1396. SZ_FAILED_OOP_REQUEST_LOG_MESSAGE,
  1397. dwHTHeader,
  1398. WIN32_FROM_HRESULT(hrIn));
  1399. }
  1400. hrCleanup = CoDisconnectObject(
  1401. static_cast<IWamRequest *>(pWamRequest),
  1402. NULL
  1403. );
  1404. DBG_ASSERT(hrCleanup == S_OK);
  1405. if (F_INRECOVERY == (BOOL)g_pfnInterlockedCompareExchange(
  1406. (LONG *)&m_fInRepair,
  1407. (LONG)F_INRECOVERY,
  1408. (LONG)F_HEALTHY))
  1409. {
  1410. // Other thread already doing the recovery job.
  1411. DBGPRINTF((DBG_CONTEXT, "Recovery mode: Other thread is doing recovery job.\n"));
  1412. //exit this function
  1413. return hrIn;
  1414. }
  1415. //
  1416. // Try to Repair(call repair function). But other thread might already working on the repairing
  1417. // Therefore, this thread will return immediately if repairing is in-place.
  1418. // Forget about pWamRequest->Release(), whoever(thread) doing the repairing job will call
  1419. // self-destruction on the pWamRequest.
  1420. //
  1421. Repair();
  1422. if (F_HEALTHY == (BOOL)g_pfnInterlockedCompareExchange(
  1423. (LONG *)&m_fInRepair,
  1424. (LONG)F_HEALTHY,
  1425. (LONG)F_INRECOVERY))
  1426. {
  1427. // Other thread already doing the recovery job.
  1428. DBG_ASSERT(FALSE);
  1429. }
  1430. return hrIn;
  1431. }
  1432. return NOERROR;
  1433. }// CWamInfoOutProc::PreProcessRequest
  1434. /*-----------------------------------------------------------------------------
  1435. -----------------------------------------------------------------------------*/
  1436. HRESULT CWamInfoOutProc::PreProcessAsyncIO
  1437. (
  1438. IN WAM_REQUEST* pWamRequest,
  1439. OUT IWam ** ppIWam
  1440. )
  1441. {
  1442. HRESULT hr = S_OK;
  1443. IWam* pIWam = NULL;
  1444. DWORD dwVersion;
  1445. pIWam = EnterOOPZone(pWamRequest, &dwVersion, FALSE);
  1446. if (dwVersion > pWamRequest->GetWamVersion())
  1447. {
  1448. // reject the request if current wam version is later
  1449. // than wam version when request began
  1450. DBGPRINTF(( DBG_CONTEXT,
  1451. "CWamInfoOutProc::ProcessAsyncIO - Crashed since request was started. "
  1452. "Wam Request %08x quits\n",
  1453. pWamRequest));
  1454. hr = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED_DNE); // UNDONE better return code?
  1455. }
  1456. else
  1457. {
  1458. if (NULL == pIWam)
  1459. {
  1460. // We reach this state if we exceed the crash limit
  1461. // or if we failed to obtain thread-valid pointer from GIP
  1462. // note that higher levels rely on hr and *don't* check the pointer
  1463. DBG_ASSERT(FExceedCrashLimit());
  1464. DBGPRINTF((DBG_CONTEXT, "Exceed crash limit, Wam Request %08x quits\n",
  1465. pWamRequest));
  1466. hr = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED_DNE);
  1467. }
  1468. }
  1469. *ppIWam = pIWam;
  1470. return hr;
  1471. }
  1472. /*-----------------------------------------------------------------------------
  1473. CWamInfoOutProc::Repair
  1474. This is the recovery function. And this function refused to repair the WAM if the crashes exceed a
  1475. threshold.
  1476. -----------------------------------------------------------------------------*/
  1477. HRESULT
  1478. CWamInfoOutProc::Repair
  1479. (
  1480. VOID
  1481. )
  1482. {
  1483. HRESULT hr = NOERROR;
  1484. COOPWamReqList* pNewList = NULL;
  1485. BOOL fExceedCrashLimit; // flag indicates we exceed crash limit or not this time
  1486. //
  1487. // Step 1. ReSet FinishRecoveryEvent to UnSignaled State.
  1488. //
  1489. //DBG_ASSERT( m_pProcessEntry );
  1490. if ( m_pProcessEntry )
  1491. {
  1492. m_pProcessEntry->NotifyCrashed();
  1493. }
  1494. ResetEvent(m_hPermitOOPEvent);
  1495. if (m_dwState == WIS_SHUTDOWN || m_dwState == WIS_CPUPAUSE)
  1496. {
  1497. //LockList();
  1498. //m_pCurrentListHead = NULL;
  1499. //UnLockList();
  1500. //
  1501. // Set Event, so that let all other wait threads go
  1502. //
  1503. SetEvent(m_hPermitOOPEvent);
  1504. goto LExit;
  1505. }
  1506. //
  1507. // Make sure m_pCurrentListHead is a valid one.
  1508. //
  1509. DBG_ASSERT(m_pCurrentListHead);
  1510. //
  1511. // Step 2. Call CleanupAll to release any empty(or timedout) OOPWamReqList
  1512. // resources. Note: CleanupAll does not touch the list pointed by m_pCurrentListHead.
  1513. //
  1514. //
  1515. CleanupAll(FALSE);
  1516. DBGPRINTF((DBG_CONTEXT, "Application %s(%08x) currently has %d OOPWamReqLists, %d crashes, m_pCurrentListHead %08x\n",
  1517. m_strApplicationPath.QueryStr(),
  1518. this,
  1519. m_cRecoverList,
  1520. m_dwWamVersion+1,
  1521. m_pCurrentListHead
  1522. ));
  1523. //
  1524. // Step 3: Create a new OOPWamReqList, and update m_pCurrentListHead to it.
  1525. //
  1526. LockList();
  1527. COOPWamReqList *pCleanupList;
  1528. pCleanupList = CONTAINING_RECORD(m_pCurrentListHead, COOPWamReqList, m_leOOPWamReqListHead);
  1529. //
  1530. // if the CurrentList is empty, then, it is OK to remove the OOPWamReqList pointed
  1531. // by CurrentListHead. Done this step in a CriticalSection. That is, you can only
  1532. // access OOPWamReqList in a CS.
  1533. //
  1534. if (IsListEmpty(m_pCurrentListHead))
  1535. {
  1536. RemoveEntryList(&pCleanupList->m_leRecoverListLink);
  1537. InterlockedDecrement((LPLONG)&m_cRecoverList);
  1538. delete pCleanupList;
  1539. }
  1540. else
  1541. {
  1542. DBG_ASSERT(pCleanupList != NULL);
  1543. pCleanupList->SetTimeStamp();
  1544. }
  1545. // m_pCurrentListHead is invalid until reassigned a new OOPList later.
  1546. //
  1547. // If WamInfoOutProc has already reach the OOPWamReqList resource threshould.
  1548. //
  1549. fExceedCrashLimit = FExceedCrashLimit();
  1550. //
  1551. // If we did not exceed the crash limit, then, it is OK to add another OOPWamReqList.
  1552. //
  1553. if (!fExceedCrashLimit)
  1554. {
  1555. InterlockedIncrement((LPLONG)&m_dwWamVersion);
  1556. //
  1557. // Alloc a new CleanupList resource
  1558. //
  1559. pNewList = new COOPWamReqList();
  1560. if (pNewList != NULL)
  1561. {
  1562. InsertTailList(&m_rgRecoverListHead, &(pNewList->m_leRecoverListLink));
  1563. InterlockedIncrement((LPLONG)&m_cRecoverList);
  1564. //
  1565. // Activate new OOPWamReq List.
  1566. //
  1567. m_pCurrentListHead = &pNewList->m_leOOPWamReqListHead;
  1568. }
  1569. else
  1570. {
  1571. //
  1572. // Find out exact what error cause new operation to fail
  1573. //
  1574. m_pCurrentListHead = NULL;
  1575. hr = HRESULT_FROM_WIN32(GetLastError());
  1576. }
  1577. }
  1578. UnLockList();
  1579. //
  1580. // Step 3. Wam is bad. Recreate the wam.
  1581. //
  1582. if (SUCCEEDED(hr) && !fExceedCrashLimit)
  1583. {
  1584. DBG_ASSERT(m_pCurrentListHead);
  1585. hr = ReInitWam();
  1586. if (FAILED(hr))
  1587. {
  1588. // If ReInitWam failed, then, we can assume there is some thing went really wrong,
  1589. // We will treate the application as it exceeds the crash limit.
  1590. LockList();
  1591. m_fNoMoreRecovery = TRUE;
  1592. m_pCurrentListHead = NULL;
  1593. UnLockList();
  1594. DBGPRINTF((DBG_CONTEXT, "Application %s(%08x) Fail to ReInitWam() in Repair, hr = %08x\n",
  1595. m_strApplicationPath.QueryStr(),
  1596. this,
  1597. hr));
  1598. }
  1599. }
  1600. // Assert that either hr succeeded or list head is null
  1601. DBG_ASSERT( SUCCEEDED(hr) || (m_pCurrentListHead == NULL) );
  1602. fExceedCrashLimit = FExceedCrashLimit();
  1603. //
  1604. // Set Event, so that let all other wait threads go
  1605. //
  1606. SetEvent(m_hPermitOOPEvent);
  1607. //
  1608. // Log the crash.
  1609. //
  1610. const CHAR *pszEventLog[1];
  1611. pszEventLog[0] = m_strApplicationPath.QueryStr();
  1612. // Event log
  1613. g_pInetSvc->LogEvent(W3_EVENT_OOPAPP_VANISHED,
  1614. 1,
  1615. pszEventLog,
  1616. 0);
  1617. //
  1618. // Crash control. If the OOP Server keeps crashing, it's a waste of computer resource
  1619. // to rebuild a ill-formed ISAPI DLL. Therefore, we will stop to service this WAMINFO
  1620. // and log our decision in the eventlog.
  1621. if (fExceedCrashLimit)
  1622. {
  1623. // Event log
  1624. g_pInetSvc->LogEvent(W3_EVENT_NO_MORE_CRASH,
  1625. 1,
  1626. pszEventLog,
  1627. 0);
  1628. }
  1629. LExit:
  1630. return hr;
  1631. }
  1632. /*-----------------------------------------------------------------------------
  1633. CWamInfoOutProc::Cleanup
  1634. Clean up all wamrequests in a OOPWamRequestList. call WamRequest Self Destruction fuction directly.
  1635. Argument:
  1636. pCleanupList: a pointer to COOPWamRequestList.
  1637. -----------------------------------------------------------------------------*/
  1638. BOOL
  1639. CWamInfoOutProc::Cleanup
  1640. (
  1641. COOPWamReqList* pCleanupList
  1642. )
  1643. {
  1644. PLIST_ENTRY ple = NULL;
  1645. WAM_REQUEST* pWamRequest = NULL;
  1646. BOOL fFoundEntry = TRUE;
  1647. // Always try to clean up any requests in the list.
  1648. while (fFoundEntry)
  1649. {
  1650. LockList();
  1651. if (!IsListEmpty(&(pCleanupList->m_leOOPWamReqListHead)))
  1652. {
  1653. ple = RemoveHeadList(&(pCleanupList->m_leOOPWamReqListHead));
  1654. DBG_ASSERT(ple);
  1655. pWamRequest = CONTAINING_RECORD(
  1656. ple,
  1657. WAM_REQUEST,
  1658. m_leOOP);
  1659. // Completely disassociate the wamreq from this list before
  1660. // we leave this critical section.
  1661. InitializeListHead( &pWamRequest->m_leOOP );
  1662. if (0 == pWamRequest->InterlockedNonZeroAddRef())
  1663. {
  1664. // Last reference to wamreq is gone, so cleanup is already
  1665. // happening
  1666. UnLockList();
  1667. continue;
  1668. }
  1669. }
  1670. else
  1671. {
  1672. fFoundEntry = FALSE;
  1673. }
  1674. UnLockList();
  1675. if (fFoundEntry)
  1676. {
  1677. HRESULT hr;
  1678. if ( !pWamRequest->IsChild() )
  1679. {
  1680. pWamRequest->WriteLogInfo(
  1681. SZ_FAILED_OOP_REQUEST_LOG_MESSAGE,
  1682. HT_SERVER_ERROR,
  1683. E_FAIL);
  1684. }
  1685. //
  1686. // Assume all the requests in the stage is only refed by COM.
  1687. hr = CoDisconnectObject(
  1688. static_cast<IWamRequest *>(pWamRequest),
  1689. NULL
  1690. );
  1691. pWamRequest->Release();
  1692. }
  1693. }
  1694. // Got to assert pCleanupList is cleaned.
  1695. DBG_ASSERT(IsListEmpty(&(pCleanupList->m_leOOPWamReqListHead)));
  1696. return TRUE;
  1697. }
  1698. /*-----------------------------------------------------------------------------
  1699. CWamInfoOutProc::CleanupAll
  1700. This function goes through the CleanupLists. It skips the current OOP list during the walk through,
  1701. If an CleanupList's timestamp indicates it is ready to cleanup, it triggers a cleanup of WAMREQUESTs in
  1702. that list. After the cleanup, the CleanupList will be removed from the linklist of CleanupList.
  1703. If a Cleanuplist contains no WAMREQUEST, this function will remove the CleanupList from the
  1704. linklist of CleanupList.
  1705. Parameter:
  1706. fScheduled: TRUE if the function is called in a scheduler.
  1707. FALSE, otherwise.
  1708. return:
  1709. BOOL
  1710. Note:
  1711. This function does not touch the CleanupList pointed by m_pCurrentListHead.
  1712. -----------------------------------------------------------------------------*/
  1713. BOOL
  1714. CWamInfoOutProc::CleanupAll
  1715. (
  1716. BOOL fScheduled
  1717. )
  1718. {
  1719. BOOL fReturn = TRUE;
  1720. PLIST_ENTRY pTemp = NULL;
  1721. COOPWamReqList* pCleanupList = NULL;
  1722. DWORD dwCurrentTime = GetTickCount();
  1723. LIST_ENTRY rgCleanupListHead;
  1724. InitializeListHead(&rgCleanupListHead);
  1725. DBG_CODE
  1726. (
  1727. DWORD cnt = 0;
  1728. );
  1729. LockList();
  1730. pTemp = (&m_rgRecoverListHead)->Flink;
  1731. while (pTemp != &m_rgRecoverListHead)
  1732. {
  1733. DBG_ASSERT(pTemp != NULL);
  1734. pCleanupList = CONTAINING_RECORD(pTemp, COOPWamReqList, m_leRecoverListLink);
  1735. pTemp = pTemp->Flink;
  1736. //
  1737. // Skip the Current Active list. A rare case when called from a Scheduler,
  1738. // the m_pCurrentListHead gets changed during the loop, in that case, we just
  1739. // have more than 2 LinkLists after the loop, therefore, a Cleanup will be scheduled
  1740. // again.
  1741. //
  1742. if (IsListEmpty(&pCleanupList->m_leOOPWamReqListHead) && !pCleanupList->FActive())
  1743. {
  1744. //
  1745. // pCleanupList record can be released.
  1746. // If pCleanupList is active, move on to the next Link.
  1747. // Otherwise, the list needs to be cleaned.
  1748. //
  1749. RemoveEntryList(&pCleanupList->m_leRecoverListLink);
  1750. InterlockedDecrement((LPLONG)&m_cRecoverList);
  1751. delete pCleanupList;
  1752. }
  1753. else if(pCleanupList->FTimeToCleanup(dwCurrentTime))
  1754. {
  1755. //
  1756. // pCleanupList is timedout, therefore, it is ok to clean all WamRequests
  1757. // in that list and then remove the list.
  1758. //
  1759. //
  1760. RemoveEntryList(&pCleanupList->m_leRecoverListLink);
  1761. InterlockedDecrement((LPLONG)&m_cRecoverList);
  1762. InsertTailList(&rgCleanupListHead, &pCleanupList->m_leRecoverListLink);
  1763. }
  1764. else
  1765. {// No Op. Keep the list there.
  1766. }
  1767. }
  1768. if (fScheduled)
  1769. {
  1770. //
  1771. // There are some cleanup list other than the current active list,
  1772. // therefore, need to schedule another work item.
  1773. //
  1774. if (m_cRecoverList > 1)
  1775. {
  1776. m_idScheduled = ScheduleWorkItem
  1777. (
  1778. CWamInfoOutProc::CleanupScheduled,
  1779. (VOID *)this,
  1780. DEFAULT_CLEANUP_WAIT,
  1781. FALSE
  1782. );
  1783. DBG_ASSERT(m_idScheduled != 0);
  1784. if (m_idScheduled == 0)
  1785. {
  1786. fReturn = FALSE;
  1787. }
  1788. }
  1789. else
  1790. {
  1791. //
  1792. // otherwise, no more to cleanup. Since this is called from Scheduled thread,
  1793. // therefore, m_pCurrentListHead is a valid OOP List head. Then, cleanup list
  1794. // count == 1 tells us no other cleanup lists need to be cleanup. reset the
  1795. // scheduled id to be 0.
  1796. //
  1797. m_idScheduled = 0;
  1798. }
  1799. }
  1800. else
  1801. {
  1802. //
  1803. // If this is called right after a crash, then, we need to check
  1804. // if there is not scheduled workitem and there are some wam requests on
  1805. // the current OOPList, we need to schedule a workitem.
  1806. //
  1807. if (m_idScheduled == 0 && !IsListEmpty(m_pCurrentListHead))
  1808. {
  1809. m_idScheduled = ScheduleWorkItem
  1810. (
  1811. CWamInfoOutProc::CleanupScheduled,
  1812. (VOID *)this,
  1813. DEFAULT_CLEANUP_WAIT,
  1814. FALSE
  1815. );
  1816. DBG_ASSERT(m_idScheduled != 0);
  1817. if (m_idScheduled == 0)
  1818. {
  1819. fReturn = FALSE;
  1820. }
  1821. }
  1822. }
  1823. UnLockList();
  1824. //
  1825. // Cleanup the lists.
  1826. //
  1827. while (!IsListEmpty(&rgCleanupListHead))
  1828. {
  1829. pTemp = RemoveHeadList(&rgCleanupListHead);
  1830. pCleanupList = CONTAINING_RECORD(pTemp, COOPWamReqList, m_leRecoverListLink);
  1831. Cleanup(pCleanupList);
  1832. delete pCleanupList;
  1833. DBG_CODE(cnt++;);
  1834. }
  1835. DBG_CODE
  1836. (
  1837. DBGPRINTF((DBG_CONTEXT, "Application %s(%08x) CleanupAll cleaned up %d list(s), idScheduled %d, m_cRecoverList %d\n",
  1838. m_strApplicationPath.QueryStr(),
  1839. this,
  1840. cnt,
  1841. m_idScheduled,
  1842. m_cRecoverList));
  1843. );
  1844. return fReturn;
  1845. }
  1846. /*-----------------------------------------------------------------------------
  1847. CWamInfoOutProc::FinalCleanup
  1848. Cleanup function called at Shutdown. It cancelled any out-standing scheduled workitem. And do the
  1849. clean up itself.
  1850. Argument:
  1851. None.
  1852. -----------------------------------------------------------------------------*/
  1853. BOOL
  1854. CWamInfoOutProc::FinalCleanup(VOID)
  1855. {
  1856. DWORD cList = m_cRecoverList; // Count of CleanupList in the WamInfo.
  1857. COOPWamReqList* pList = NULL;
  1858. PLIST_ENTRY ple = NULL;
  1859. if (0 != m_idScheduled)
  1860. {
  1861. //
  1862. // Still have out-standing Scheduled cleanup work item.
  1863. // Remove the scheduled cleanup work item here.
  1864. // And let me clean up.
  1865. //
  1866. DWORD dwOldCookie = m_idScheduled;
  1867. BOOL fRemoved;
  1868. fRemoved = RemoveWorkItem(dwOldCookie);
  1869. if (fRemoved)
  1870. {
  1871. // The Work Item is removed.
  1872. // This thread is going to clean up.
  1873. InterlockedExchange((LPLONG)&(m_idScheduled), 0);
  1874. }
  1875. else
  1876. {
  1877. DWORD dwErr = GetLastError();
  1878. DBGPRINTF((DBG_CONTEXT, "Application %s(%08x) failed to removeWorkItem(%d), ErrCode %d\n",
  1879. m_strApplicationPath.QueryStr(),
  1880. this,
  1881. m_idScheduled,
  1882. dwErr));
  1883. DBG_ASSERT(FALSE);
  1884. }
  1885. }
  1886. //
  1887. // Walk through the cleanuplist head list, remove all cleanup list.
  1888. //
  1889. while(!IsListEmpty(&m_rgRecoverListHead))
  1890. {
  1891. LockList();
  1892. ple = RemoveHeadList(&m_rgRecoverListHead);
  1893. UnLockList();
  1894. pList = CONTAINING_RECORD(ple, COOPWamReqList, m_leRecoverListLink);
  1895. DBG_ASSERT(pList);
  1896. // No out-standing Scheduled cleanup work item.
  1897. // Verify the list should be clean.
  1898. //
  1899. if ( !(IsListEmpty(&(pList->m_leOOPWamReqListHead))))
  1900. {
  1901. // The List can have some element without a WorkItem Scheduled.
  1902. // A crash happens right after a request finishs OOP operation but before
  1903. // COM releases the request's refs.
  1904. Cleanup(pList);
  1905. }
  1906. //
  1907. // Work Item is already finished.
  1908. // The List should be empty now.
  1909. //
  1910. DBG_ASSERT(IsListEmpty(&(pList->m_leOOPWamReqListHead)));
  1911. delete pList;
  1912. pList = NULL;
  1913. cList--;
  1914. }
  1915. //
  1916. // cList should be 0 and the ListHead should be empty too.
  1917. //
  1918. DBG_ASSERT(cList == 0 && IsListEmpty(&m_rgRecoverListHead));
  1919. m_cRecoverList = cList;
  1920. m_pCurrentListHead = NULL;
  1921. return TRUE;
  1922. }
  1923. /*-----------------------------------------------------------------------------
  1924. CWamInfoOutProc::CleanupScheduled
  1925. Call back function for Cleanup.
  1926. Argument:
  1927. pContext: a pointer to COOPWamReqList.(refer to header file for the definition of COOPWamReqList).
  1928. -----------------------------------------------------------------------------*/
  1929. VOID
  1930. CWamInfoOutProc::CleanupScheduled
  1931. (
  1932. VOID *pContext
  1933. )
  1934. {
  1935. CWamInfoOutProc* pWamInfoOutProc = reinterpret_cast<CWamInfoOutProc*>(pContext);
  1936. DBG_ASSERT(pWamInfoOutProc);
  1937. //
  1938. // Call CleanupAll
  1939. //
  1940. pWamInfoOutProc->CleanupAll(TRUE);
  1941. return;
  1942. }
  1943. /*-----------------------------------------------------------------------------
  1944. CWamInfoOutProc::ReInit
  1945. Reinit a Wam.
  1946. Argument:
  1947. NONE.
  1948. -----------------------------------------------------------------------------*/
  1949. HRESULT
  1950. CWamInfoOutProc::ReInitWam
  1951. (
  1952. VOID
  1953. )
  1954. {
  1955. HRESULT hr = NOERROR;
  1956. WCHAR wszPackageId[uSizeCLSIDStr];
  1957. ChangeToState(WIS_REPAIR);
  1958. DBG_ASSERT(m_pProcessEntry);
  1959. memcpy(wszPackageId, m_pProcessEntry->QueryPackageId(), sizeof(wszPackageId));
  1960. if (m_pIWam != NULL)
  1961. {
  1962. IWam * pIWam;
  1963. hr = g_GIPAPI.Get(m_dwIWamGipCookie, IID_IWam, (void **) &pIWam);
  1964. if(SUCCEEDED(hr))
  1965. {
  1966. pIWam->UninitWam();
  1967. pIWam->Release();
  1968. }
  1969. hr = g_GIPAPI.Revoke(m_dwIWamGipCookie);
  1970. // balance the AddRef in CWamInfo::Init.
  1971. m_pIWam->Release();
  1972. m_pIWam = NULL;
  1973. }
  1974. // If we got a handle for an Out Of Proc Wam, release it
  1975. if( m_pProcessEntry != NULL)
  1976. {
  1977. g_pWamDictator->m_PTable.RemoveWamInfoFromProcessTable(this);
  1978. m_pProcessEntry = NULL;
  1979. }
  1980. //
  1981. // This Dereference() balanced the AddRef in the old WAM Init time.
  1982. //
  1983. DBGPRINTF((
  1984. DBG_CONTEXT
  1985. , "Dereference CWamInfoOutProc(%08x)\n"
  1986. , this
  1987. ));
  1988. Dereference();
  1989. // Ok. We make a new Wam.
  1990. hr = CWamInfo::Init((WCHAR*)wszPackageId, g_pWamDictator->QueryInetInfoPid());
  1991. if (SUCCEEDED(hr))
  1992. {
  1993. DBGPRINTF((DBG_CONTEXT, "ReInited a new Wam. \n"));
  1994. if (m_fJobEnabled)
  1995. {
  1996. DBG_ASSERT(m_pwsiInstance != NULL);
  1997. m_pwsiInstance->AddProcessToJob(m_pProcessEntry->QueryProcessHandle(), TRUE);
  1998. }
  1999. if (SUCCEEDED(hr))
  2000. {
  2001. ChangeToState(WIS_RUNNING);
  2002. }
  2003. }
  2004. // if shtudown process has been started, changed state to STOP_PENDING.
  2005. if (m_fShuttingDown)
  2006. {
  2007. ChangeToState(WIS_SHUTDOWN);
  2008. }
  2009. return hr;
  2010. }
  2011. VOID
  2012. CWamInfoOutProc::NotifyGetInfoForName
  2013. (
  2014. IN LPCSTR pszServerVariable
  2015. )
  2016. {
  2017. DBG_ASSERT( pszServerVariable );
  2018. if( pszServerVariable && *pszServerVariable != '\0' )
  2019. {
  2020. const SV_CACHE_MAP & refSVMap = g_pWamDictator->QueryServerVariableMap();
  2021. DWORD dwOrdinal;
  2022. if( refSVMap.FindOrdinal( pszServerVariable,
  2023. // Could get len on hash
  2024. strlen( pszServerVariable ),
  2025. &dwOrdinal
  2026. ) )
  2027. {
  2028. m_svCache.SetCacheIt( dwOrdinal );
  2029. }
  2030. }
  2031. }
  2032. HRESULT
  2033. CWamInfoOutProc::DoProcessRequestCall
  2034. (
  2035. IN IWam * pIWam,
  2036. IN WAM_REQUEST * pWamRequest,
  2037. OUT BOOL * pfHandled
  2038. )
  2039. {
  2040. DBG_ASSERT(pfHandled);
  2041. DBG_ASSERT(pWamRequest);
  2042. DBG_ASSERT(pIWam);
  2043. //
  2044. // Using _alloca to get the core state data is "probably"
  2045. // perfectly safe here. Since this call is heading oop the stack
  2046. // is going to bottom out fairly soon.
  2047. //
  2048. // But the core state, in the case of a post or put could be quite
  2049. // large. and there is currently no bound on the amount of data that
  2050. // could be part of the server variable cache. So we'll use
  2051. // dwMaxStackAlloc as a threshold for using alloca
  2052. //
  2053. const DWORD dwMaxStackAlloc = (1024 * 4) - sizeof(DWORD);
  2054. BOOL fHeapAllocCore = FALSE;
  2055. BOOL fHeapAllocServerVars = FALSE;
  2056. HRESULT hr = NOERROR;
  2057. DWORD cbWrcStrings;
  2058. DWORD cbServerVars;
  2059. OOP_CORE_STATE oopCoreState;
  2060. ZeroMemory( &oopCoreState, sizeof(OOP_CORE_STATE) );
  2061. // Copy the current cache list
  2062. SV_CACHE_LIST svCache( m_svCache );
  2063. SV_CACHE_LIST::BUFFER_ITEM svCacheItems[SVID_COUNT];
  2064. DWORD cCachedServerVariables = SVID_COUNT;
  2065. svCache.GetBufferItems( svCacheItems, &cCachedServerVariables );
  2066. // Get the core state
  2067. WAM_REQ_CORE_FIXED wrc;
  2068. oopCoreState.cbFixedCore = sizeof( WAM_REQ_CORE_FIXED );
  2069. oopCoreState.pbFixedCore = (LPBYTE)&wrc;
  2070. cbWrcStrings = pWamRequest->CbWrcStrings( m_fInProcess );
  2071. oopCoreState.cbCoreState = WRC_CB_FIXED_ARRAYS + cbWrcStrings;
  2072. if( oopCoreState.cbCoreState > dwMaxStackAlloc )
  2073. {
  2074. fHeapAllocCore = TRUE;
  2075. oopCoreState.pbCoreState =
  2076. (LPBYTE)LocalAlloc( LPTR, oopCoreState.cbCoreState );
  2077. if( oopCoreState.pbCoreState == NULL )
  2078. {
  2079. hr = E_OUTOFMEMORY;
  2080. }
  2081. }
  2082. else
  2083. {
  2084. oopCoreState.pbCoreState =
  2085. (LPBYTE)_alloca( oopCoreState.cbCoreState );
  2086. }
  2087. if( SUCCEEDED(hr) )
  2088. {
  2089. DBG_ASSERT( oopCoreState.pbCoreState );
  2090. //
  2091. // Load the core state. This will fail if the oop application
  2092. // has died. In this case, the easiest thing to do is to make
  2093. // the call to IWam::Process request with a NULL core state
  2094. // and let the failure get handled back by the normal code path
  2095. //
  2096. hr = pWamRequest->GetCoreState( oopCoreState.cbCoreState,
  2097. oopCoreState.pbCoreState,
  2098. oopCoreState.cbFixedCore,
  2099. oopCoreState.pbFixedCore
  2100. );
  2101. }
  2102. // Get the server variable cache data
  2103. if( SUCCEEDED(hr) )
  2104. {
  2105. // Temporarily use a separate buffer for the server variable values
  2106. cbServerVars = pWamRequest->CbCachedSVStrings(
  2107. svCacheItems,
  2108. cCachedServerVariables
  2109. );
  2110. if( cbServerVars )
  2111. {
  2112. DBG_ASSERT( cCachedServerVariables > 0 );
  2113. // Temporarily use a separate buffer
  2114. oopCoreState.cbServerVarData = cbServerVars;
  2115. if( cbServerVars > dwMaxStackAlloc )
  2116. {
  2117. fHeapAllocServerVars = TRUE;
  2118. oopCoreState.pbServerVarData =
  2119. (LPBYTE)LocalAlloc( LPTR, cbServerVars );
  2120. if( oopCoreState.pbServerVarData == NULL )
  2121. {
  2122. hr = E_OUTOFMEMORY;
  2123. }
  2124. }
  2125. else
  2126. {
  2127. oopCoreState.pbServerVarData =
  2128. (LPBYTE)_alloca( cbServerVars );
  2129. }
  2130. if( SUCCEEDED(hr) )
  2131. {
  2132. DBG_ASSERT( oopCoreState.pbServerVarData );
  2133. hr = pWamRequest->GetCachedSVStrings(
  2134. oopCoreState.pbServerVarData,
  2135. oopCoreState.cbServerVarData,
  2136. svCacheItems,
  2137. cCachedServerVariables
  2138. );
  2139. if( SUCCEEDED(hr) )
  2140. {
  2141. oopCoreState.pbServerVarCache = (LPBYTE)svCacheItems;
  2142. oopCoreState.cbServerVars =
  2143. cCachedServerVariables *
  2144. sizeof(SV_CACHE_LIST::BUFFER_ITEM);
  2145. }
  2146. }
  2147. }
  2148. }
  2149. OOP_CORE_STATE * pOopCoreState = (SUCCEEDED(hr)) ? &oopCoreState :
  2150. NULL;
  2151. // The last thing we do...
  2152. hr = pIWam->ProcessRequest( pWamRequest,
  2153. cbWrcStrings,
  2154. pOopCoreState,
  2155. pfHandled
  2156. );
  2157. if( fHeapAllocCore && oopCoreState.pbCoreState )
  2158. {
  2159. LocalFree( oopCoreState.pbCoreState );
  2160. }
  2161. if( fHeapAllocServerVars && oopCoreState.pbServerVarData )
  2162. {
  2163. LocalFree( oopCoreState.pbServerVarData );
  2164. }
  2165. return hr;
  2166. }
  2167. HRESULT CWamInfoOutProc::GetStatistics
  2168. (
  2169. DWORD Level,
  2170. LPWAM_STATISTICS_INFO pWamStatsInfo
  2171. )
  2172. {
  2173. HRESULT hr = S_OK;
  2174. IWam* pIWam = NULL;
  2175. DWORD dwVersion;
  2176. if (!FExceedCrashLimit())
  2177. {
  2178. pIWam = m_pIWam;
  2179. }
  2180. if (NULL == pIWam)
  2181. {
  2182. // We reach this state only if we exceed the crash limit.
  2183. DBG_ASSERT(FExceedCrashLimit());
  2184. hr = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED_DNE);
  2185. }
  2186. else
  2187. {
  2188. hr = pIWam->GetStatistics(0, pWamStatsInfo);
  2189. // 1> If the OOP call failed during the call, RPC returns RPC_S_CALL_FAILED.
  2190. // ie. OOP process crashed during the call
  2191. // 2> If OOP process crashed before the call, RPC returns RPC_S_CALL_FAILED_DNE.
  2192. //
  2193. // Subsequest requests to the crashed OOP Process will get RPC_S_SERVER_UNAVAILABLE.
  2194. //
  2195. if (RPC_S_CALL_FAILED == WIN32_FROM_HRESULT(hr) ||
  2196. RPC_S_CALL_FAILED_DNE == WIN32_FROM_HRESULT(hr) ||
  2197. RPC_S_SERVER_UNAVAILABLE == WIN32_FROM_HRESULT(hr))
  2198. {
  2199. const BOOL F_INRECOVERY = TRUE;
  2200. const BOOL F_HEALTHY = FALSE;
  2201. if (F_INRECOVERY == (BOOL)g_pfnInterlockedCompareExchange(
  2202. (LONG *)&m_fInRepair,
  2203. (LONG)F_INRECOVERY,
  2204. (LONG)F_HEALTHY))
  2205. {
  2206. // Other thread already doing the recovery job.
  2207. DBGPRINTF((DBG_CONTEXT, "Recovery mode: Other thread is doing recovery job.\n"));
  2208. hr = NOERROR;
  2209. }
  2210. //
  2211. // Try to Repair(call repair function). But other thread might already working on the repairing
  2212. // Therefore, this thread will return immediately if repairing is in-place.
  2213. //
  2214. Repair();
  2215. if (F_HEALTHY == (BOOL)g_pfnInterlockedCompareExchange(
  2216. (LONG *)&m_fInRepair,
  2217. (LONG)F_HEALTHY,
  2218. (LONG)F_INRECOVERY))
  2219. {
  2220. // Other thread already doing the recovery job.
  2221. DBG_ASSERT(FALSE);
  2222. }
  2223. }
  2224. }
  2225. return hr;
  2226. }
  2227. /************************ End of File ***********************/