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.

1151 lines
27 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. wprecycler.cxx
  5. Abstract:
  6. Implementation of WP_RECYCLER. Object handles worker process recycling
  7. - Memory based recycling
  8. - Schedule based recycling
  9. - Elapsed Time based recycling
  10. - Processed Request Count based recycling
  11. Dependencies:
  12. g_pwpContext is used by WP_RECYCLER to be able to send messages
  13. Author:
  14. Jaroslav Dunajsky (JaroslaD) 07-Dec-2000
  15. Environment:
  16. Win32 - User Mode
  17. Project:
  18. W3DT.DLL
  19. --*/
  20. #include "precomp.hxx"
  21. #include "wprecycler.hxx"
  22. #define ONE_DAY_IN_MILLISECONDS (1000 * 60 * 60 * 24)
  23. //
  24. // Static variables
  25. //
  26. CRITICAL_SECTION WP_RECYCLER::sm_CritSec;
  27. //
  28. // Static variables for Memory based recycling
  29. //
  30. HANDLE WP_RECYCLER::sm_hTimerForMemoryBased = NULL;
  31. BOOL WP_RECYCLER::sm_fIsStartedMemoryBased = FALSE;
  32. SIZE_T WP_RECYCLER::sm_MaxValueForVirtualMemoryBasedInKB = 0;
  33. SIZE_T WP_RECYCLER::sm_MaxValueForPrivateBytesBasedInKB = 0;
  34. DWORD WP_RECYCLER::sm_CurrentPID = NULL;
  35. BUFFER WP_RECYCLER::sm_buffSystemProcessInfo = NULL;
  36. //
  37. // arbitrary value for default SystemProcessInfoBuffer
  38. // it will be resized when needed
  39. //
  40. DWORD WP_RECYCLER::sm_cbSystemProcessInfo =
  41. sizeof(SYSTEM_PROCESS_INFORMATION) * 50;
  42. //
  43. // Static variables for Time based recycling
  44. //
  45. HANDLE WP_RECYCLER::sm_hTimerForTimeBased = NULL;
  46. BOOL WP_RECYCLER::sm_fIsStartedTimeBased = FALSE;
  47. //
  48. // Static variables for Schedule based recycling
  49. //
  50. HANDLE WP_RECYCLER::sm_hTimerQueueForScheduleBased = NULL;
  51. BOOL WP_RECYCLER::sm_fIsStartedScheduleBased = FALSE;
  52. //
  53. // Static variables for Request based recycling
  54. //
  55. BOOL WP_RECYCLER::sm_fIsStartedRequestBased = FALSE;
  56. DWORD WP_RECYCLER::sm_dwMaxValueForRequestBased = 0;
  57. LONG WP_RECYCLER::sm_RecyclingMsgSent = 0;
  58. BOOL WP_RECYCLER::sm_fCritSecInit = FALSE;
  59. //
  60. // Static methods for Schedule based recycling
  61. //
  62. //static
  63. HRESULT
  64. WP_RECYCLER::StartScheduleBased(
  65. IN const WCHAR * pwszScheduleTimes
  66. )
  67. /*++
  68. Routine Description:
  69. Start schedule based recycling
  70. Arguments:
  71. pwszScheduleTimes - MULTISZ array of time information
  72. <time>\0<time>\0\0
  73. time is of military format hh:mm
  74. (hh>=0 && hh<=23)
  75. (mm>=0 && hh<=59)
  76. Return Value:
  77. HRESULT
  78. --*/
  79. {
  80. HRESULT hr = E_FAIL;
  81. BOOL fRet = FALSE;
  82. const WCHAR * pwszCurrentChar = pwszScheduleTimes;
  83. HANDLE hTimer;
  84. WORD wHours = 0;
  85. WORD wMinutes = 0;
  86. WORD wDigitCount = 0;
  87. SYSTEMTIME SystemTime;
  88. FILETIME FileTime;
  89. FILETIME CurrentFileTime;
  90. ULARGE_INTEGER largeintCurrentTime;
  91. ULARGE_INTEGER largeintTime;
  92. DWORD dwDueTime = 0;
  93. DBG_ASSERT(TRUE == sm_fCritSecInit);
  94. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  95. IF_DEBUG( WPRECYCLER )
  96. {
  97. DBGPRINTF(( DBG_CONTEXT,
  98. "WP_RECYCLER::StartScheduleBased()\n"));
  99. }
  100. DBG_ASSERT( pwszScheduleTimes != NULL );
  101. //
  102. // If scheduler based recycling has been running already
  103. // terminate it before restarting with new settings
  104. //
  105. if ( WP_RECYCLER::sm_fIsStartedScheduleBased )
  106. {
  107. WP_RECYCLER::TerminateScheduleBased();
  108. }
  109. WP_RECYCLER::sm_hTimerQueueForScheduleBased = CreateTimerQueue();
  110. if ( WP_RECYCLER::sm_hTimerQueueForScheduleBased == NULL )
  111. {
  112. hr = HRESULT_FROM_WIN32( GetLastError() );
  113. goto Failed;
  114. }
  115. //
  116. // Gets current time
  117. //
  118. GetLocalTime( &SystemTime );
  119. SystemTimeToFileTime( &SystemTime,
  120. &CurrentFileTime );
  121. memcpy( &largeintCurrentTime,
  122. &CurrentFileTime,
  123. sizeof( ULARGE_INTEGER ) );
  124. //
  125. // empty string in MULTISZ indicates the end of MULTISZ
  126. //
  127. while ( *pwszCurrentChar != '\0' )
  128. {
  129. //
  130. // Skip white spaces
  131. //
  132. while ( iswspace( (wint_t) *pwszCurrentChar ) )
  133. {
  134. pwszCurrentChar++;
  135. }
  136. //
  137. // Start of the time info
  138. // Expect military format hh:mm
  139. //
  140. //
  141. // Process hours (up to 2 digits is valid)
  142. //
  143. wHours = 0;
  144. wDigitCount = 0;
  145. while ( iswdigit( *pwszCurrentChar ) )
  146. {
  147. wDigitCount++;
  148. wHours = 10 * wHours + (*pwszCurrentChar - '0');
  149. pwszCurrentChar++;
  150. }
  151. if ( wDigitCount > 2 ||
  152. ( wHours > 23 ) )
  153. {
  154. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  155. goto Failed;
  156. }
  157. //
  158. // Hours - minutes separator
  159. // Be liberal - any character that is not a digit or '\0' is OK
  160. //
  161. if ( *pwszCurrentChar == '\0' )
  162. {
  163. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  164. goto Failed;
  165. }
  166. pwszCurrentChar++;
  167. //
  168. // Process minutes (must be exactly 2 digits)
  169. //
  170. wMinutes = 0;
  171. wDigitCount = 0;
  172. while ( iswdigit( (wint_t) *pwszCurrentChar ) )
  173. {
  174. wDigitCount++;
  175. wMinutes = 10 * wMinutes + (*pwszCurrentChar - '0');
  176. pwszCurrentChar++;
  177. }
  178. if ( ( wDigitCount != 2 ) ||
  179. ( wMinutes > 59 ) )
  180. {
  181. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  182. goto Failed;
  183. }
  184. //
  185. // Skip white spaces
  186. //
  187. while ( iswspace( (wint_t)*pwszCurrentChar ) )
  188. {
  189. pwszCurrentChar++;
  190. }
  191. //
  192. // Check for terminating zero
  193. //
  194. if ( *pwszCurrentChar != '\0' )
  195. {
  196. //
  197. // Extra characters in the time string
  198. //
  199. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  200. goto Failed;
  201. }
  202. pwszCurrentChar++;
  203. //
  204. // Convert Hours and Minutes info
  205. //
  206. SystemTime.wHour = wHours;
  207. SystemTime.wMinute = wMinutes;
  208. SystemTime.wSecond = 0;
  209. SystemTime.wMilliseconds = 0;
  210. SystemTimeToFileTime( &SystemTime,
  211. &FileTime );
  212. memcpy( &largeintTime,
  213. &FileTime,
  214. sizeof(ULARGE_INTEGER) );
  215. //
  216. // Issue 12/21/2000 jaroslad:
  217. // This method of setting absolute time with CreateTimerQueueTimer
  218. // is bad since instead of setting absolute time the relative time is
  219. // calculated and used for timer.
  220. // This approach fails badly if someone changes machine
  221. // time. Other Api that enables setting abolute time must be used for proper
  222. // implementation
  223. //
  224. // Get Due Time in milliseconds
  225. //
  226. dwDueTime = static_cast<DWORD>(
  227. ( largeintTime.QuadPart - largeintCurrentTime.QuadPart )/ 10000);
  228. if ( largeintTime.QuadPart < largeintCurrentTime.QuadPart)
  229. {
  230. dwDueTime = ONE_DAY_IN_MILLISECONDS - static_cast<DWORD>(
  231. ( largeintCurrentTime.QuadPart - largeintTime.QuadPart )/ 10000);
  232. }
  233. else
  234. {
  235. dwDueTime = static_cast<DWORD>(
  236. ( largeintTime.QuadPart - largeintCurrentTime.QuadPart )/ 10000);
  237. }
  238. if ( dwDueTime == 0 )
  239. {
  240. //
  241. // this event is to be scheduled for the next day
  242. // one day has 1000 * 60 * 60 * 24 of (100-nanosecond intervals)
  243. //
  244. dwDueTime += ONE_DAY_IN_MILLISECONDS;
  245. }
  246. //
  247. // Schedule event for specified time, repeating once a day
  248. //
  249. IF_DEBUG( WPRECYCLER )
  250. {
  251. DBGPRINTF(( DBG_CONTEXT,
  252. "Schedule recycling for %d:%d (in %d milliseconds)\n",
  253. (int) wHours,
  254. (int) wMinutes,
  255. dwDueTime));
  256. }
  257. fRet = CreateTimerQueueTimer(
  258. &hTimer,
  259. WP_RECYCLER::sm_hTimerQueueForScheduleBased,
  260. WP_RECYCLER::TimerCallbackForScheduleBased,
  261. NULL,
  262. dwDueTime,
  263. // repeat daily (interval in milliseconds)
  264. ONE_DAY_IN_MILLISECONDS,
  265. WT_EXECUTELONGFUNCTION );
  266. if ( !fRet )
  267. {
  268. hr = HRESULT_FROM_WIN32( GetLastError() );
  269. goto Failed;
  270. }
  271. //
  272. // hTimer will not be stored
  273. // sm_hTimerQueueForScheduleBased is going to be used for cleanup
  274. // DeleteTimerQueueEx() should be able to correctly delete all timers
  275. // in the queue
  276. //
  277. }
  278. WP_RECYCLER::sm_fIsStartedScheduleBased = TRUE;
  279. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  280. return S_OK;
  281. Failed:
  282. WP_RECYCLER::TerminateScheduleBased();
  283. DBG_ASSERT( FAILED( hr ) );
  284. IF_DEBUG( WPRECYCLER )
  285. {
  286. DBGPRINTF(( DBG_CONTEXT,
  287. "WP_RECYCLER::StartScheduleBased() failed with error hr=0x%x\n",
  288. hr ));
  289. }
  290. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  291. return hr;
  292. }
  293. //static
  294. VOID
  295. WP_RECYCLER::TerminateScheduleBased(
  296. VOID
  297. )
  298. /*++
  299. Routine Description:
  300. Stops schedule based recycling
  301. Performs cleanup
  302. Note:
  303. It is safe to call this method for cleanup if Start failed
  304. Arguments:
  305. NONE
  306. Return Value:
  307. VOID
  308. --*/
  309. {
  310. DBG_ASSERT(TRUE == sm_fCritSecInit);
  311. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  312. if( WP_RECYCLER::sm_hTimerQueueForScheduleBased != NULL )
  313. {
  314. if ( !DeleteTimerQueueEx(
  315. WP_RECYCLER::sm_hTimerQueueForScheduleBased,
  316. INVALID_HANDLE_VALUE /* wait for callbacks to complete */
  317. ) )
  318. {
  319. DBGPRINTF(( DBG_CONTEXT,
  320. "failed to call DeleteTimerQueueEx(): hr=0x%x\n",
  321. HRESULT_FROM_WIN32(GetLastError()) ));
  322. }
  323. WP_RECYCLER::sm_hTimerQueueForScheduleBased = NULL;
  324. }
  325. WP_RECYCLER::sm_fIsStartedScheduleBased = FALSE;
  326. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  327. return;
  328. }
  329. //static
  330. VOID
  331. WINAPI
  332. WP_RECYCLER::TimerCallbackForScheduleBased(
  333. PVOID,
  334. BOOLEAN
  335. )
  336. /*++
  337. Routine Description:
  338. Timer callback for Schedule based recycling
  339. It is passed to CreateTimerQueueTimer()
  340. Routine will inform WAS that process is ready to be recycled
  341. because scheduled time has been reached
  342. Arguments:
  343. see the description of WAITORTIMERCALLBACK type in MSDN
  344. Return Value:
  345. none
  346. --*/
  347. {
  348. DBG_ASSERT( WP_RECYCLER::sm_fIsStartedScheduleBased );
  349. IF_DEBUG( WPRECYCLER )
  350. {
  351. DBGPRINTF(( DBG_CONTEXT,
  352. "WP_RECYCLER::TimerCallbackForScheduleBased()"
  353. " - tell WAS to recycle\n" ));
  354. }
  355. //
  356. // Indicate to WAS that we are ready for recycling
  357. //
  358. WP_RECYCLER::SendRecyclingMsg( IPM_WP_RESTART_SCHEDULED_TIME_REACHED );
  359. }
  360. //
  361. // Static methods for Memory based recycling
  362. //
  363. //static
  364. HRESULT
  365. WP_RECYCLER::StartMemoryBased(
  366. IN DWORD dwMaxVirtualMemoryUsageInKB,
  367. IN DWORD dwMaxPrivateBytesUsageInKB
  368. )
  369. /*++
  370. Routine Description:
  371. Start virtual memory usage based recycling.
  372. Arguments:
  373. dwMaxVirtualMemoryUsageInKB - If usage of virtual memory reaches this value
  374. worker process is ready for recycling
  375. Note:
  376. VM usage will be checked periodically. See the value of internal constant
  377. CHECK_MEMORY_TIME_PERIOD
  378. Return Value:
  379. HRESULT
  380. --*/
  381. {
  382. HRESULT hr = E_FAIL;
  383. BOOL fRet = FALSE;
  384. DBG_ASSERT(TRUE == sm_fCritSecInit);
  385. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  386. IF_DEBUG( WPRECYCLER )
  387. {
  388. DBGPRINTF(( DBG_CONTEXT,
  389. "WP_RECYCLER::StartMemoryBased(VM:%d kB, Private Bytes:%d kB)\n",
  390. dwMaxVirtualMemoryUsageInKB,
  391. dwMaxPrivateBytesUsageInKB));
  392. }
  393. //
  394. // If time based recycling has been running already
  395. // terminate it before restarting with new settings
  396. //
  397. if ( WP_RECYCLER::sm_fIsStartedMemoryBased == TRUE )
  398. {
  399. WP_RECYCLER::TerminateMemoryBased();
  400. }
  401. if ( dwMaxVirtualMemoryUsageInKB == 0 && dwMaxPrivateBytesUsageInKB == 0 )
  402. {
  403. //
  404. // 0 means not to run memory recycling
  405. //
  406. hr = S_OK;
  407. goto succeeded;
  408. }
  409. fRet = CreateTimerQueueTimer( &WP_RECYCLER::sm_hTimerForMemoryBased,
  410. NULL,
  411. WP_RECYCLER::TimerCallbackForMemoryBased,
  412. NULL,
  413. CHECK_MEMORY_TIME_PERIOD,
  414. CHECK_MEMORY_TIME_PERIOD,
  415. WT_EXECUTELONGFUNCTION );
  416. if ( !fRet )
  417. {
  418. WP_RECYCLER::sm_hTimerForMemoryBased = NULL;
  419. hr = HRESULT_FROM_WIN32( GetLastError() );
  420. goto failed;
  421. }
  422. //
  423. // Get current process handle
  424. // It will be used for NtQueryInformationProcess()
  425. // in the timer callback
  426. // there is no error to check for and handle doesn't need to be closed
  427. // on cleanup
  428. //
  429. sm_CurrentPID = GetCurrentProcessId();
  430. sm_MaxValueForVirtualMemoryBasedInKB = dwMaxVirtualMemoryUsageInKB;
  431. sm_MaxValueForPrivateBytesBasedInKB = dwMaxPrivateBytesUsageInKB;
  432. WP_RECYCLER::sm_fIsStartedMemoryBased = TRUE;
  433. succeeded:
  434. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  435. return S_OK;
  436. failed:
  437. DBG_ASSERT( FAILED( hr ) );
  438. WP_RECYCLER::TerminateMemoryBased();
  439. IF_DEBUG( WPRECYCLER )
  440. {
  441. DBGPRINTF(( DBG_CONTEXT,
  442. "WP_RECYCLER::StartMemoryBased() failed with error hr=0x%x\n",
  443. hr ));
  444. }
  445. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  446. return hr;
  447. }
  448. //static
  449. VOID
  450. WP_RECYCLER::TerminateMemoryBased(
  451. VOID
  452. )
  453. /*++
  454. Routine Description:
  455. Stops virtual memory usage based recycling
  456. Performs cleanup
  457. Note:
  458. It is safe to call this method for cleanup if Start failed
  459. Arguments:
  460. NONE
  461. Return Value:
  462. VOID
  463. --*/
  464. {
  465. DBG_ASSERT(TRUE == sm_fCritSecInit);
  466. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  467. if ( WP_RECYCLER::sm_hTimerForMemoryBased != NULL )
  468. {
  469. if ( !DeleteTimerQueueTimer(
  470. NULL,
  471. WP_RECYCLER::sm_hTimerForMemoryBased,
  472. INVALID_HANDLE_VALUE /* wait for callbacks to complete */
  473. ) )
  474. {
  475. DBGPRINTF(( DBG_CONTEXT,
  476. "failed to call DeleteTimerQueueTimer(): hr=0x%x\n",
  477. HRESULT_FROM_WIN32(GetLastError()) ));
  478. }
  479. WP_RECYCLER::sm_hTimerForMemoryBased = NULL;
  480. }
  481. sm_fIsStartedMemoryBased = FALSE;
  482. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  483. return;
  484. }
  485. //static
  486. VOID
  487. WINAPI
  488. WP_RECYCLER::TimerCallbackForMemoryBased(
  489. PVOID,
  490. BOOLEAN
  491. )
  492. /*++
  493. Routine Description:
  494. Timer callback for Elapsed time based recycling
  495. This Callback is passed to CreateTimerQueueTimer()
  496. Virtual memory and Private Bytes usage will be checked and
  497. if limit has been reached then routine will inform WAS
  498. that process is ready to be recycled
  499. Arguments:
  500. see description of WAITORTIMERCALLBACK type in MSDN
  501. Return Value:
  502. none
  503. --*/
  504. {
  505. NTSTATUS Status = 0;
  506. PSYSTEM_PROCESS_INFORMATION pProcessInfo = NULL;
  507. DBG_ASSERT( WP_RECYCLER::sm_fIsStartedMemoryBased );
  508. //
  509. // Keep trying larger buffers until we get all the information
  510. // Note: There seems to be no easier way to get PrivateBytes counter
  511. // for the process other then to enumerate all the processes
  512. // This may cause performance problem with large number of worker processes
  513. // that are self monitoring on memory usage
  514. //
  515. for(;;)
  516. {
  517. if ( !sm_buffSystemProcessInfo.Resize( sm_cbSystemProcessInfo ) )
  518. {
  519. // We failed to reallocate then just silently exit
  520. // CODEWORK: This may cause worker process to never recycle
  521. // if memory is too low to handle memory usage check
  522. //
  523. return;
  524. }
  525. Status = NtQuerySystemInformation(
  526. SystemProcessInformation,
  527. sm_buffSystemProcessInfo.QueryPtr(),
  528. sm_buffSystemProcessInfo.QuerySize(),
  529. NULL
  530. );
  531. if( Status != STATUS_INFO_LENGTH_MISMATCH ) break;
  532. sm_cbSystemProcessInfo *= 2;
  533. }
  534. if( Status == STATUS_SUCCESS )
  535. {
  536. DWORD NextOffset = 0;
  537. //
  538. // enumerate info about all processes until you find
  539. // the current process (identified by PID)
  540. //
  541. for(;;)
  542. {
  543. //
  544. // get process info from buffer
  545. //
  546. pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
  547. (((PBYTE) sm_buffSystemProcessInfo.QueryPtr()) + NextOffset );
  548. NextOffset += pProcessInfo->NextEntryOffset;
  549. //
  550. // Compare Pid to see if we found the current process
  551. //
  552. if ( HandleToULong( pProcessInfo->UniqueProcessId ) == sm_CurrentPID )
  553. {
  554. break;
  555. }
  556. if ( pProcessInfo->NextEntryOffset == 0 )
  557. {
  558. //
  559. // Well, we should never get here because the current process
  560. // must be listed but to eliminate potential problems
  561. // since we are using private NT API let's assume not found error
  562. //
  563. Status = STATUS_NOT_FOUND;
  564. break;
  565. }
  566. }
  567. }
  568. if ( ! NT_SUCCESS ( Status ) )
  569. {
  570. IF_DEBUG( WPRECYCLER )
  571. {
  572. DBGPRINTF(( DBG_CONTEXT,
  573. "NtQueryInformationProcess failed with Status: %d\n",
  574. Status ));
  575. }
  576. return;
  577. }
  578. //
  579. // Check virtual bytes
  580. //
  581. if ( sm_MaxValueForVirtualMemoryBasedInKB != 0 &&
  582. pProcessInfo->VirtualSize/1024 >= sm_MaxValueForVirtualMemoryBasedInKB )
  583. {
  584. IF_DEBUG( WPRECYCLER )
  585. {
  586. DBGPRINTF(( DBG_CONTEXT,
  587. "WP_RECYCLER::TimerCallbackForMemoryBased()"
  588. " - current VM:%ld kB, configured max VM:%ld kB"
  589. " - tell WAS to recycle\n",
  590. pProcessInfo->VirtualSize/1024 ,
  591. sm_MaxValueForVirtualMemoryBasedInKB ));
  592. }
  593. //
  594. // we reached Virtual Memory Usage limit
  595. //
  596. WP_RECYCLER::SendRecyclingMsg( IPM_WP_RESTART_VIRTUAL_MEMORY_LIMIT_REACHED );
  597. }
  598. //
  599. // Check private bytes
  600. //
  601. if ( sm_MaxValueForPrivateBytesBasedInKB != 0 &&
  602. pProcessInfo->PrivatePageCount/1024 >= sm_MaxValueForPrivateBytesBasedInKB )
  603. {
  604. IF_DEBUG( WPRECYCLER )
  605. {
  606. DBGPRINTF(( DBG_CONTEXT,
  607. "WP_RECYCLER::TimerCallbackForMemoryBased()"
  608. " - current Private Bytes:%ld kB, configured max Private Bytes:%ld kB"
  609. " - tell WAS to recycle\n",
  610. pProcessInfo->PrivatePageCount/1024 ,
  611. sm_MaxValueForPrivateBytesBasedInKB ));
  612. }
  613. //
  614. // we reached Private Memory Usage limit
  615. //
  616. WP_RECYCLER::SendRecyclingMsg( IPM_WP_RESTART_PRIVATE_BYTES_LIMIT_REACHED );
  617. }
  618. }
  619. //
  620. // Static methods for Time based recycling
  621. //
  622. //static
  623. HRESULT
  624. WP_RECYCLER::StartTimeBased(
  625. IN DWORD dwPeriodicRestartTimeInMinutes
  626. )
  627. /*++
  628. Routine Description:
  629. Start elapsed time based recycling
  630. Arguments:
  631. dwPeriodicRestartTimeInMinutes - how often to restart (in minutes)
  632. Return Value:
  633. HRESULT
  634. --*/
  635. {
  636. HRESULT hr = E_FAIL;
  637. BOOL fRet = FALSE;
  638. DBG_ASSERT(TRUE == sm_fCritSecInit);
  639. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  640. IF_DEBUG( WPRECYCLER )
  641. {
  642. DBGPRINTF(( DBG_CONTEXT,
  643. "WP_RECYCLER::StartTimeBased(%d min)\n" ,
  644. dwPeriodicRestartTimeInMinutes ));
  645. }
  646. //
  647. // If time based recycling has been running already
  648. // terminate it before restarting with new settings
  649. //
  650. if ( WP_RECYCLER::sm_fIsStartedTimeBased == TRUE )
  651. {
  652. WP_RECYCLER::TerminateTimeBased();
  653. }
  654. if ( dwPeriodicRestartTimeInMinutes == 0 )
  655. {
  656. //
  657. // 0 means not to run time based recycling
  658. //
  659. hr = S_OK;
  660. goto succeeded;
  661. }
  662. fRet = CreateTimerQueueTimer( &WP_RECYCLER::sm_hTimerForTimeBased,
  663. NULL,
  664. WP_RECYCLER::TimerCallbackForTimeBased,
  665. NULL,
  666. dwPeriodicRestartTimeInMinutes * 60000,
  667. // convert to msec
  668. dwPeriodicRestartTimeInMinutes * 60000,
  669. // convert to msec
  670. WT_EXECUTELONGFUNCTION );
  671. if ( !fRet )
  672. {
  673. WP_RECYCLER::sm_hTimerForTimeBased = NULL;
  674. hr = HRESULT_FROM_WIN32( GetLastError() );
  675. goto failed;
  676. }
  677. WP_RECYCLER::sm_fIsStartedTimeBased = TRUE;
  678. succeeded:
  679. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  680. return S_OK;
  681. failed:
  682. DBG_ASSERT( FAILED( hr ) );
  683. WP_RECYCLER::TerminateTimeBased();
  684. IF_DEBUG( WPRECYCLER )
  685. {
  686. DBGPRINTF(( DBG_CONTEXT,
  687. "WP_RECYCLER::StartTimeBased() failed with error hr=0x%x\n",
  688. hr));
  689. }
  690. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  691. return hr;
  692. }
  693. //static
  694. VOID
  695. WP_RECYCLER::TerminateTimeBased(
  696. VOID
  697. )
  698. /*++
  699. Routine Description:
  700. Stops elapsed time based recycling
  701. Performs cleanup
  702. Note:
  703. It is safe to call this method for cleanup if Start failed
  704. Arguments:
  705. NONE
  706. Return Value:
  707. HRESULT
  708. --*/
  709. {
  710. DBG_ASSERT(TRUE == sm_fCritSecInit);
  711. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  712. if ( WP_RECYCLER::sm_hTimerForTimeBased != NULL )
  713. {
  714. if ( !DeleteTimerQueueTimer(
  715. NULL,
  716. WP_RECYCLER::sm_hTimerForTimeBased,
  717. INVALID_HANDLE_VALUE /* wait for callbacks to complete */
  718. ) )
  719. {
  720. DBGPRINTF(( DBG_CONTEXT,
  721. "failed to call DeleteTimerQueueTimer(): hr=0x%x\n",
  722. HRESULT_FROM_WIN32(GetLastError()) ));
  723. }
  724. WP_RECYCLER::sm_hTimerForTimeBased = NULL;
  725. }
  726. WP_RECYCLER::sm_fIsStartedTimeBased = FALSE;
  727. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  728. return;
  729. }
  730. //static
  731. VOID
  732. WINAPI
  733. WP_RECYCLER::TimerCallbackForTimeBased(
  734. PVOID,
  735. BOOLEAN
  736. )
  737. /*++
  738. Routine Description:
  739. Timer callback for Elapsed time based recycling
  740. This Callback is passed to CreateTimerQueueTimer()
  741. Routine will inform WAS that process is ready to be recycled
  742. because required elapsed time has been reached
  743. Arguments:
  744. see description of WAITORTIMERCALLBACK type in MSDN
  745. Return Value:
  746. none
  747. --*/
  748. {
  749. DBG_ASSERT( WP_RECYCLER::sm_fIsStartedTimeBased );
  750. IF_DEBUG( WPRECYCLER )
  751. {
  752. DBGPRINTF(( DBG_CONTEXT,
  753. "WP_RECYCLER::TimerCallbackForTimeBased"
  754. " - tell WAS to recycle\n" ));
  755. }
  756. WP_RECYCLER::SendRecyclingMsg( IPM_WP_RESTART_ELAPSED_TIME_REACHED );
  757. }
  758. //
  759. // Static methods for Request based recycling
  760. //
  761. //static
  762. HRESULT
  763. WP_RECYCLER::StartRequestBased(
  764. IN DWORD dwRequests
  765. )
  766. /*++
  767. Routine Description:
  768. Start request based recycling.
  769. Arguments:
  770. dwRequests - If number of requests processed by worker process reaches this value
  771. recycling will be required
  772. Return Value:
  773. HRESULT
  774. --*/
  775. {
  776. HRESULT hr = E_FAIL;
  777. DBG_ASSERT(TRUE == sm_fCritSecInit);
  778. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  779. IF_DEBUG( WPRECYCLER )
  780. {
  781. DBGPRINTF(( DBG_CONTEXT,
  782. "WP_RECYCLER::StartRequestBased(%d kB)\n" ,
  783. dwRequests ));
  784. }
  785. //
  786. // If time based recycling has been running already
  787. // terminate it before restarting with new settings
  788. //
  789. if ( WP_RECYCLER::sm_fIsStartedRequestBased == TRUE )
  790. {
  791. WP_RECYCLER::TerminateRequestBased();
  792. }
  793. if ( dwRequests == 0 )
  794. {
  795. //
  796. // 0 means not to run request based recycling
  797. //
  798. hr = S_OK;
  799. goto succeeded;
  800. }
  801. InterlockedExchange(
  802. reinterpret_cast<LONG *>(&sm_dwMaxValueForRequestBased),
  803. dwRequests );
  804. InterlockedExchange(
  805. reinterpret_cast<LONG *>(&WP_RECYCLER::sm_fIsStartedTimeBased),
  806. TRUE );
  807. hr = S_OK;
  808. succeeded:
  809. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  810. return hr;
  811. }
  812. //static
  813. VOID
  814. WP_RECYCLER::TerminateRequestBased(
  815. VOID
  816. )
  817. /*++
  818. Routine Description:
  819. Stops request based recycling
  820. Performs cleanup
  821. Note:
  822. It is safe to call this method for cleanup if Start failed
  823. Arguments:
  824. NONE
  825. Return Value:
  826. HRESULT
  827. --*/
  828. {
  829. DBG_ASSERT(TRUE == sm_fCritSecInit);
  830. EnterCriticalSection( &WP_RECYCLER::sm_CritSec );
  831. //
  832. // InterlockedExchange is used because Request Based recycling callback
  833. // IsRequestCountLimitReached() is called for each request
  834. // and we don't synchronize it with &WP_RECYCLER::sm_CritSec
  835. //
  836. InterlockedExchange(
  837. reinterpret_cast<LONG *>(&WP_RECYCLER::sm_fIsStartedTimeBased),
  838. FALSE );
  839. LeaveCriticalSection( &WP_RECYCLER::sm_CritSec );
  840. return;
  841. }