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.

3438 lines
87 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. w3jobobj.cxx
  5. Abstract:
  6. This module implements the W3_JOB_OBJECT class
  7. Author:
  8. Michael Thomas (michth) Jan-02-1998
  9. --*/
  10. /*++
  11. Site and Job Object Locking
  12. The locking is rather complex, so here I attempt to describe the
  13. locks and interactions.
  14. W3_SERVER_INSTANCE Locks:
  15. 1) m_tslock, inherited from IIS_SERVER_INSTANCE. Accessed via
  16. the methods LockThisForRead, LockThisForWrite, and UnlockThis. Controls
  17. access to the main members of the class, and to the job object members
  18. m_dwJobResetInterval and m_dwJobIntervalSchedulerCookie.
  19. 2) m_tsJobLock. Accessed via the methods LockJobsForRead, LockJobsForWrite,
  20. and UnlockJobss. Controls access to the job object members, except
  21. m_dwJobResetInterval and m_dwJobIntervalSchedulerCookie.
  22. Scheduler Locks:
  23. 1) For each deferred process set up via ScheduleWorkItem, the following pseudo
  24. lock applies: RemoveWorkItem will not not complete while the scheduled item is
  25. being processed.
  26. W3_JOB_OBJECT Locks:
  27. 1) m_csLock, Accessed via the methods LockThis and UnlockThis. Controls access
  28. to all members.
  29. Other Locks:
  30. 1) The site calls many other components, which may have their own locking.
  31. Interactions:
  32. For the resources used in the W3_SERVER_INSTANCE locks, it is possible to
  33. recursively get read locks, recursively get write locks, or recursively get
  34. get a read lock with a write lock held. Attempting to recursively get a write
  35. lock if a read lock is held will deadlock. Care must be take to prevent this
  36. situation when calling routines which grab a write lock.
  37. The deferred processing routines must not get locks that are held when
  38. RemoveWorkItem is called. This is why we need 2 instance locks, and why
  39. m_dwJobResetInterval and m_dwJobIntervalSchedulerCookie are not controlled
  40. via m_tsJobLock.
  41. JobResetInterval accesses many job object members. It gets m_tsJobLock. It
  42. must not get m_tslock.
  43. All places that call RemoveWorkItem(m_dwJobIntervalSchedulerCookie) must
  44. not hold m_tsJobLock. They should have m_tslock.
  45. QueryandLogJobInfo does not get any lock as m_tsJobLock is held when
  46. RemoveWorkItem(m_dwJobLoggingSchedulerCookie) is called. This is relatively
  47. benign. Routines which call QueryandLogJobInfo which are not running as
  48. deferred routines should LockJobsForRead when QueryAndLogJobInfo is called.
  49. --*/
  50. #include "w3p.hxx"
  51. #include <issched.hxx>
  52. #include <wmrgexp.h>
  53. #include <wamexec.hxx>
  54. //
  55. // Map logging events to strings.
  56. //
  57. static LPCSTR pszarrayJOLE[JOLE_NUM_ELEMENTS] = {
  58. JOLE_SITE_START_STR,
  59. JOLE_SITE_STOP_STR,
  60. JOLE_SITE_PAUSE_STR,
  61. JOLE_PERIODIC_LOG_STR,
  62. JOLE_RESET_INT_START_STR,
  63. JOLE_RESET_INT_STOP_STR,
  64. JOLE_RESET_INT_CHANGE_STR,
  65. JOLE_LOGGING_INT_START_STR,
  66. JOLE_LOGGING_INT_STOP_STR,
  67. JOLE_LOGGING_INT_CHANGE_STR,
  68. JOLE_EVENTLOG_LIMIT_STR,
  69. JOLE_PRIORITY_LIMIT_STR,
  70. JOLE_PROCSTOP_LIMIT_STR,
  71. JOLE_PAUSE_LIMIT_STR,
  72. JOLE_EVENTLOG_LIMIT_RESET_STR,
  73. JOLE_PRIORITY_LIMIT_RESET_STR,
  74. JOLE_PROCSTOP_LIMIT_RESET_STR,
  75. JOLE_PAUSE_LIMIT_RESET_STR,
  76. };
  77. //
  78. // Map Logging Process types to strings.
  79. //
  80. static LPCSTR pszarrayJOPT[JOPT_NUM_ELEMENTS] = {
  81. JOPT_CGI_STR,
  82. JOPT_APP_STR,
  83. JOPT_ALL_STR
  84. };
  85. W3_JOB_OBJECT::W3_JOB_OBJECT(DWORD dwJobCGICPULimit):
  86. m_hJobObject ( NULL ),
  87. m_dwJobCGICPULimit ( dwJobCGICPULimit ),
  88. m_dwError ( ERROR_SUCCESS )
  89. {
  90. ResetCounters(NULL);
  91. INITIALIZE_CRITICAL_SECTION(&m_csLock);
  92. m_hJobObject = CreateJobObject(NULL,
  93. NULL);
  94. if (m_hJobObject == NULL) {
  95. m_dwError = GetLastError();
  96. }
  97. else if ( m_dwJobCGICPULimit != NO_W3_CPU_CGI_LIMIT ) {
  98. SetJobLimit(SLA_PROCESS_CPU_LIMIT, m_dwJobCGICPULimit);
  99. }
  100. IF_DEBUG( JOB_OBJECTS )
  101. {
  102. DBGPRINTF((DBG_CONTEXT,
  103. "[W3_JOB_OBJECT::W3_JOB_OBJECT] \nConstructing job object %p, error = 0x%X\n",
  104. this,
  105. m_dwError));
  106. }
  107. }
  108. W3_JOB_OBJECT::~W3_JOB_OBJECT(
  109. VOID
  110. )
  111. {
  112. LockThis();
  113. if (m_hJobObject != NULL) {
  114. TerminateJobObject(m_hJobObject,
  115. ERROR_SUCCESS);
  116. DBG_REQUIRE(CloseHandle(m_hJobObject));
  117. m_hJobObject = NULL;
  118. }
  119. UnlockThis();
  120. DeleteCriticalSection(&m_csLock);
  121. IF_DEBUG( JOB_OBJECTS )
  122. {
  123. DBGPRINTF((DBG_CONTEXT,
  124. "[W3_JOB_OBJECT::~W3_JOB_OBJECT] \nDestructing job object %p\n",
  125. this,
  126. m_dwError));
  127. }
  128. } // W3_JOB_OBJECT::~W3_JOB_OBJECT
  129. /*++
  130. Routine Description:
  131. Add Job Object to the global completion port for limit checking.
  132. Sets up the job object to post to the completion port.
  133. Arguments:
  134. hCompletionPort The completion port to add the job object to.
  135. pvCompletionKey The completion key.
  136. Return Value:
  137. DWORD - ERROR_SUCCESS
  138. Errors returned by SetInformationJobObject
  139. Notes:
  140. --*/
  141. DWORD
  142. W3_JOB_OBJECT::SetCompletionPort(HANDLE hCompletionPort,
  143. PVOID pvCompletionKey)
  144. {
  145. JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacpPort;
  146. JOBOBJECT_END_OF_JOB_TIME_INFORMATION joeotiTermination;
  147. DWORD dwReturn = ERROR_SUCCESS;
  148. joacpPort.CompletionPort = hCompletionPort;
  149. joacpPort.CompletionKey = pvCompletionKey;
  150. LockThis();
  151. DBG_ASSERT(m_hJobObject != NULL);
  152. if (!SetInformationJobObject(m_hJobObject,
  153. JobObjectAssociateCompletionPortInformation,
  154. &joacpPort,
  155. sizeof(joacpPort))) {
  156. dwReturn = GetLastError();
  157. //
  158. // Log an event
  159. //
  160. DBGPRINTF((DBG_CONTEXT,
  161. "[SetCompletionPort] - SetInformationJobObject failed, error code = 0x%X\n",
  162. GetLastError()));
  163. g_pInetSvc->LogEvent( W3_EVENT_JOB_SET_LIMIT_FAILED,
  164. 0,
  165. NULL,
  166. dwReturn );
  167. }
  168. else {
  169. //
  170. // Need notifications
  171. //
  172. joeotiTermination.EndOfJobTimeAction = JOB_OBJECT_POST_AT_END_OF_JOB;
  173. if (!SetInformationJobObject(m_hJobObject,
  174. JobObjectEndOfJobTimeInformation,
  175. &joeotiTermination,
  176. sizeof(joeotiTermination))) {
  177. dwReturn = GetLastError();
  178. //
  179. // Log an event
  180. //
  181. DBGPRINTF((DBG_CONTEXT,
  182. "[SetCompletionPort] - SetInformationJobObject failed, error code = 0x%X\n",
  183. GetLastError()));
  184. g_pInetSvc->LogEvent( W3_EVENT_JOB_SET_LIMIT_FAILED,
  185. 0,
  186. NULL,
  187. dwReturn );
  188. }
  189. }
  190. UnlockThis();
  191. IF_DEBUG( JOB_OBJECTS )
  192. {
  193. DBGPRINTF((DBG_CONTEXT,
  194. "[W3_JOB_OBJECT::W3_JOB_OBJECT] \nJob object %p assigned to completion port 0x%X error = 0x%X\n",
  195. this,
  196. HandleToUlong(hCompletionPort),
  197. dwReturn));
  198. }
  199. return dwReturn;
  200. }
  201. /*++
  202. Routine Description:
  203. Set new limits for the Job Object.
  204. Arguments:
  205. slaActions The limit to set
  206. dwValue The new limit value. NO_W3_CPU_LIMIT = 0 = Remove Limit.
  207. Return Value:
  208. Notes:
  209. --*/
  210. VOID
  211. W3_JOB_OBJECT::SetJobLimit(SET_LIMIT_ACTION slaAction,
  212. DWORD dwValue,
  213. LONGLONG llJobCPULimit)
  214. {
  215. JOBOBJECT_BASIC_LIMIT_INFORMATION jobliLimits;
  216. LONGLONG llTimeLimit;
  217. LockThis();
  218. //
  219. // Save CGI limit for future use.
  220. //
  221. if (slaAction == SLA_PROCESS_CPU_LIMIT) {
  222. m_dwJobCGICPULimit = dwValue;
  223. }
  224. //
  225. // Get the existing limits.
  226. //
  227. if (!QueryInformationJobObject(m_hJobObject,
  228. JobObjectBasicLimitInformation,
  229. &jobliLimits,
  230. sizeof(jobliLimits),
  231. NULL)) {
  232. //
  233. // Log an event
  234. //
  235. DBGPRINTF((DBG_CONTEXT,
  236. "[SetJobLimit] - QueryInformationJobObject failed, error code = 0x%X\n",
  237. GetLastError()));
  238. g_pInetSvc->LogEvent( W3_EVENT_JOB_QUERY_FAILED,
  239. 0,
  240. NULL,
  241. GetLastError() );
  242. }
  243. else {
  244. //
  245. // Change the limit.
  246. //
  247. switch (slaAction) {
  248. case SLA_PROCESS_CPU_LIMIT:
  249. if (dwValue == NO_W3_CPU_CGI_LIMIT) {
  250. jobliLimits.LimitFlags &= ~JOB_OBJECT_LIMIT_PROCESS_TIME;
  251. }
  252. else {
  253. jobliLimits.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_TIME;
  254. llTimeLimit = (LONGLONG)m_dwJobCGICPULimit * (LONGLONG)SECONDSTO100NANOSECONDS;
  255. jobliLimits.PerProcessUserTimeLimit.LowPart = (DWORD)llTimeLimit;
  256. jobliLimits.PerProcessUserTimeLimit.HighPart = (DWORD)(llTimeLimit >> 32);
  257. }
  258. //
  259. // Keep existing job cpu limit
  260. //
  261. jobliLimits.LimitFlags |= JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME;
  262. break;
  263. case SLA_PROCESS_PRIORITY_CLASS:
  264. if (dwValue == NO_W3_CPU_LIMIT) {
  265. jobliLimits.LimitFlags &= ~JOB_OBJECT_LIMIT_PRIORITY_CLASS;
  266. }
  267. else {
  268. jobliLimits.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
  269. jobliLimits.PriorityClass = (DWORD)dwValue;
  270. }
  271. //
  272. // Keep existing job cpu limit
  273. //
  274. jobliLimits.LimitFlags |= JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME;
  275. break;
  276. case SLA_TERMINATE_ALL_PROCESSES:
  277. JOBOBJECT_END_OF_JOB_TIME_INFORMATION joeotiTermination;
  278. if (dwValue == NO_W3_CPU_LIMIT) {
  279. //
  280. // Need notifications
  281. //
  282. joeotiTermination.EndOfJobTimeAction = JOB_OBJECT_POST_AT_END_OF_JOB;
  283. if (!SetInformationJobObject(m_hJobObject,
  284. JobObjectEndOfJobTimeInformation,
  285. &joeotiTermination,
  286. sizeof(joeotiTermination))) {
  287. //
  288. // Log an event
  289. //
  290. DBGPRINTF((DBG_CONTEXT,
  291. "[SetJobLimit] - SetInformationJobObject failed, error code = 0x%X\n",
  292. GetLastError()));
  293. g_pInetSvc->LogEvent( W3_EVENT_JOB_SET_LIMIT_FAILED,
  294. 0,
  295. NULL,
  296. GetLastError() );
  297. }
  298. jobliLimits.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_TIME;
  299. }
  300. else {
  301. //
  302. // Need Terminate Behavior instead of notifications
  303. //
  304. joeotiTermination.EndOfJobTimeAction = JOB_OBJECT_TERMINATE_AT_END_OF_JOB;
  305. if (!SetInformationJobObject(m_hJobObject,
  306. JobObjectEndOfJobTimeInformation,
  307. &joeotiTermination,
  308. sizeof(joeotiTermination))) {
  309. //
  310. // Log an event
  311. //
  312. DBGPRINTF((DBG_CONTEXT,
  313. "[SetJobLimit] - SetInformationJobObject failed, error code = 0x%X\n",
  314. GetLastError()));
  315. g_pInetSvc->LogEvent( W3_EVENT_JOB_SET_LIMIT_FAILED,
  316. 0,
  317. NULL,
  318. GetLastError() );
  319. }
  320. jobliLimits.LimitFlags |= JOB_OBJECT_LIMIT_JOB_TIME;
  321. jobliLimits.PerJobUserTimeLimit.QuadPart = 1;
  322. }
  323. break;
  324. case SLA_JOB_CPU_LIMIT:
  325. if (dwValue == NO_W3_CPU_LIMIT) {
  326. jobliLimits.LimitFlags &= ~JOB_OBJECT_LIMIT_JOB_TIME;
  327. }
  328. else {
  329. #if 0
  330. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiInfo;
  331. if (!QueryInformationJobObject(m_hJobObject,
  332. JobObjectBasicAccountingInformation,
  333. &jobaiInfo,
  334. sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
  335. NULL)) {
  336. DBGPRINTF((DBG_CONTEXT,
  337. "[QueryJobInfo] - QueryInformationJobObject failed, error code = 0x%X\n",
  338. GetLastError()));
  339. g_pInetSvc->LogEvent( W3_EVENT_JOB_QUERY_FAILED,
  340. 0,
  341. NULL,
  342. GetLastError() );
  343. }
  344. else {
  345. llJobCPULimit += jobaiInfo.TotalUserTime.QuadPart;
  346. #endif
  347. jobliLimits.LimitFlags |= JOB_OBJECT_LIMIT_JOB_TIME;
  348. jobliLimits.PerJobUserTimeLimit.QuadPart = llJobCPULimit;
  349. #if 0
  350. }
  351. #endif
  352. }
  353. break;
  354. default:
  355. DBG_ASSERT(FALSE);
  356. }
  357. //
  358. // Set the new limit.
  359. //
  360. if (!SetInformationJobObject(m_hJobObject,
  361. JobObjectBasicLimitInformation,
  362. &jobliLimits,
  363. sizeof(jobliLimits))) {
  364. //
  365. // Log an event
  366. //
  367. DBGPRINTF((DBG_CONTEXT,
  368. "[SetJobLimit] - SetInformationJobObject failed, error code = 0x%X\n",
  369. GetLastError()));
  370. g_pInetSvc->LogEvent( W3_EVENT_JOB_SET_LIMIT_FAILED,
  371. 0,
  372. NULL,
  373. GetLastError() );
  374. }
  375. else {
  376. IF_DEBUG( JOB_OBJECTS )
  377. {
  378. DBGPRINTF((DBG_CONTEXT,
  379. "[W3_JOB_OBJECT::SetJobLimit] \nJob object %p limit action %d taken,\n"
  380. "value = 0x%X, cpulimit high word = 0x%X, cpulimit low word = 0x%X\n",
  381. this,
  382. slaAction,
  383. dwValue,
  384. (DWORD)((LONGLONG)llJobCPULimit >> 32),
  385. (DWORD)llJobCPULimit ));
  386. }
  387. }
  388. }
  389. UnlockThis();
  390. }
  391. /*++
  392. Routine Description:
  393. Add a process to the Job Object.
  394. Arguments:
  395. hProcess The process handle to add to the Job Object.
  396. Return Value:
  397. HRESULT - ERROR_SUCCESS
  398. Errors returned by AssignProcessToJobObject
  399. Notes:
  400. --*/
  401. DWORD
  402. W3_JOB_OBJECT::AddProcessToJob(
  403. IN HANDLE hProcess
  404. )
  405. {
  406. HRESULT dwReturn = ERROR_SUCCESS;
  407. DBG_ASSERT (m_hJobObject != NULL);
  408. if (!AssignProcessToJobObject(m_hJobObject,
  409. hProcess)) {
  410. dwReturn = GetLastError();
  411. }
  412. IF_DEBUG( JOB_OBJECTS )
  413. {
  414. DBGPRINTF((DBG_CONTEXT,
  415. "[W3_JOB_OBJECT::AddProcessToJob] \nProcess 0x%X added to Job object %p, error = 0x%X\n",
  416. HandleToUlong(hProcess),
  417. this,
  418. dwReturn ));
  419. }
  420. return dwReturn;
  421. }
  422. /*++
  423. Routine Description:
  424. Query Job Object Information.
  425. Arguments:
  426. pjobaiInfo Buffer to return the data.
  427. Will contain the difference since the last call to Reset Counters.
  428. Return Value:
  429. BOOL TRUE if succeeded
  430. Notes:
  431. --*/
  432. BOOL
  433. W3_JOB_OBJECT::QueryJobInfo(
  434. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiInfo,
  435. BOOL bResetCounters
  436. )
  437. {
  438. BOOL bReturn = FALSE;
  439. LockThis();
  440. DBG_ASSERT(m_hJobObject != NULL);
  441. bReturn = QueryInformationJobObject(m_hJobObject,
  442. JobObjectBasicAccountingInformation,
  443. pjobaiInfo,
  444. sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
  445. NULL);
  446. if (!bReturn) {
  447. DBGPRINTF((DBG_CONTEXT,
  448. "[QueryJobInfo] - QueryInformationJobObject failed, error code = 0x%X\n",
  449. GetLastError()));
  450. g_pInetSvc->LogEvent( W3_EVENT_JOB_QUERY_FAILED,
  451. 0,
  452. NULL,
  453. GetLastError() );
  454. }
  455. else {
  456. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiInfoBackup;
  457. //
  458. // Save away the real info in case we need it to reset the counters
  459. //
  460. if (bResetCounters) {
  461. memcpy(&jobaiInfoBackup, pjobaiInfo, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION));
  462. }
  463. //
  464. // Subtract off the previous counters, to return values for this interval.
  465. //
  466. SubtractJobInfo(pjobaiInfo,
  467. pjobaiInfo,
  468. &m_jobaiPrevInfo);
  469. //
  470. // Reset the counters
  471. // This modifies m_jobaiPrevInfo, so must be done after setting pjobaiInfo.
  472. //
  473. if (bResetCounters) {
  474. ResetCounters(&jobaiInfoBackup);
  475. }
  476. }
  477. UnlockThis();
  478. IF_DEBUG( JOB_OBJECTS )
  479. {
  480. DBGPRINTF((DBG_CONTEXT,
  481. "[W3_JOB_OBJECT::QueryJobInfo] \nJob object %p queried, Counter Reset = 0x%X, boolean error = 0x%X\n",
  482. this,
  483. bResetCounters,
  484. bReturn ));
  485. }
  486. return bReturn;
  487. }
  488. /*++
  489. Routine Description:
  490. Reset the period CPU counters. Can't actually reset them, so store the old values.
  491. Caller must call QueryInformationJobObject before calling Reset Counters and pass
  492. in the results.
  493. Arguments:
  494. pjobaiInfo Buffer with current counters.
  495. If null, initialize counters to 0.
  496. Return Value:
  497. Notes:
  498. --*/
  499. VOID
  500. W3_JOB_OBJECT::ResetCounters( JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiInfo )
  501. {
  502. if (pjobaiInfo == NULL) {
  503. memset((VOID *)&m_jobaiPrevInfo,
  504. 0,
  505. sizeof(m_jobaiPrevInfo));
  506. }
  507. else {
  508. //
  509. // Set these to the total values, since the ThisPeriod values
  510. // returned by QueryJobInfo may have been adjusted by a previous reset.
  511. //
  512. memcpy(&m_jobaiPrevInfo, pjobaiInfo, sizeof(m_jobaiPrevInfo));
  513. //
  514. // Active Processes is not a cumulative value, so set it to 0
  515. //
  516. //
  517. m_jobaiPrevInfo.ActiveProcesses = 0;
  518. }
  519. IF_DEBUG( JOB_OBJECTS )
  520. {
  521. DBGPRINTF((DBG_CONTEXT,
  522. "[W3_JOB_OBJECT::ResetCounters] \nJob object %p counters reset\n",
  523. this ));
  524. }
  525. }
  526. /*++
  527. Routine Description:
  528. Increment the stopped process for this period. This is called because
  529. stopped applications are stopped outside the job object, so do not get counted
  530. in the job object statistics.
  531. Can't actually add them to the job object info, so subtract them from the previous
  532. values, which get subtracted from current values on queries.
  533. Arguments:
  534. Return Value:
  535. Notes:
  536. --*/
  537. VOID
  538. W3_JOB_OBJECT::IncrementStoppedProcs( VOID )
  539. {
  540. LockThis();
  541. m_jobaiPrevInfo.TotalTerminatedProcesses--;
  542. UnlockThis();
  543. IF_DEBUG( JOB_OBJECTS )
  544. {
  545. DBGPRINTF((DBG_CONTEXT,
  546. "[W3_JOB_OBJECT::IncrementStoppedProcs] \nJob object %p stopped processes incremented\n",
  547. this ));
  548. }
  549. }
  550. /*++
  551. Routine Description:
  552. Process the Instance Stop Notification. When the instance stops:
  553. - remove periodic query routine.
  554. - Log current info
  555. - Reset Counters
  556. - Record the state
  557. Arguments:
  558. Return Value:
  559. Notes:
  560. --*/
  561. VOID
  562. W3_SERVER_INSTANCE::ProcessStopNotification()
  563. {
  564. //
  565. // Remove periodic reset
  566. //
  567. if (m_dwJobIntervalSchedulerCookie != 0) {
  568. DBG_REQUIRE(RemoveWorkItem( m_dwJobIntervalSchedulerCookie ));
  569. m_dwJobIntervalSchedulerCookie = 0;
  570. }
  571. LockJobsForWrite();
  572. IF_DEBUG( JOB_OBJECTS )
  573. {
  574. DBGPRINTF((DBG_CONTEXT,
  575. "[W3_SERVER_INSTANCE::ProcessStopNotification] \n"
  576. "Processing site stop notification for site %p\n",
  577. this ));
  578. }
  579. m_dwLastJobState = MD_SERVER_STATE_STOPPED;
  580. //
  581. // Remove periodic logging
  582. //
  583. if (m_dwJobLoggingSchedulerCookie != 0) {
  584. DBG_REQUIRE(RemoveWorkItem( m_dwJobLoggingSchedulerCookie ));
  585. m_dwJobLoggingSchedulerCookie = 0;
  586. QueryAndLogJobInfo(JOLE_LOGGING_INT_STOP, FALSE);
  587. }
  588. //
  589. // log info.
  590. //
  591. QueryAndLogJobInfo(JOLE_RESET_INT_STOP, FALSE);
  592. QueryAndLogJobInfo(JOLE_SITE_STOP, TRUE);
  593. //
  594. // Counters have been reset, so reset the limits
  595. //
  596. SetJobSiteCPULimits(TRUE);
  597. UnlockJobs();
  598. }
  599. /*++
  600. Routine Description:
  601. Start Job Objects up. Called in response to site start
  602. or enabling of limits or logging.
  603. Arguments:
  604. Return Value:
  605. Notes:
  606. Jobs lock must be held for write at entry.
  607. --*/
  608. VOID
  609. W3_SERVER_INSTANCE::StartJobs()
  610. {
  611. IF_DEBUG( JOB_OBJECTS )
  612. {
  613. DBGPRINTF((DBG_CONTEXT,
  614. "[W3_SERVER_INSTANCE::StartJobs] \n"
  615. "Starting Job Object functionality for site %p\n",
  616. this ));
  617. }
  618. SetCompletionPorts();
  619. LimitSiteCPU(TRUE, TRUE);
  620. ScheduleJobDeferredProcessing();
  621. }
  622. /*++
  623. Routine Description:
  624. Stop Job Objects, if necessary. Called in response to disabling
  625. of limits or logging.
  626. Arguments:
  627. Return Value:
  628. Notes:
  629. Jobs lock must be held for write EXACTLY ONCE at entry.
  630. --*/
  631. VOID
  632. W3_SERVER_INSTANCE::StopJobs()
  633. {
  634. IF_DEBUG( JOB_OBJECTS )
  635. {
  636. DBGPRINTF((DBG_CONTEXT,
  637. "[W3_SERVER_INSTANCE::StopJobs] \n"
  638. "Conditinally removing Job Object functionality for site %p\n",
  639. this ));
  640. }
  641. if (!m_fCPULoggingEnabled && m_dwJobLoggingSchedulerCookie) {
  642. DBG_REQUIRE(RemoveWorkItem( m_dwJobLoggingSchedulerCookie ));
  643. m_dwJobLoggingSchedulerCookie = 0;
  644. QueryAndLogJobInfo(JOLE_LOGGING_INT_STOP, FALSE);
  645. }
  646. if (!m_fCPULimitsEnabled) {
  647. LimitSiteCPU(FALSE, TRUE);
  648. }
  649. if (!m_fCPULoggingEnabled && !m_fCPULimitsEnabled) {
  650. //
  651. // Cannot have job lock when removing the interval work item.
  652. // This is still protected by the site lock, and job object
  653. // functions are disabled, so not really a problem.
  654. //
  655. UnlockJobs();
  656. if (m_dwJobIntervalSchedulerCookie != 0) {
  657. DBG_REQUIRE(RemoveWorkItem( m_dwJobIntervalSchedulerCookie ));
  658. m_dwJobIntervalSchedulerCookie = 0;
  659. QueryAndLogJobInfo(JOLE_RESET_INT_STOP, TRUE);
  660. }
  661. LockJobsForWrite();
  662. }
  663. }
  664. /*++
  665. Routine Description:
  666. Process the Instance Start Notification. When the instance starts:
  667. - Log current info
  668. - Reset Counters if previous state was stopped
  669. - Add periodic query routine
  670. - Record the state
  671. The logging and resetting above are done both at stop and start, in
  672. case an application or CGI continued processing while the instance was
  673. stopped or paused.
  674. Arguments:
  675. Return Value:
  676. Notes:
  677. --*/
  678. VOID
  679. W3_SERVER_INSTANCE::ProcessStartNotification()
  680. {
  681. LockJobsForWrite();
  682. IF_DEBUG( JOB_OBJECTS )
  683. {
  684. DBGPRINTF((DBG_CONTEXT,
  685. "[W3_SERVER_INSTANCE::ProcessStartNotification] \n"
  686. "Processing site start notification for site %p\n",
  687. this ));
  688. }
  689. //
  690. // If not already started
  691. //
  692. if (m_dwLastJobState != MD_SERVER_STATE_STARTED) {
  693. if (m_dwLastJobState == MD_SERVER_STATE_STOPPED) {
  694. QueryAndLogJobInfo(JOLE_SITE_START, FALSE);
  695. StartJobs();
  696. }
  697. else {
  698. DBG_ASSERT(m_dwLastJobState == MD_SERVER_STATE_PAUSED);
  699. QueryAndLogJobInfo(JOLE_SITE_START, FALSE);
  700. }
  701. m_dwLastJobState = MD_SERVER_STATE_STARTED;
  702. }
  703. UnlockJobs();
  704. }
  705. /*++
  706. Routine Description:
  707. Process the Instance Pause Notification. When the instance starts:
  708. - Remove periodic query routine
  709. - Log current info
  710. - Record the state
  711. Arguments:
  712. Return Value:
  713. Notes:
  714. --*/
  715. VOID
  716. W3_SERVER_INSTANCE::ProcessPauseNotification()
  717. {
  718. LockJobsForWrite();
  719. IF_DEBUG( JOB_OBJECTS )
  720. {
  721. DBGPRINTF((DBG_CONTEXT,
  722. "[W3_SERVER_INSTANCE::ProcessPauseNotification] \n"
  723. "Processing site pause notification for site %p\n",
  724. this ));
  725. }
  726. QueryAndLogJobInfo(JOLE_SITE_PAUSE, FALSE);
  727. m_dwLastJobState = MD_SERVER_STATE_PAUSED;
  728. UnlockJobs();
  729. }
  730. /*++
  731. Routine Description:
  732. Schedule the deferred processing routines for reset interval and logging,
  733. if they're not already scheduled.
  734. The instance and jobs locks must both be locked for write when this is called.
  735. Arguments:
  736. Return Value:
  737. BOOL TRUE if succeeded
  738. Notes:
  739. --*/
  740. BOOL
  741. W3_SERVER_INSTANCE::ScheduleJobDeferredProcessing()
  742. {
  743. BOOL bReturn = TRUE;
  744. bReturn = ScheduleJobDeferredReset();
  745. if (bReturn) {
  746. bReturn = ScheduleJobDeferredLogging();
  747. }
  748. return bReturn;
  749. }
  750. /*++
  751. Routine Description:
  752. Schedule the deferred processing routine for reset interval,
  753. if it's not already scheduled.
  754. The instance lock must be locked for write when this is called.
  755. Arguments:
  756. Return Value:
  757. BOOL TRUE if succeeded
  758. Notes:
  759. --*/
  760. BOOL
  761. W3_SERVER_INSTANCE::ScheduleJobDeferredReset()
  762. {
  763. BOOL bReturn = TRUE;
  764. LockJobsForWrite();
  765. if ((m_fCPULoggingEnabled || m_fCPULimitsEnabled)&&
  766. (m_dwJobIntervalSchedulerCookie == 0)) {
  767. m_dwJobIntervalSchedulerCookie = ScheduleWorkItem(DeferredJobResetInterval,
  768. (void *)this,
  769. m_dwJobResetInterval * MINUTESTOMILISECONDS,
  770. TRUE);
  771. if (m_dwJobIntervalSchedulerCookie == 0) {
  772. bReturn = FALSE;
  773. //
  774. // Log an event
  775. //
  776. DBGPRINTF((DBG_CONTEXT,
  777. "[ScheduleJobDeferredReset] - ScheduleWorkItem failed, error code = 0x%X\n",
  778. GetLastError()));
  779. g_pInetSvc->LogEvent( W3_EVENT_JOB_SCEDULE_FAILED,
  780. 0,
  781. NULL,
  782. GetLastError() );
  783. }
  784. else {
  785. //
  786. // Log Start of new interval
  787. //
  788. QueryAndLogJobInfo(JOLE_RESET_INT_START, FALSE);
  789. IF_DEBUG( JOB_OBJECTS )
  790. {
  791. DBGPRINTF((DBG_CONTEXT,
  792. "[W3_SERVER_INSTANCE::ScheduleJobDeferredReset] \n"
  793. "Reset interval processing started for site %p\n",
  794. this ));
  795. }
  796. }
  797. }
  798. UnlockJobs();
  799. return bReturn;
  800. }
  801. /*++
  802. Routine Description:
  803. Schedule the deferred processing routine for and logging,
  804. if it's not already scheduled.
  805. The jobs lock must be locked for write when this is called.
  806. Arguments:
  807. Return Value:
  808. BOOL TRUE if succeeded
  809. Notes:
  810. --*/
  811. BOOL
  812. W3_SERVER_INSTANCE::ScheduleJobDeferredLogging()
  813. {
  814. BOOL bReturn = TRUE;
  815. LockJobsForWrite();
  816. if (m_fCPULoggingEnabled && (m_dwJobLoggingSchedulerCookie == 0)) {
  817. m_dwJobLoggingSchedulerCookie = ScheduleWorkItem(DeferredQueryAndLogJobInfo,
  818. (void *)this,
  819. m_dwJobQueryInterval * MINUTESTOMILISECONDS,
  820. TRUE);
  821. if (m_dwJobLoggingSchedulerCookie == 0) {
  822. bReturn = FALSE;
  823. //
  824. // Log an event
  825. //
  826. DBGPRINTF((DBG_CONTEXT,
  827. "[ScheduleJobDeferredLogging] - ScheduleWorkItem failed, error code = 0x%X\n",
  828. GetLastError()));
  829. g_pInetSvc->LogEvent( W3_EVENT_JOB_SCEDULE_FAILED,
  830. 0,
  831. NULL,
  832. GetLastError() );
  833. }
  834. else {
  835. QueryAndLogJobInfo(JOLE_LOGGING_INT_START, FALSE);
  836. IF_DEBUG( JOB_OBJECTS )
  837. {
  838. DBGPRINTF((DBG_CONTEXT,
  839. "[W3_SERVER_INSTANCE::ScheduleJobDeferredLogging] \n"
  840. "Logging Deferred processing started %p\n",
  841. this ));
  842. }
  843. }
  844. }
  845. UnlockJobs();
  846. return bReturn;
  847. }
  848. /*++
  849. Routine Description:
  850. Add all Job Objects in the instance to the global completion port for limit checking, if
  851. limit checking is enabled.
  852. Arguments:
  853. Return Value:
  854. DWORD - ERROR_SUCCESS
  855. Errors returned by SetCompletionPort
  856. Notes:
  857. --*/
  858. VOID
  859. W3_SERVER_INSTANCE::SetCompletionPorts( )
  860. {
  861. SetCompletionPort(m_pwjoApplication);
  862. SetCompletionPort(m_pwjoCGI);
  863. }
  864. /*++
  865. Routine Description:
  866. Add Job Object to the global completion port for limit checking, if
  867. limit checking is enabled.
  868. Arguments:
  869. pwjoCurrent The job object class to add to the completion port.
  870. Return Value:
  871. DWORD - ERROR_SUCCESS
  872. Errors returned by W3_LIMIT_JOB_THREAD::GetLimitJobThread
  873. Errors returned by W3_JOB_OBJECT::SetCompletionPort
  874. Notes:
  875. --*/
  876. DWORD
  877. W3_SERVER_INSTANCE::SetCompletionPort(
  878. IN PW3_JOB_OBJECT pwjoCurrent
  879. )
  880. {
  881. DWORD dwReturn = ERROR_SUCCESS;
  882. PW3_LIMIT_JOB_THREAD pwljtClass = NULL;
  883. if (m_fCPULimitsEnabled && pwjoCurrent != NULL) {
  884. dwReturn = W3_LIMIT_JOB_THREAD::GetLimitJobThread(&pwljtClass);
  885. if (dwReturn == ERROR_SUCCESS) {
  886. DBG_ASSERT(pwljtClass != NULL);
  887. dwReturn = pwjoCurrent->SetCompletionPort(pwljtClass->GetCompletionPort(),
  888. (PVOID)this);
  889. }
  890. IF_DEBUG( JOB_OBJECTS )
  891. {
  892. DBGPRINTF((DBG_CONTEXT,
  893. "[W3_SERVER_INSTANCE::SetCompletionPort] \n"
  894. "Job Object %p for site %p added to completion port 0x%X\n"
  895. "error = 0x%X\n",
  896. pwjoCurrent,
  897. this,
  898. (pwljtClass != NULL) ? HandleToUlong(pwljtClass->GetCompletionPort()) : 0,
  899. dwReturn ));
  900. }
  901. }
  902. return dwReturn;
  903. }
  904. /*++
  905. Routine Description:
  906. Add a process to the appropriate Job Object. Create the Job Object class if
  907. necessary. Add to completion port if limits enabled.
  908. Arguments:
  909. hProcess The process handle to add to the Job Object.
  910. bIsApplicationProcess If true, process is added to the Application Job Object.
  911. Otherwise, it is added to the CGI Job Object.
  912. Return Value:
  913. DWORD - ERROR_SUCCESS
  914. ERROR_NOT_ENOUGH_MEMORY
  915. Errors returned by ScheduleWorkItem
  916. Errors returned by W3_JOB_OBJECT::AddProcessToJob
  917. Notes:
  918. --*/
  919. DWORD
  920. W3_SERVER_INSTANCE::AddProcessToJob(
  921. IN HANDLE hProcess,
  922. IN BOOL bIsApplicationProcess
  923. )
  924. {
  925. DWORD dwReturn = ERROR_SUCCESS;
  926. PW3_JOB_OBJECT *ppwjoCurrent = NULL;
  927. if (m_fCPULoggingEnabled || m_fCPULimitsEnabled) {
  928. if (bIsApplicationProcess) {
  929. ppwjoCurrent = &m_pwjoApplication;
  930. }
  931. else {
  932. ppwjoCurrent = &m_pwjoCGI;
  933. }
  934. if (*ppwjoCurrent == NULL) {
  935. LockJobsForWrite();
  936. if (*ppwjoCurrent == NULL) {
  937. if (bIsApplicationProcess) {
  938. *ppwjoCurrent = new W3_JOB_OBJECT();
  939. }
  940. else {
  941. *ppwjoCurrent = new W3_JOB_OBJECT(m_dwJobCGICPULimit);
  942. }
  943. if (*ppwjoCurrent == NULL) {
  944. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  945. }
  946. else {
  947. dwReturn = (*ppwjoCurrent)->GetInitError();
  948. }
  949. if (dwReturn != ERROR_SUCCESS) {
  950. delete (*ppwjoCurrent);
  951. *ppwjoCurrent = NULL;
  952. }
  953. else {
  954. dwReturn = SetCompletionPort(*ppwjoCurrent);
  955. if (dwReturn != ERROR_SUCCESS) {
  956. delete (*ppwjoCurrent);
  957. *ppwjoCurrent = NULL;
  958. }
  959. else {
  960. if (m_fJobSiteCPULimitPriorityEnabled) {
  961. SetJobLimits(SLA_PROCESS_PRIORITY_CLASS, IDLE_PRIORITY_CLASS);
  962. }
  963. if (m_fJobSiteCPULimitProcStopEnabled) {
  964. SetJobLimits(SLA_TERMINATE_ALL_PROCESSES, 1);
  965. }
  966. //
  967. // Set limit times and enforce current limits in job object
  968. //
  969. LimitSiteCPU(TRUE,
  970. TRUE);
  971. }
  972. }
  973. }
  974. UnlockJobs();
  975. }
  976. //
  977. // *ppwjoCurrent This only gets changed in this routine, and only gets deleted at termination,
  978. // so I claim we do not need instance locking here.
  979. //
  980. // The job object class does its own locking if necessary.
  981. //
  982. if (*ppwjoCurrent != NULL) {
  983. dwReturn = (*ppwjoCurrent)->AddProcessToJob(hProcess);
  984. }
  985. }
  986. IF_DEBUG( JOB_OBJECTS )
  987. {
  988. DBGPRINTF((DBG_CONTEXT,
  989. "[W3_SERVER_INSTANCE::AddProcessToJob] \n"
  990. "Site %p adding process 0x%X to Job Object %p\n"
  991. "error = 0x%X\n",
  992. this,
  993. HandleToUlong(hProcess),
  994. (ppwjoCurrent != NULL) ? *ppwjoCurrent : 0,
  995. dwReturn ));
  996. }
  997. return dwReturn;
  998. }
  999. /*++
  1000. Routine Description:
  1001. Calculate the CPU time as a percentage of the interval time.
  1002. Return and ASCII string with the percent in 2.3% format.
  1003. Arguments:
  1004. llCPUTime The currently used CPU time in 100 nanosecond units.
  1005. pszPercentCPUTime character buffer of length 8 to return the string.
  1006. Return Value:
  1007. HRESULT - ERROR_SUCCESS
  1008. E_OUTOFMEMORY
  1009. Errors returned by ScheduleWorkItem
  1010. Errors returned by W3_JOB_OBJECT::AddProcessToJob
  1011. Notes:
  1012. --*/
  1013. VOID
  1014. W3_SERVER_INSTANCE::GetPercentFromCPUTime(IN LONGLONG llCPUTime,
  1015. OUT LPSTR pszPercentCPUTime)
  1016. {
  1017. LONGLONG llPercentCPUTime = (llCPUTime * (LONGLONG)100000) / m_llJobResetIntervalCPU;
  1018. DBG_ASSERT(llPercentCPUTime < 100000);
  1019. //
  1020. // Can't find a numeric routine to format this nicely so just do it.
  1021. //
  1022. pszPercentCPUTime[0] = (char)((llPercentCPUTime / 10000) + '0');
  1023. pszPercentCPUTime[1] = (char)(((llPercentCPUTime / 1000) % 10) + '0');
  1024. pszPercentCPUTime[2] = '.';
  1025. pszPercentCPUTime[3] = (char)(((llPercentCPUTime / 100) % 10) + '0');
  1026. pszPercentCPUTime[4] = (char)(((llPercentCPUTime / 10) % 10) + '0');
  1027. pszPercentCPUTime[5] = (char)((llPercentCPUTime % 10) + '0');
  1028. pszPercentCPUTime[6] = '%';
  1029. pszPercentCPUTime[7] = '\0';
  1030. }
  1031. #define ShouldLogJobInfo() ((m_fCPULoggingEnabled || joleLogEvent == JOLE_LOGGING_INT_STOP) && \
  1032. (m_dwJobLoggingOptions != MD_CPU_DISABLE_ALL_LOGGING))
  1033. /*++
  1034. Routine Description:
  1035. Write log informatin to the log file.
  1036. Arguments:
  1037. pjbaiLogInfo The statistics.
  1038. joleLogEvent The event being logged.
  1039. joptProcessType The type of the processes being logged (CGI, Application, All).
  1040. Return Value:
  1041. HRESULT - ERROR_SUCCESS
  1042. E_OUTOFMEMORY
  1043. Errors returned by ScheduleWorkItem
  1044. Errors returned by W3_JOB_OBJECT::AddProcessToJob
  1045. Notes:
  1046. --*/
  1047. VOID
  1048. W3_SERVER_INSTANCE::LogJobInfo(IN PJOBOBJECT_BASIC_ACCOUNTING_INFORMATION pjbaiLogInfo,
  1049. IN JOB_OBJECT_LOG_EVENTS joleLogEvent,
  1050. IN JOB_OBJECT_PROCESS_TYPE joptProcessType)
  1051. {
  1052. CUSTOM_LOG_DATA cldarrayLogInfo[JOLF_NUM_ELEMENTS];
  1053. char pszUserTimePercent[8];
  1054. char pszKernelTimePercent[8];
  1055. cldarrayLogInfo[JOLF_EVENT].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_EVENT_PATH;
  1056. cldarrayLogInfo[JOLF_EVENT].pData = (PVOID) pszarrayJOLE[joleLogEvent];
  1057. cldarrayLogInfo[JOLF_INFO_TYPE].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_PROCESS_TYPE_PATH;
  1058. cldarrayLogInfo[JOLF_INFO_TYPE].pData = (PVOID) pszarrayJOPT[joptProcessType];
  1059. GetPercentFromCPUTime((LONGLONG)pjbaiLogInfo->TotalUserTime.QuadPart,
  1060. pszUserTimePercent);
  1061. cldarrayLogInfo[JOLF_USER_TIME].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_USER_TIME_PATH;
  1062. cldarrayLogInfo[JOLF_USER_TIME].pData = (PVOID) &pszUserTimePercent;
  1063. GetPercentFromCPUTime((LONGLONG)pjbaiLogInfo->TotalKernelTime.QuadPart,
  1064. pszKernelTimePercent);
  1065. cldarrayLogInfo[JOLF_KERNEL_TIME].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_KERNEL_TIME_PATH;
  1066. cldarrayLogInfo[JOLF_KERNEL_TIME].pData = (PVOID) &pszKernelTimePercent;
  1067. cldarrayLogInfo[JOLF_PAGE_FAULT].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_PAGE_FAULT_PATH;
  1068. cldarrayLogInfo[JOLF_PAGE_FAULT].pData = (PVOID) &(pjbaiLogInfo->TotalPageFaultCount);
  1069. cldarrayLogInfo[JOLF_TOTAL_PROCS].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_TOTAL_PROCS_PATH;
  1070. cldarrayLogInfo[JOLF_TOTAL_PROCS].pData = (PVOID) &(pjbaiLogInfo->TotalProcesses);
  1071. cldarrayLogInfo[JOLF_ACTIVE_PROCS].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_ACTIVE_PROCS_PATH;
  1072. cldarrayLogInfo[JOLF_ACTIVE_PROCS].pData = (PVOID) &(pjbaiLogInfo->ActiveProcesses);
  1073. cldarrayLogInfo[JOLF_TERMINATED_PROCS].szPropertyPath = W3_CPU_LOG_PATH W3_CPU_LOG_TERMINATED_PROCS_PATH;
  1074. cldarrayLogInfo[JOLF_TERMINATED_PROCS].pData = (PVOID) &(pjbaiLogInfo->TotalTerminatedProcesses);
  1075. DWORD dwError = m_Logging.LogCustomInformation(JOLF_NUM_ELEMENTS,
  1076. cldarrayLogInfo,
  1077. "#SubComponent: Process Accounting"
  1078. );
  1079. if (dwError != ERROR_SUCCESS) {
  1080. //
  1081. // CODEWORK - Log to Event Log Here
  1082. //
  1083. }
  1084. IF_DEBUG( JOB_OBJECTS )
  1085. {
  1086. DBGPRINTF((DBG_CONTEXT,
  1087. "[W3_SERVER_INSTANCE::LogJobInfo] \n"
  1088. "Site %p logging job information,\n"
  1089. "event = %d, process type = %d, error = 0x%X\n",
  1090. this,
  1091. (DWORD)joleLogEvent,
  1092. (DWORD)joptProcessType,
  1093. dwError ));
  1094. }
  1095. }
  1096. /*++
  1097. Routine Description:
  1098. Query the job objects in this site and log the info.
  1099. Arguments:
  1100. joleLogEvent The event being Logged.
  1101. bResetCounters Reset the counters if TRUE.
  1102. Return Value:
  1103. HRESULT - ERROR_SUCCESS
  1104. Errors returned by Logging
  1105. Notes:
  1106. This does not get the jobs lock, because that would risk a deadlock between the
  1107. deferred process and the call to RemoveWorkItem call to remove the deferred
  1108. process. It is ok, and a good idea, for nondeferred called to hold the jobs lock.
  1109. --*/
  1110. VOID
  1111. W3_SERVER_INSTANCE::QueryAndLogJobInfo( IN JOB_OBJECT_LOG_EVENTS joleLogEvent,
  1112. IN BOOL bResetCounters )
  1113. {
  1114. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiApplicationInfo;
  1115. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiCGIInfo;
  1116. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiSumInfo;
  1117. //
  1118. // Make sure counters get reset for limits, even if logging is off
  1119. //
  1120. if ( (bResetCounters) ||
  1121. ShouldLogJobInfo() ) {
  1122. QueryAndSumJobInfo(&jobaiSumInfo,
  1123. &jobaiApplicationInfo,
  1124. &jobaiCGIInfo,
  1125. bResetCounters);
  1126. //
  1127. // LogJobsInfo will check ShouldLogJobInfo, so no
  1128. // need to check it again.
  1129. //
  1130. LogJobsInfo( joleLogEvent,
  1131. &jobaiApplicationInfo,
  1132. &jobaiCGIInfo,
  1133. &jobaiSumInfo );
  1134. }
  1135. }
  1136. /*++
  1137. Routine Description:
  1138. Log the info for all jobs on this site.
  1139. Arguments:
  1140. joleLogEvent The event being Logged.
  1141. jobaiApplicationInfo The job object info for applications.
  1142. jobaiCGIInfo The job object info for CGI.
  1143. jobaiSumInfo The job object info for all jobs.
  1144. Return Value:
  1145. Notes:
  1146. This does not get the jobs lock, because that would risk a deadlock between the
  1147. deferred process and the call to RemoveWorkItem call to remove the deferred
  1148. process. It is ok, and a good idea, for nondeferred called to hold the jobs lock.
  1149. --*/
  1150. VOID
  1151. W3_SERVER_INSTANCE::LogJobsInfo( IN JOB_OBJECT_LOG_EVENTS joleLogEvent,
  1152. IN JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiApplicationInfo,
  1153. IN JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiCGIInfo,
  1154. IN JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiSumInfo )
  1155. {
  1156. if (ShouldLogJobInfo()) {
  1157. IF_DEBUG( JOB_OBJECTS )
  1158. {
  1159. DBGPRINTF((DBG_CONTEXT,
  1160. "[W3_SERVER_INSTANCE::LogJobsInfo] \n"
  1161. "Site %p conditionally logging job information for all types\n"
  1162. "event = %d\n",
  1163. this,
  1164. (DWORD)joleLogEvent ));
  1165. }
  1166. if ((m_dwJobLoggingOptions & MD_CPU_ENABLE_CGI_LOGGING) != 0) {
  1167. LogJobInfo(pjobaiCGIInfo,
  1168. joleLogEvent,
  1169. JOPT_CGI);
  1170. }
  1171. if ((m_dwJobLoggingOptions & MD_CPU_ENABLE_APP_LOGGING) != 0) {
  1172. LogJobInfo(pjobaiApplicationInfo,
  1173. joleLogEvent,
  1174. JOPT_APP);
  1175. }
  1176. if ((m_dwJobLoggingOptions & MD_CPU_ENABLE_ALL_PROC_LOGGING) != 0) {
  1177. LogJobInfo(pjobaiSumInfo,
  1178. joleLogEvent,
  1179. JOPT_ALL);
  1180. }
  1181. }
  1182. }
  1183. /*++
  1184. Routine Description:
  1185. Reset The job query interval.
  1186. Logging has either been enabled, disabled, or the interval changed.
  1187. Reset query routine as appropriate.
  1188. Arguments:
  1189. Return Value:
  1190. Notes:
  1191. --*/
  1192. VOID
  1193. W3_SERVER_INSTANCE::ResetJobQueryInterval()
  1194. {
  1195. LockJobsForWrite();
  1196. IF_DEBUG( JOB_OBJECTS )
  1197. {
  1198. DBGPRINTF((DBG_CONTEXT,
  1199. "[W3_SERVER_INSTANCE::ResetJobQueryInterval] \n"
  1200. "Site %p resetting job query interval\n",
  1201. this ));
  1202. }
  1203. if (m_dwJobLoggingSchedulerCookie != 0) {
  1204. DBG_REQUIRE(RemoveWorkItem( m_dwJobLoggingSchedulerCookie ));
  1205. m_dwJobLoggingSchedulerCookie = 0;
  1206. QueryAndLogJobInfo(JOLE_LOGGING_INT_STOP, FALSE);
  1207. }
  1208. if ((m_dwLastJobState != MD_SERVER_STATE_STOPPED) &&
  1209. (m_fCPULoggingEnabled)) {
  1210. QueryAndLogJobInfo(JOLE_LOGGING_INT_CHANGE, FALSE);
  1211. ScheduleJobDeferredLogging();
  1212. }
  1213. UnlockJobs();
  1214. }
  1215. /*++
  1216. Routine Description:
  1217. Changes the job reset interval. Removes all existing limits, resets all information,
  1218. and starts the new interval.
  1219. Notes:
  1220. Instance should be locked for write prior to this call.
  1221. --*/
  1222. VOID
  1223. W3_SERVER_INSTANCE::ResetJobResetInterval()
  1224. {
  1225. if (m_dwJobIntervalSchedulerCookie != 0) {
  1226. //
  1227. // Remove periodic reset
  1228. //
  1229. DBG_REQUIRE(RemoveWorkItem( m_dwJobIntervalSchedulerCookie ));
  1230. m_dwJobIntervalSchedulerCookie = 0;
  1231. }
  1232. //
  1233. // Calculate the total cpu time per interval
  1234. //
  1235. m_llJobResetIntervalCPU = GetCPUTimeFromInterval(m_dwJobResetInterval);
  1236. LockJobsForWrite();
  1237. IF_DEBUG( JOB_OBJECTS )
  1238. {
  1239. DBGPRINTF((DBG_CONTEXT,
  1240. "[W3_SERVER_INSTANCE::ResetJobResetInterval] \n"
  1241. "Site %p resetting job reset interval\n",
  1242. this ));
  1243. }
  1244. if ((m_dwLastJobState != MD_SERVER_STATE_STOPPED) &&
  1245. (m_fCPULoggingEnabled || m_fCPULimitsEnabled)) {
  1246. //
  1247. // Remove periodic logging
  1248. //
  1249. if (m_dwJobLoggingSchedulerCookie != 0) {
  1250. DBG_REQUIRE(RemoveWorkItem( m_dwJobLoggingSchedulerCookie ));
  1251. m_dwJobLoggingSchedulerCookie = 0;
  1252. QueryAndLogJobInfo(JOLE_LOGGING_INT_STOP, TRUE);
  1253. }
  1254. QueryAndLogJobInfo(JOLE_RESET_INT_STOP, TRUE);
  1255. //
  1256. // reset the counters, and log info.
  1257. //
  1258. QueryAndLogJobInfo(JOLE_RESET_INT_CHANGE, FALSE);
  1259. //
  1260. // Reset Limits
  1261. //
  1262. SetJobSiteCPULimits(TRUE);
  1263. //
  1264. // Restart Periodic routines, if necessary
  1265. //
  1266. ScheduleJobDeferredProcessing();
  1267. }
  1268. UnlockJobs();
  1269. }
  1270. /*++
  1271. Routine Description:
  1272. Interval expired, start next interval.
  1273. Removes all existing limits, resets all information,
  1274. and starts the new interval.
  1275. --*/
  1276. VOID
  1277. W3_SERVER_INSTANCE::JobResetInterval()
  1278. {
  1279. LockJobsForWrite();
  1280. IF_DEBUG( JOB_OBJECTS )
  1281. {
  1282. DBGPRINTF((DBG_CONTEXT,
  1283. "[W3_SERVER_INSTANCE::JobResetInterval] \n"
  1284. "Site %p processing job reset interval\n",
  1285. this ));
  1286. }
  1287. //
  1288. // Remove periodic logging
  1289. //
  1290. if (m_dwJobLoggingSchedulerCookie != 0) {
  1291. DBG_REQUIRE(RemoveWorkItem( m_dwJobLoggingSchedulerCookie ));
  1292. QueryAndLogJobInfo(JOLE_LOGGING_INT_STOP, FALSE);
  1293. }
  1294. //
  1295. // reset the counters, and log info.
  1296. //
  1297. QueryAndLogJobInfo(JOLE_RESET_INT_STOP, TRUE);
  1298. //
  1299. // Reset Limits
  1300. //
  1301. SetJobSiteCPULimits(TRUE);
  1302. //
  1303. // Log Again to show start of new interval
  1304. //
  1305. QueryAndLogJobInfo(JOLE_RESET_INT_START, FALSE);
  1306. //
  1307. // Restart Periodic logging, if necessary
  1308. //
  1309. if (m_dwJobLoggingSchedulerCookie != 0) {
  1310. m_dwJobLoggingSchedulerCookie = 0;
  1311. ScheduleJobDeferredLogging();
  1312. }
  1313. UnlockJobs();
  1314. }
  1315. /*++
  1316. Routine Description:
  1317. Terminate Applications. Called when ProcStop limit hit.
  1318. --*/
  1319. VOID
  1320. W3_SERVER_INSTANCE::TerminateCPUApplications(DWORD_PTR dwValue)
  1321. {
  1322. BUFFER bufDataPaths;
  1323. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  1324. LPSTR pszCurrentPath;
  1325. DWORD dwMBValue;
  1326. STR strPath;
  1327. BOOL fAppUnloaded;
  1328. IF_DEBUG( JOB_OBJECTS )
  1329. {
  1330. DBGPRINTF((DBG_CONTEXT,
  1331. "[W3_SERVER_INSTANCE::TerminateCPUApplications] \n"
  1332. "Site %p terminating Applications\n",
  1333. this ));
  1334. }
  1335. if ( mb.Open( QueryMDPath(),
  1336. METADATA_PERMISSION_READ ) ) {
  1337. //
  1338. // First find the OOP Applications
  1339. //
  1340. if (mb.GetDataPaths(NULL,
  1341. MD_APP_PACKAGE_ID,
  1342. STRING_METADATA,
  1343. &bufDataPaths)) {
  1344. //
  1345. // For each OOP Application
  1346. //
  1347. for (pszCurrentPath = (LPSTR)bufDataPaths.QueryPtr();
  1348. *pszCurrentPath != '\0';
  1349. pszCurrentPath += (strlen(pszCurrentPath) + 1)) {
  1350. //
  1351. // If the application is CPU enabled.
  1352. //
  1353. if (mb.GetDword(pszCurrentPath,
  1354. MD_CPU_APP_ENABLED,
  1355. IIS_MD_UT_FILE,
  1356. &dwMBValue) &&
  1357. dwMBValue) {
  1358. //
  1359. // Close the metabase before doing application calls
  1360. //
  1361. mb.Close();
  1362. strPath.Copy(QueryMDPath());
  1363. strPath.Append(pszCurrentPath);
  1364. strPath.SetLen(strlen(strPath.QueryStr()) - 1);
  1365. if (dwValue != NO_W3_CPU_LIMIT) {
  1366. g_pWamDictator->UnLoadWamInfo(&strPath, TRUE, &fAppUnloaded);
  1367. if (fAppUnloaded) {
  1368. if (m_pwjoApplication != NULL) {
  1369. m_pwjoApplication->IncrementStoppedProcs();
  1370. }
  1371. }
  1372. }
  1373. else {
  1374. g_pWamDictator->CPUResumeWamInfo(&strPath);
  1375. }
  1376. if ( !mb.Open( QueryMDPath(),
  1377. METADATA_PERMISSION_READ ) ) {
  1378. break;
  1379. }
  1380. }
  1381. }
  1382. }
  1383. }
  1384. }
  1385. /*++
  1386. Routine Description:
  1387. Set or reset Job limits.
  1388. Arguments:
  1389. slaAction The limit to set
  1390. dwValue The limit value. 0 = remove limit.
  1391. Return Value:
  1392. Notes:
  1393. Requires at least write lock on entry.
  1394. Cannot have read lock, as TerminateCPUApplications will eventually result
  1395. in a call to AddProcessToJob, which attempts to get a write lock.
  1396. --*/
  1397. VOID
  1398. W3_SERVER_INSTANCE::SetJobLimits(SET_LIMIT_ACTION slaAction,
  1399. DWORD dwValue,
  1400. LONGLONG llJobCPULimit)
  1401. {
  1402. IF_DEBUG( JOB_OBJECTS )
  1403. {
  1404. DBGPRINTF((DBG_CONTEXT,
  1405. "[W3_SERVER_INSTANCE::SetJobLimits] \n"
  1406. "Site %p taking limit action %d,\n"
  1407. "value = 0x%X, cpulimit high word = 0x%X, cpulimit low word = 0x%X\n",
  1408. this,
  1409. slaAction,
  1410. dwValue,
  1411. (DWORD)((LONGLONG)llJobCPULimit >> 32),
  1412. (DWORD)llJobCPULimit ));
  1413. }
  1414. switch (slaAction) {
  1415. case SLA_PROCESS_CPU_LIMIT:
  1416. if (m_pwjoCGI != NULL) {
  1417. m_pwjoCGI->SetJobLimit(slaAction, dwValue);
  1418. }
  1419. break;
  1420. case SLA_PROCESS_PRIORITY_CLASS:
  1421. if (m_pwjoCGI != NULL) {
  1422. m_pwjoCGI->SetJobLimit(slaAction, dwValue);
  1423. }
  1424. if (m_pwjoApplication != NULL) {
  1425. m_pwjoApplication->SetJobLimit(slaAction, dwValue);
  1426. }
  1427. break;
  1428. case SLA_TERMINATE_ALL_PROCESSES:
  1429. if (m_pwjoCGI != NULL) {
  1430. m_pwjoCGI->SetJobLimit(slaAction, dwValue);
  1431. }
  1432. W3_JOB_QUEUE::QueueWorkItem( JQA_TERMINATE_SITE_APPS,
  1433. (PVOID)this,
  1434. (PVOID)UIntToPtr(dwValue) );
  1435. break;
  1436. case SLA_JOB_CPU_LIMIT:
  1437. if (m_pwjoCGI != NULL) {
  1438. m_pwjoCGI->SetJobLimit(slaAction, dwValue, llJobCPULimit);
  1439. }
  1440. if (m_pwjoApplication != NULL) {
  1441. m_pwjoApplication->SetJobLimit(slaAction, dwValue, llJobCPULimit);
  1442. }
  1443. break;
  1444. default:
  1445. DBG_ASSERT(FALSE);
  1446. }
  1447. }
  1448. /*++
  1449. Routine Description:
  1450. Set the limit processing as appropriate.
  1451. Called when limits or reset interval may have changed.
  1452. Remove limits that should not be there.
  1453. Add limits that should be there.
  1454. Start or stop site limit Deferred Processing Routine.
  1455. --*/
  1456. VOID
  1457. W3_SERVER_INSTANCE::SetJobSiteCPULimits(BOOL fHasWriteLock)
  1458. {
  1459. if (!fHasWriteLock) {
  1460. LockJobsForWrite();
  1461. }
  1462. //
  1463. // There may be a limit that was increased or removed, so first
  1464. // check and disable penalty if necessary.
  1465. //
  1466. LimitSiteCPU(FALSE,
  1467. TRUE);
  1468. //
  1469. // There may have been a limit added are reduced, so
  1470. // enable limits.
  1471. //
  1472. LimitSiteCPU(TRUE,
  1473. TRUE);
  1474. if (!fHasWriteLock) {
  1475. UnlockJobs();
  1476. }
  1477. }
  1478. /*++
  1479. Routine Description:
  1480. Add the values of 2 job objects.
  1481. Arguments:
  1482. pjobaiSumInfo Buffer to return the sum.
  1483. pjobaiInfo1 First job object info.
  1484. pjobaiInfo2 Second job object info.
  1485. --*/
  1486. VOID
  1487. W3_JOB_OBJECT::SumJobInfo(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiSumInfo,
  1488. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiInfo1,
  1489. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiInfo2)
  1490. {
  1491. DBG_ASSERT (pjobaiSumInfo != NULL);
  1492. DBG_ASSERT (pjobaiInfo1 != NULL);
  1493. DBG_ASSERT (pjobaiInfo2 != NULL);
  1494. pjobaiSumInfo->TotalUserTime.QuadPart =
  1495. (pjobaiInfo1->TotalUserTime.QuadPart +
  1496. pjobaiInfo2->TotalUserTime.QuadPart);
  1497. pjobaiSumInfo->TotalKernelTime.QuadPart =
  1498. (pjobaiInfo1->TotalKernelTime.QuadPart +
  1499. pjobaiInfo2->TotalKernelTime.QuadPart);
  1500. pjobaiSumInfo->ThisPeriodTotalUserTime.QuadPart =
  1501. (pjobaiInfo1->ThisPeriodTotalUserTime.QuadPart +
  1502. pjobaiInfo2->ThisPeriodTotalUserTime.QuadPart);
  1503. pjobaiSumInfo->ThisPeriodTotalKernelTime.QuadPart =
  1504. (pjobaiInfo1->ThisPeriodTotalKernelTime.QuadPart +
  1505. pjobaiInfo2->ThisPeriodTotalKernelTime.QuadPart);
  1506. pjobaiSumInfo->TotalPageFaultCount =
  1507. (pjobaiInfo1->TotalPageFaultCount +
  1508. pjobaiInfo2->TotalPageFaultCount);
  1509. pjobaiSumInfo->TotalProcesses =
  1510. (pjobaiInfo1->TotalProcesses +
  1511. pjobaiInfo2->TotalProcesses);
  1512. pjobaiSumInfo->ActiveProcesses =
  1513. (pjobaiInfo1->ActiveProcesses +
  1514. pjobaiInfo2->ActiveProcesses);
  1515. pjobaiSumInfo->TotalTerminatedProcesses =
  1516. (pjobaiInfo1->TotalTerminatedProcesses +
  1517. pjobaiInfo2->TotalTerminatedProcesses);
  1518. }
  1519. /*++
  1520. Routine Description:
  1521. Subtract the values of 2 job objects.
  1522. Arguments:
  1523. pjobaiResultInfo Buffer to return the difference.
  1524. pjobaiInfo1 First job object info, to subtract from.
  1525. pjobaiInfo2 Second job object info, to subtract.
  1526. --*/
  1527. VOID
  1528. W3_JOB_OBJECT::SubtractJobInfo(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiResultinfo,
  1529. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiInfo1,
  1530. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiInfo2)
  1531. {
  1532. DBG_ASSERT (pjobaiResultinfo != NULL);
  1533. DBG_ASSERT (pjobaiInfo1 != NULL);
  1534. DBG_ASSERT (pjobaiInfo2 != NULL);
  1535. pjobaiResultinfo->TotalUserTime.QuadPart =
  1536. (pjobaiInfo1->TotalUserTime.QuadPart -
  1537. pjobaiInfo2->TotalUserTime.QuadPart);
  1538. pjobaiResultinfo->TotalKernelTime.QuadPart =
  1539. (pjobaiInfo1->TotalKernelTime.QuadPart -
  1540. pjobaiInfo2->TotalKernelTime.QuadPart);
  1541. pjobaiResultinfo->ThisPeriodTotalUserTime.QuadPart =
  1542. (pjobaiInfo1->ThisPeriodTotalUserTime.QuadPart -
  1543. pjobaiInfo2->ThisPeriodTotalUserTime.QuadPart);
  1544. pjobaiResultinfo->ThisPeriodTotalKernelTime.QuadPart =
  1545. (pjobaiInfo1->ThisPeriodTotalKernelTime.QuadPart -
  1546. pjobaiInfo2->ThisPeriodTotalKernelTime.QuadPart);
  1547. pjobaiResultinfo->TotalPageFaultCount =
  1548. (pjobaiInfo1->TotalPageFaultCount -
  1549. pjobaiInfo2->TotalPageFaultCount);
  1550. pjobaiResultinfo->TotalProcesses =
  1551. (pjobaiInfo1->TotalProcesses -
  1552. pjobaiInfo2->TotalProcesses);
  1553. pjobaiResultinfo->ActiveProcesses =
  1554. (pjobaiInfo1->ActiveProcesses -
  1555. pjobaiInfo2->ActiveProcesses);
  1556. pjobaiResultinfo->TotalTerminatedProcesses =
  1557. (pjobaiInfo1->TotalTerminatedProcesses -
  1558. pjobaiInfo2->TotalTerminatedProcesses);
  1559. }
  1560. /*++
  1561. Routine Description:
  1562. Query the job objects. Return the results of those queries and the sum.
  1563. Reset counters if necessary (on interval change).
  1564. Arguments:
  1565. pjobaiSumInfo Buffer to return the sum.
  1566. pjobaiApplicationInfo Buffer to return the Application info.
  1567. pjobaiCGIInfo Buffer to return the CGI infoSecond job object info.
  1568. bResetCounters Reset counters if true.
  1569. Returns:
  1570. TRUE = Information was queried from at least one job object.
  1571. FALSE = All returned info set to 0.
  1572. --*/
  1573. BOOL
  1574. W3_SERVER_INSTANCE::QueryAndSumJobInfo(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiSumInfo,
  1575. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiApplicationInfo,
  1576. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiCGIInfo,
  1577. BOOL bResetCounters)
  1578. {
  1579. BOOL bIsApplicationInfo = FALSE;
  1580. BOOL bIsCGIInfo = FALSE;
  1581. BOOL bReturn = FALSE;
  1582. IF_DEBUG( JOB_OBJECTS )
  1583. {
  1584. DBGPRINTF((DBG_CONTEXT,
  1585. "[W3_SERVER_INSTANCE::QueryAndSumJobInfo] \n"
  1586. "Site %p Querying all site job objects\n",
  1587. this ));
  1588. }
  1589. if (m_pwjoApplication != NULL) {
  1590. bIsApplicationInfo = m_pwjoApplication->QueryJobInfo(pjobaiApplicationInfo,
  1591. bResetCounters);
  1592. }
  1593. if (!bIsApplicationInfo) {
  1594. memset(pjobaiApplicationInfo, 0, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION ));
  1595. }
  1596. if (m_pwjoCGI != NULL) {
  1597. bIsCGIInfo = m_pwjoCGI->QueryJobInfo(pjobaiCGIInfo,
  1598. bResetCounters);
  1599. }
  1600. if (!bIsCGIInfo) {
  1601. memset(pjobaiCGIInfo, 0, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION ));
  1602. }
  1603. W3_JOB_OBJECT::SumJobInfo(pjobaiSumInfo,
  1604. pjobaiApplicationInfo,
  1605. pjobaiCGIInfo);
  1606. if (bIsCGIInfo || bIsApplicationInfo) {
  1607. bReturn = TRUE;
  1608. }
  1609. return bReturn;
  1610. }
  1611. /*++
  1612. Routine Description:
  1613. Convert a percent CPU limit to a CPU time.
  1614. Arguments:
  1615. dwLimitPercent The percent limit in units 1/100000 of the reset interval.
  1616. Returns:
  1617. The cpu time for the limit in 100 nanosecond units.
  1618. --*/
  1619. LONGLONG
  1620. W3_SERVER_INSTANCE::PercentCPULimitToCPUTime(DWORD dwLimitPercent)
  1621. {
  1622. //
  1623. // It's always safe to divide the reset interval by 100000, since it is
  1624. // calculated as minutes * 60 * 10000000
  1625. //
  1626. return ((LONGLONG)dwLimitPercent * (m_llJobResetIntervalCPU / (LONGLONG)100000));
  1627. }
  1628. /*++
  1629. Routine Description:
  1630. Check if the Site CPU Limit as been exceeded.
  1631. Arguments:
  1632. CPULimit - The limit in 100 nanosecond units.
  1633. Return Value:
  1634. TRUE if limit is valid.
  1635. Notes:
  1636. --*/
  1637. BOOL
  1638. W3_SERVER_INSTANCE::ExceededLimit(LONGLONG llCPULimit,
  1639. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiSumInfo)
  1640. {
  1641. LONGLONG llCurrentCPU;
  1642. BOOL bReturn = FALSE;
  1643. if (IsLimitValid(llCPULimit)) {
  1644. llCurrentCPU = pjobaiSumInfo->TotalUserTime.QuadPart +
  1645. pjobaiSumInfo->TotalKernelTime.QuadPart;
  1646. if (llCurrentCPU >= llCPULimit) {
  1647. bReturn = TRUE;
  1648. }
  1649. }
  1650. return bReturn;
  1651. }
  1652. /*++
  1653. Routine Description:
  1654. Calculate the time until a limit is reached.
  1655. Arguments:
  1656. llCPULimit The limit, in 100 nanosecond units.
  1657. pjobaiSumInfo The current resources used for the site.
  1658. Returns:
  1659. The time left in 100 nanosecond units.
  1660. If the limit does not exist, or is in the past, then MAXLONGLONG.
  1661. --*/
  1662. LONGLONG
  1663. W3_SERVER_INSTANCE::CalculateTimeUntilStop(LONGLONG llCPULimit,
  1664. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *pjobaiSumInfo)
  1665. {
  1666. LONGLONG llCurrentCPU;
  1667. LONGLONG llReturn = MAXLONGLONG;
  1668. if (IsLimitValid(llCPULimit)) {
  1669. llCurrentCPU = pjobaiSumInfo->TotalUserTime.QuadPart +
  1670. pjobaiSumInfo->TotalKernelTime.QuadPart;
  1671. if (llCurrentCPU < llCPULimit) {
  1672. llReturn = llCPULimit - llCurrentCPU;
  1673. }
  1674. }
  1675. return llReturn;
  1676. }
  1677. /*++
  1678. Routine Description:
  1679. Calculate the time limit to set in each job object from
  1680. the time until the next limit is hit.
  1681. Arguments:
  1682. llTimeToNextLimit The time until the next limit is hit, 100 nanosecond units.
  1683. dwNumJobObjects The current number of job objects.
  1684. Returns:
  1685. The time to set, in seconds.
  1686. --*/
  1687. LONGLONG
  1688. W3_SERVER_INSTANCE::CalculateNewJobLimit(LONGLONG llTimeToNextLimit,
  1689. DWORD dwNumJobObjects)
  1690. {
  1691. LONGLONG llNewJobLimit = llTimeToNextLimit;
  1692. DBG_ASSERT (dwNumJobObjects > 0);
  1693. if ((dwNumJobObjects > 1) &&
  1694. (llNewJobLimit > MINUTESTO100NANOSECONDS)) {
  1695. //
  1696. // If more than a minute left, divide time
  1697. // among job objects.
  1698. //
  1699. llNewJobLimit /= dwNumJobObjects;
  1700. }
  1701. return llNewJobLimit;
  1702. }
  1703. /*++
  1704. Routine Description:
  1705. Check site limits and enable or disable as appropriate.
  1706. Arguments:
  1707. fEnableLimits - TRUE = check and enable limits if exceeded.
  1708. FALSE = check and disable limits if not exceeded.
  1709. --*/
  1710. VOID
  1711. W3_SERVER_INSTANCE::LimitSiteCPU(BOOL fEnableLimits,
  1712. BOOL fHasWriteLock)
  1713. {
  1714. if (!fHasWriteLock) {
  1715. LockJobsForWrite();
  1716. }
  1717. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiSumInfo;
  1718. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiCGIInfo;
  1719. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobaiApplicationInfo;
  1720. //
  1721. // If there is any information
  1722. //
  1723. if (QueryAndSumJobInfo(&jobaiSumInfo,
  1724. &jobaiApplicationInfo,
  1725. &jobaiCGIInfo,
  1726. FALSE)) {
  1727. IF_DEBUG( JOB_OBJECTS )
  1728. {
  1729. DBGPRINTF((DBG_CONTEXT,
  1730. "[W3_SERVER_INSTANCE::LimitSiteCPU] \n"
  1731. "Site %p checking site limits, fEnableLimits = 0x%X\n",
  1732. this,
  1733. (DWORD)fEnableLimits ));
  1734. }
  1735. const CHAR * apsz[1];
  1736. //
  1737. // if limits enabled for this site and paremeter = enable limits
  1738. //
  1739. if (m_fCPULimitsEnabled && fEnableLimits) {
  1740. //
  1741. // if limit not in force and limit exceeded
  1742. // then enable limit
  1743. //
  1744. if ((!m_fJobSiteCPULimitLogEventEnabled) &&
  1745. (ExceededLimit(m_llJobSiteCPULimitLogEvent,
  1746. &jobaiSumInfo))) {
  1747. m_fJobSiteCPULimitLogEventEnabled = TRUE;
  1748. LogJobsInfo( JOLE_EVENTLOG_LIMIT,
  1749. &jobaiApplicationInfo,
  1750. & jobaiCGIInfo,
  1751. &jobaiSumInfo );
  1752. //
  1753. // Log an event
  1754. //
  1755. DBGPRINTF((DBG_CONTEXT,
  1756. "[LimitSiteCPU] - LogEvent Limit Hit\n"));
  1757. apsz[0] = QuerySiteName();
  1758. DBG_ASSERT(apsz[0] != NULL);
  1759. g_pInetSvc->LogEvent( W3_EVENT_JOB_LOGEVENT_LIMIT,
  1760. 1,
  1761. apsz,
  1762. 0 );
  1763. }
  1764. //
  1765. // if limit not in force and limit exceeded
  1766. // then enable limit
  1767. //
  1768. if ((!m_fJobSiteCPULimitPriorityEnabled) &&
  1769. (ExceededLimit(m_llJobSiteCPULimitPriority,
  1770. &jobaiSumInfo))) {
  1771. m_fJobSiteCPULimitPriorityEnabled = TRUE;
  1772. LogJobsInfo( JOLE_PRIORITY_LIMIT,
  1773. &jobaiApplicationInfo,
  1774. & jobaiCGIInfo,
  1775. &jobaiSumInfo );
  1776. SetJobLimits(SLA_PROCESS_PRIORITY_CLASS, IDLE_PRIORITY_CLASS);
  1777. //
  1778. // Log an event
  1779. //
  1780. DBGPRINTF((DBG_CONTEXT,
  1781. "[LimitSiteCPU] - Priority Limit Hit\n"));
  1782. apsz[0] = QuerySiteName();
  1783. DBG_ASSERT(apsz[0] != NULL);
  1784. g_pInetSvc->LogEvent( W3_EVENT_JOB_PRIORITY_LIMIT,
  1785. 1,
  1786. apsz,
  1787. 0 );
  1788. }
  1789. //
  1790. // if limit not in force and limit exceeded
  1791. // then enable limit
  1792. //
  1793. if ((!m_fJobSiteCPULimitProcStopEnabled) &&
  1794. (ExceededLimit(m_llJobSiteCPULimitProcStop,
  1795. &jobaiSumInfo))) {
  1796. m_fJobSiteCPULimitProcStopEnabled = TRUE;
  1797. LogJobsInfo( JOLE_PROCSTOP_LIMIT,
  1798. &jobaiApplicationInfo,
  1799. & jobaiCGIInfo,
  1800. &jobaiSumInfo );
  1801. SetJobLimits(SLA_TERMINATE_ALL_PROCESSES, 1);
  1802. //
  1803. // Log an event
  1804. //
  1805. DBGPRINTF((DBG_CONTEXT,
  1806. "[LimitSiteCPU] - ProcStop Limit Hit\n"));
  1807. apsz[0] = QuerySiteName();
  1808. DBG_ASSERT(apsz[0] != NULL);
  1809. g_pInetSvc->LogEvent( W3_EVENT_JOB_PROCSTOP_LIMIT,
  1810. 1,
  1811. apsz,
  1812. 0 );
  1813. }
  1814. //
  1815. // if limit not in force and limit exceeded
  1816. // then enable limit
  1817. //
  1818. if ((!m_fJobSiteCPULimitPauseEnabled) &&
  1819. (ExceededLimit(m_llJobSiteCPULimitPause,
  1820. &jobaiSumInfo))) {
  1821. m_fJobSiteCPULimitPauseEnabled = TRUE;
  1822. LogJobsInfo( JOLE_PAUSE_LIMIT,
  1823. &jobaiApplicationInfo,
  1824. & jobaiCGIInfo,
  1825. &jobaiSumInfo );
  1826. //
  1827. // Log an event
  1828. //
  1829. DBGPRINTF((DBG_CONTEXT,
  1830. "[LimitSiteCPU] - Site Pause Limit Hit\n"));
  1831. apsz[0] = QuerySiteName();
  1832. DBG_ASSERT(apsz[0] != NULL);
  1833. g_pInetSvc->LogEvent( W3_EVENT_JOB_PAUSE_LIMIT,
  1834. 1,
  1835. apsz,
  1836. 0 );
  1837. }
  1838. //
  1839. // Calulate and set job limits
  1840. //
  1841. DWORD dwNumJobObjects;
  1842. dwNumJobObjects = 0;
  1843. if (m_pwjoCGI != NULL) {
  1844. dwNumJobObjects++;
  1845. }
  1846. if (m_pwjoApplication != NULL) {
  1847. dwNumJobObjects++;
  1848. }
  1849. if (!m_fJobSiteCPULimitPauseEnabled &&
  1850. !m_fJobSiteCPULimitProcStopEnabled &&
  1851. dwNumJobObjects > 0) {
  1852. //
  1853. // There may be another limit coming
  1854. //
  1855. LONGLONG llTimeToNextLimit;
  1856. llTimeToNextLimit = MAXLONGLONG;
  1857. llTimeToNextLimit = min(llTimeToNextLimit,
  1858. CalculateTimeUntilStop(m_llJobSiteCPULimitLogEvent,
  1859. &jobaiSumInfo));
  1860. llTimeToNextLimit = min(llTimeToNextLimit,
  1861. CalculateTimeUntilStop(m_llJobSiteCPULimitPriority,
  1862. &jobaiSumInfo));
  1863. llTimeToNextLimit = min(llTimeToNextLimit,
  1864. CalculateTimeUntilStop(m_llJobSiteCPULimitProcStop,
  1865. &jobaiSumInfo));
  1866. llTimeToNextLimit = min(llTimeToNextLimit,
  1867. CalculateTimeUntilStop(m_llJobSiteCPULimitPause,
  1868. &jobaiSumInfo));
  1869. if (llTimeToNextLimit != MAXLONGLONG) {
  1870. LONGLONG llNewJobLimit;
  1871. llNewJobLimit = CalculateNewJobLimit(llTimeToNextLimit,
  1872. dwNumJobObjects);
  1873. IF_DEBUG( JOB_OBJECTS )
  1874. {
  1875. DBGPRINTF((DBG_CONTEXT,
  1876. "[W3_SERVER_INSTANCE::LimitSiteCPU] \nSetting New Limit in seconds,"
  1877. "high word = %u, low word = %u \n",
  1878. (DWORD)((LONGLONG)(llNewJobLimit / SECONDSTO100NANOSECONDS) >> 32),
  1879. (DWORD)((LONGLONG)(llNewJobLimit / SECONDSTO100NANOSECONDS)) ));
  1880. }
  1881. SetJobLimits(SLA_JOB_CPU_LIMIT,
  1882. 1,
  1883. llNewJobLimit);
  1884. }
  1885. }
  1886. }
  1887. else {
  1888. //
  1889. // There's been a configuration change. May need to disable
  1890. // a limit that's already been hit.
  1891. //
  1892. //
  1893. // if limit in force and limit not exceeded
  1894. // then disable limit.
  1895. //
  1896. if ((m_fJobSiteCPULimitLogEventEnabled) &&
  1897. (!m_fCPULimitsEnabled || !ExceededLimit(m_llJobSiteCPULimitLogEvent,
  1898. &jobaiSumInfo))) {
  1899. m_fJobSiteCPULimitLogEventEnabled = FALSE;
  1900. //log event
  1901. LogJobsInfo( JOLE_EVENTLOG_LIMIT_RESET,
  1902. &jobaiApplicationInfo,
  1903. & jobaiCGIInfo,
  1904. &jobaiSumInfo );
  1905. }
  1906. if ((m_fJobSiteCPULimitPriorityEnabled) &&
  1907. (!m_fCPULimitsEnabled || !ExceededLimit(m_llJobSiteCPULimitPriority,
  1908. &jobaiSumInfo))) {
  1909. // log event
  1910. m_fJobSiteCPULimitPriorityEnabled = FALSE;
  1911. LogJobsInfo( JOLE_PRIORITY_LIMIT_RESET,
  1912. &jobaiApplicationInfo,
  1913. & jobaiCGIInfo,
  1914. &jobaiSumInfo );
  1915. SetJobLimits(SLA_PROCESS_PRIORITY_CLASS, NO_W3_CPU_LIMIT);
  1916. }
  1917. if ((m_fJobSiteCPULimitProcStopEnabled) &&
  1918. (!m_fCPULimitsEnabled || !ExceededLimit(m_llJobSiteCPULimitProcStop,
  1919. &jobaiSumInfo))) {
  1920. m_fJobSiteCPULimitProcStopEnabled = FALSE;
  1921. // log event
  1922. LogJobsInfo( JOLE_PROCSTOP_LIMIT_RESET,
  1923. &jobaiApplicationInfo,
  1924. & jobaiCGIInfo,
  1925. &jobaiSumInfo );
  1926. SetJobLimits(SLA_TERMINATE_ALL_PROCESSES, NO_W3_CPU_LIMIT);
  1927. // Stop Processes
  1928. }
  1929. if ((m_fJobSiteCPULimitPauseEnabled) &&
  1930. (!m_fCPULimitsEnabled || !ExceededLimit(m_llJobSiteCPULimitPause,
  1931. &jobaiSumInfo))) {
  1932. // log event
  1933. m_fJobSiteCPULimitPauseEnabled = FALSE;
  1934. LogJobsInfo( JOLE_PAUSE_LIMIT_RESET,
  1935. &jobaiApplicationInfo,
  1936. & jobaiCGIInfo,
  1937. &jobaiSumInfo );
  1938. }
  1939. if (!m_fCPULimitsEnabled) {
  1940. SetJobLimits(SLA_JOB_CPU_LIMIT,
  1941. NO_W3_CPU_LIMIT);
  1942. }
  1943. }
  1944. }
  1945. if (!fHasWriteLock) {
  1946. UnlockJobs();
  1947. }
  1948. }
  1949. W3_LIMIT_JOB_THREAD *W3_LIMIT_JOB_THREAD::m_pljtLimitJobs = NULL;
  1950. /*++
  1951. Routine Description:
  1952. Constructor for W3_LIMIT_JOB_THREAD.
  1953. Create a completion port and a thread to monitor it.
  1954. Arguments:
  1955. None
  1956. Returns:
  1957. Sets m_dwInitError, which can be queried by GetInitError()
  1958. Notes:
  1959. The caller should check the init error and delete this class on failure.
  1960. --*/
  1961. W3_LIMIT_JOB_THREAD::W3_LIMIT_JOB_THREAD( void ):
  1962. m_hLimitThread ( NULL ),
  1963. m_hCompletionPort ( NULL ),
  1964. m_dwInitError ( NO_ERROR )
  1965. {
  1966. DWORD dwLimitThreadId;
  1967. m_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, // No associated file
  1968. NULL, // No existing completion port
  1969. NULL, // Completion Key, this is ignored
  1970. 0); // max Concurrent threads = numprocessors
  1971. if (m_hCompletionPort == NULL) {
  1972. m_dwInitError = GetLastError();
  1973. }
  1974. else {
  1975. m_hLimitThread = CreateThread(NULL, // Security Attributes
  1976. 0, // Default Stack Size
  1977. &LimitThreadProcStub, // Thread start routine
  1978. (LPVOID)this, // parameter
  1979. 0, // Creation Flags
  1980. &dwLimitThreadId); // Thread ID
  1981. if (m_hLimitThread == NULL) {
  1982. m_dwInitError = GetLastError();
  1983. }
  1984. }
  1985. }
  1986. /*++
  1987. Routine Description:
  1988. Destructor for W3_LIMIT_JOB_THREAD.
  1989. Posts an entry to the completion port to cause the thread to terminate.
  1990. Close the handle of the completion port.
  1991. Arguments:
  1992. None
  1993. Returns:
  1994. Nothing
  1995. --*/
  1996. W3_LIMIT_JOB_THREAD::~W3_LIMIT_JOB_THREAD( void )
  1997. {
  1998. TerminateLimitJobThreadThread();
  1999. if (m_hCompletionPort != NULL) {
  2000. DBG_REQUIRE(CloseHandle(m_hCompletionPort));
  2001. }
  2002. }
  2003. /*++
  2004. Routine Description:
  2005. Terminate the LimitJobThread class, if any.
  2006. Arguments:
  2007. None
  2008. Returns:
  2009. Nothing
  2010. --*/
  2011. //static
  2012. VOID
  2013. W3_LIMIT_JOB_THREAD::TerminateLimitJobThread( void )
  2014. // Static
  2015. {
  2016. LockGlobals();
  2017. if ( m_pljtLimitJobs )
  2018. {
  2019. delete m_pljtLimitJobs;
  2020. m_pljtLimitJobs = NULL;
  2021. }
  2022. UnlockGlobals();
  2023. }
  2024. /*++
  2025. Routine Description:
  2026. Stop Processing the job queue, if any.
  2027. Arguments:
  2028. None
  2029. Returns:
  2030. Nothing
  2031. --*/
  2032. // static
  2033. VOID
  2034. W3_LIMIT_JOB_THREAD::StopLimitJobThread( void )
  2035. {
  2036. LockGlobals();
  2037. if ( m_pljtLimitJobs )
  2038. {
  2039. m_pljtLimitJobs->TerminateLimitJobThreadThread();
  2040. }
  2041. UnlockGlobals();
  2042. }
  2043. /*++
  2044. Routine Description:
  2045. Terminate the limit thread.
  2046. Arguments:
  2047. None
  2048. Returns:
  2049. Nothing
  2050. --*/
  2051. VOID
  2052. W3_LIMIT_JOB_THREAD::TerminateLimitJobThreadThread( void )
  2053. {
  2054. if (m_hLimitThread != NULL) {
  2055. DBG_ASSERT(m_hCompletionPort != NULL);
  2056. PostQueuedCompletionStatus(m_hCompletionPort,
  2057. 0, // dwNumberOfBytesTransferred
  2058. 0, // dwCompletionKey
  2059. NULL); // lpOverlapped
  2060. DBG_REQUIRE (WaitForSingleObject(m_hLimitThread,
  2061. 10000) != WAIT_TIMEOUT);
  2062. m_hLimitThread = NULL;
  2063. }
  2064. }
  2065. /*++
  2066. Routine Description:
  2067. Static routine to get an instance of this class.
  2068. Arguments:
  2069. ppljtLimitJobs On return, will contain the class pointer
  2070. Returns:
  2071. ERROR_SUCCESS
  2072. ERROR_NOT_ENOUGH_MEMORY
  2073. Errors returned by constructor
  2074. --*/
  2075. DWORD
  2076. W3_LIMIT_JOB_THREAD::GetLimitJobThread( W3_LIMIT_JOB_THREAD ** ppljtLimitJobs )
  2077. {
  2078. DWORD dwReturn = ERROR_SUCCESS;
  2079. if (m_pljtLimitJobs != NULL) {
  2080. *ppljtLimitJobs = m_pljtLimitJobs;
  2081. }
  2082. else {
  2083. LockGlobals();
  2084. if (m_pljtLimitJobs != NULL) {
  2085. *ppljtLimitJobs = m_pljtLimitJobs;
  2086. }
  2087. else {
  2088. PW3_LIMIT_JOB_THREAD pljtLimitJobs;
  2089. //
  2090. // Create the class
  2091. // Be sure not to set m_pljtLimitJobs until it's
  2092. // known valid, since it's normally accessed
  2093. // outside the lock.
  2094. //
  2095. pljtLimitJobs = new W3_LIMIT_JOB_THREAD();
  2096. if (pljtLimitJobs == NULL) {
  2097. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  2098. }
  2099. else {
  2100. dwReturn = pljtLimitJobs->GetInitError();
  2101. if (dwReturn == NO_ERROR) {
  2102. m_pljtLimitJobs = pljtLimitJobs;
  2103. *ppljtLimitJobs = pljtLimitJobs;
  2104. }
  2105. else {
  2106. delete pljtLimitJobs;
  2107. }
  2108. }
  2109. }
  2110. UnlockGlobals();
  2111. }
  2112. return dwReturn;
  2113. }
  2114. /*++
  2115. Routine Description:
  2116. The main routine of the monitoring thread.
  2117. Calls the instance on check job object limits.
  2118. Terminates when the completion key is NULL.
  2119. Arguments:
  2120. ppljtLimitJobs On return, will contain the class pointer
  2121. Returns:
  2122. ERROR_SUCCESS
  2123. ERROR_NOT_ENOUGH_MEMORY
  2124. Errors returned by constructor
  2125. --*/
  2126. DWORD
  2127. W3_LIMIT_JOB_THREAD::LimitThreadProc ( void )
  2128. {
  2129. DWORD dwNumberOfBytesTransferred;
  2130. ULONG_PTR uipCompletionKey;
  2131. LPOVERLAPPED pOverlapped;
  2132. DBG_ASSERT(m_hCompletionPort != NULL);
  2133. while (TRUE) {
  2134. if (GetQueuedCompletionStatus(m_hCompletionPort,
  2135. &dwNumberOfBytesTransferred,
  2136. &uipCompletionKey,
  2137. &pOverlapped,
  2138. INFINITE) ) {
  2139. if (uipCompletionKey == NULL) {
  2140. break;
  2141. }
  2142. if (dwNumberOfBytesTransferred == JOB_OBJECT_MSG_END_OF_JOB_TIME) {
  2143. DBG_ASSERT(uipCompletionKey != NULL);
  2144. ((W3_SERVER_INSTANCE *)uipCompletionKey)->LimitSiteCPU(TRUE,
  2145. FALSE);
  2146. }
  2147. }
  2148. else {
  2149. DBG_ASSERT(FALSE);
  2150. }
  2151. }
  2152. return NO_ERROR;
  2153. }
  2154. W3_JOB_QUEUE *W3_JOB_QUEUE::m_pjqJobQueue = NULL;
  2155. /*++
  2156. Routine Description:
  2157. Constructor for W3_JOB_QUEUE.
  2158. Create a Job Queue and a thread to monitor it.
  2159. Arguments:
  2160. None
  2161. Returns:
  2162. Sets m_dwInitError, which can be queried by GetInitError()
  2163. Notes:
  2164. The caller should check the init error and delete this class on failure.
  2165. --*/
  2166. W3_JOB_QUEUE::W3_JOB_QUEUE( void ):
  2167. m_hQueueThread ( NULL ),
  2168. m_hQueueEvent ( NULL ),
  2169. m_dwInitError ( NO_ERROR )
  2170. {
  2171. DWORD dwQueueThreadId;
  2172. INITIALIZE_CRITICAL_SECTION( &m_csLock );
  2173. InitializeListHead( &m_leJobQueue );
  2174. m_hQueueEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
  2175. if (m_hQueueEvent == NULL) {
  2176. m_dwInitError = GetLastError();
  2177. }
  2178. else {
  2179. m_hQueueThread = CreateThread(NULL, // Security Attributes
  2180. 0, // Default Stack Size
  2181. &QueueThreadProcStub, // Thread start routine
  2182. (LPVOID)this, // parameter
  2183. 0, // Creation Flags
  2184. &dwQueueThreadId); // Thread ID
  2185. if (m_hQueueThread == NULL) {
  2186. m_dwInitError = GetLastError();
  2187. }
  2188. }
  2189. }
  2190. /*++
  2191. Routine Description:
  2192. Destructor for W3_JOB_QUEUE.
  2193. Arguments:
  2194. None
  2195. Returns:
  2196. Nothing
  2197. --*/
  2198. W3_JOB_QUEUE::~W3_JOB_QUEUE( void )
  2199. {
  2200. PLIST_ENTRY pleWorkItem;
  2201. PJOB_WORK_ITEM pjwiWorkItem;
  2202. TerminateJobQueueThread();
  2203. LockThis();
  2204. while (!IsListEmpty(&m_leJobQueue )) {
  2205. pleWorkItem = RemoveHeadList ( &m_leJobQueue );
  2206. pjwiWorkItem = CONTAINING_RECORD(pleWorkItem,
  2207. JOB_WORK_ITEM,
  2208. ListEntry);
  2209. LocalFree (pjwiWorkItem);
  2210. }
  2211. if (m_hQueueEvent != NULL) {
  2212. CloseHandle(m_hQueueEvent);
  2213. }
  2214. UnlockThis();
  2215. DeleteCriticalSection(&m_csLock);
  2216. }
  2217. /*++
  2218. Routine Description:
  2219. Terminate the job queue, if any.
  2220. Arguments:
  2221. None
  2222. Returns:
  2223. Nothing
  2224. --*/
  2225. // Static
  2226. VOID
  2227. W3_JOB_QUEUE::TerminateJobQueue( void )
  2228. {
  2229. LockGlobals();
  2230. if ( m_pjqJobQueue )
  2231. {
  2232. delete m_pjqJobQueue;
  2233. m_pjqJobQueue = NULL;
  2234. }
  2235. UnlockGlobals();
  2236. }
  2237. /*++
  2238. Routine Description:
  2239. Stop Processing the job queue, if any.
  2240. Arguments:
  2241. None
  2242. Returns:
  2243. Nothing
  2244. --*/
  2245. // Static
  2246. VOID
  2247. W3_JOB_QUEUE::StopJobQueue( void )
  2248. {
  2249. LockGlobals();
  2250. if ( m_pjqJobQueue )
  2251. {
  2252. m_pjqJobQueue->TerminateJobQueueThread();
  2253. }
  2254. UnlockGlobals();
  2255. }
  2256. /*++
  2257. Routine Description:
  2258. Stop Processing the job queue, if any.
  2259. Arguments:
  2260. None
  2261. Returns:
  2262. Nothing
  2263. --*/
  2264. VOID
  2265. W3_JOB_QUEUE::TerminateJobQueueThread( void )
  2266. {
  2267. if (m_hQueueThread != NULL) {
  2268. DBG_REQUIRE(QueueWorkItem_Worker( JQA_TERMINATE_THREAD,
  2269. NULL,
  2270. NULL,
  2271. FALSE ) == ERROR_SUCCESS);
  2272. DBG_REQUIRE (WaitForSingleObject(m_hQueueThread,
  2273. 10000) != WAIT_TIMEOUT);
  2274. m_hQueueThread = NULL;
  2275. }
  2276. }
  2277. /*++
  2278. Routine Description:
  2279. Static routine to get an instance of this class.
  2280. Arguments:
  2281. ppljtLimitJobs On return, will contain the class pointer
  2282. Returns:
  2283. ERROR_SUCCESS
  2284. ERROR_NOT_ENOUGH_MEMORY
  2285. Errors returned by constructor
  2286. --*/
  2287. DWORD
  2288. W3_JOB_QUEUE::GetJobQueue( W3_JOB_QUEUE ** ppjqJobQueue )
  2289. {
  2290. DWORD dwReturn = ERROR_SUCCESS;
  2291. if (m_pjqJobQueue != NULL) {
  2292. *ppjqJobQueue = m_pjqJobQueue;
  2293. }
  2294. else {
  2295. LockGlobals();
  2296. if (m_pjqJobQueue != NULL) {
  2297. *ppjqJobQueue = m_pjqJobQueue;
  2298. }
  2299. else {
  2300. PW3_JOB_QUEUE pjqJobQueue;
  2301. //
  2302. // Create the class
  2303. // Be sure not to set m_pjqJobQueue until it's
  2304. // known valid, since it's normally accessed
  2305. // outside the lock.
  2306. //
  2307. pjqJobQueue = new W3_JOB_QUEUE();
  2308. if (pjqJobQueue == NULL) {
  2309. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  2310. }
  2311. else {
  2312. dwReturn = pjqJobQueue->GetInitError();
  2313. if (dwReturn == NO_ERROR) {
  2314. m_pjqJobQueue = pjqJobQueue;
  2315. *ppjqJobQueue = pjqJobQueue;
  2316. }
  2317. else {
  2318. delete pjqJobQueue;
  2319. }
  2320. }
  2321. }
  2322. UnlockGlobals();
  2323. }
  2324. return dwReturn;
  2325. }
  2326. /*++
  2327. Routine Description:
  2328. The main routine of the monitoring thread.
  2329. Processes the queue of work items.
  2330. Terminates when it receives a JQA_TERMINATE_THREAD item.
  2331. Arguments:
  2332. Returns:
  2333. ERROR_SUCCESS
  2334. --*/
  2335. DWORD
  2336. W3_JOB_QUEUE::QueueThreadProc ( void )
  2337. {
  2338. DWORD dwResult;
  2339. PLIST_ENTRY pleWorkItem;
  2340. PJOB_WORK_ITEM pjwiWorkItem;
  2341. BOOL fTerminate = FALSE;
  2342. while (!fTerminate) {
  2343. dwResult = WaitForSingleObject ( m_hQueueEvent, INFINITE );
  2344. if (dwResult == WAIT_FAILED) {
  2345. Sleep(1000);
  2346. continue;
  2347. }
  2348. LockThis();
  2349. while (!IsListEmpty(&m_leJobQueue ) && !fTerminate) {
  2350. pleWorkItem = RemoveHeadList ( &m_leJobQueue );
  2351. UnlockThis();
  2352. pjwiWorkItem = CONTAINING_RECORD(pleWorkItem,
  2353. JOB_WORK_ITEM,
  2354. ListEntry);
  2355. switch (pjwiWorkItem->jqaAction) {
  2356. case JQA_RESTART_ALL_APPS:
  2357. {
  2358. DeferredRestartChangedApplications(pjwiWorkItem->pvParam);
  2359. }
  2360. break;
  2361. case JQA_TERMINATE_SITE_APPS:
  2362. {
  2363. ((PW3_SERVER_INSTANCE)(pjwiWorkItem->pwsiInstance))->
  2364. TerminateCPUApplications((DWORD_PTR)pjwiWorkItem->pvParam);
  2365. }
  2366. break;
  2367. case JQA_TERMINATE_THREAD:
  2368. {
  2369. fTerminate = TRUE;
  2370. }
  2371. break;
  2372. default:
  2373. {
  2374. DBG_ASSERT(FALSE);
  2375. }
  2376. }
  2377. LocalFree(pjwiWorkItem);
  2378. LockThis();
  2379. }
  2380. UnlockThis();
  2381. }
  2382. return NO_ERROR;
  2383. }
  2384. /*++
  2385. Routine Description:
  2386. Static routine to queue a work item to the job
  2387. queue.
  2388. Arguments:
  2389. jqaAction The action to perform.
  2390. pwsiInstance The instance associated with this action.
  2391. pvParam The parameter to the work item.
  2392. Returns:
  2393. ERROR_SUCCESS
  2394. Errors returned by GetJobQueue
  2395. Errors returned by QueueWorkItem_Worker
  2396. --*/
  2397. DWORD
  2398. W3_JOB_QUEUE::QueueWorkItem( JOB_QUEUE_ACTION jqaAction,
  2399. PVOID pwsiInstance,
  2400. PVOID pvParam)
  2401. {
  2402. W3_JOB_QUEUE *pjqJobQueue;
  2403. DWORD dwReturn;
  2404. dwReturn = GetJobQueue(&pjqJobQueue);
  2405. if (dwReturn == ERROR_SUCCESS) {
  2406. dwReturn = pjqJobQueue->QueueWorkItem_Worker(jqaAction,
  2407. pwsiInstance,
  2408. pvParam);
  2409. }
  2410. if (dwReturn != ERROR_SUCCESS) {
  2411. //
  2412. // Log an event
  2413. //
  2414. DBGPRINTF((DBG_CONTEXT,
  2415. "[W3_JOB_QUEUE::QueueWorkItem_Worker] - failed, error code = 0x%X\n",
  2416. dwReturn));
  2417. g_pInetSvc->LogEvent( W3_EVENT_JOB_QUEUE_FAILURE,
  2418. 0,
  2419. NULL,
  2420. dwReturn );
  2421. }
  2422. return dwReturn;
  2423. }
  2424. /*++
  2425. Routine Description:
  2426. Queue a work item to the job queue.
  2427. Arguments:
  2428. jqaAction The action to perform.
  2429. pwsiInstance The instance associated with this action.
  2430. pvParam The parameter to the work item.
  2431. fQueueAtTail If true, item will added to end of queue.
  2432. Returns:
  2433. ERROR_SUCCESS
  2434. ERROR_NOT_ENOUGH_MEMORY
  2435. --*/
  2436. DWORD
  2437. W3_JOB_QUEUE::QueueWorkItem_Worker( JOB_QUEUE_ACTION jqaAction,
  2438. PVOID pwsiInstance,
  2439. PVOID pvParam,
  2440. BOOL fQueueAtTail)
  2441. {
  2442. DWORD dwReturn = ERROR_SUCCESS;
  2443. PJOB_WORK_ITEM pjwiWorkItem;
  2444. pjwiWorkItem = (PJOB_WORK_ITEM)LocalAlloc( LMEM_FIXED, sizeof(JOB_WORK_ITEM) );
  2445. if (pjwiWorkItem == NULL) {
  2446. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  2447. }
  2448. else {
  2449. pjwiWorkItem->jqaAction = jqaAction;
  2450. pjwiWorkItem->pwsiInstance = pwsiInstance;
  2451. pjwiWorkItem->pvParam = pvParam;
  2452. LockThis();
  2453. if (fQueueAtTail) {
  2454. InsertTailList( &m_leJobQueue, &(pjwiWorkItem->ListEntry) );
  2455. }
  2456. else {
  2457. InsertHeadList( &m_leJobQueue, &(pjwiWorkItem->ListEntry) );
  2458. }
  2459. UnlockThis();
  2460. SetEvent(m_hQueueEvent);
  2461. }
  2462. return dwReturn;
  2463. }
  2464. /*++
  2465. Routine Description:
  2466. Stub routine to pass into ScheduleWorkItem, to handle interval change.
  2467. Arguments:
  2468. pContext W3_SERVER_INSTANCE Pointer.
  2469. Return Value:
  2470. Notes:
  2471. --*/
  2472. VOID
  2473. DeferredJobResetInterval(
  2474. VOID * pContext
  2475. )
  2476. {
  2477. ((W3_SERVER_INSTANCE *)pContext)->JobResetInterval();
  2478. }
  2479. /*++
  2480. Routine Description:
  2481. Stub routine to pass into ScheduleWorkItem, to do periodic logging.
  2482. Arguments:
  2483. pContext W3_SERVER_INSTANCE Pointer.
  2484. Return Value:
  2485. Notes:
  2486. --*/
  2487. VOID
  2488. DeferredQueryAndLogJobInfo(
  2489. VOID * pContext
  2490. )
  2491. {
  2492. ((W3_SERVER_INSTANCE *)pContext)->QueryAndLogJobInfo(JOLE_PERIODIC_LOG, FALSE);
  2493. }
  2494. /*++
  2495. Routine Description:
  2496. Restart Wams if they have been job enabled or disabled.
  2497. Arguments:
  2498. pContext LPSTR path where metadata was changed.
  2499. Return Value:
  2500. Notes:
  2501. --*/
  2502. VOID
  2503. DeferredRestartChangedApplications(VOID * pContext)
  2504. {
  2505. BUFFER bufDataPaths;
  2506. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  2507. LPSTR pszCurrentPath;
  2508. STR strPath;
  2509. LPSTR pszMDPath = (LPSTR) pContext;
  2510. DBG_ASSERT(pszMDPath != NULL);
  2511. IF_DEBUG( JOB_OBJECTS )
  2512. {
  2513. DBGPRINTF((DBG_CONTEXT,
  2514. "[DeferredRestartChangedApplications] \n"
  2515. "Restarting changed applications under path %s\n",
  2516. pszMDPath ));
  2517. }
  2518. //
  2519. // Get rid of trailing /
  2520. //
  2521. DBG_ASSERT(pszMDPath[strlen(pszMDPath) - 1] == '/');
  2522. pszMDPath[strlen(pszMDPath) - 1] = '\0';
  2523. if ( mb.Open( pszMDPath,
  2524. METADATA_PERMISSION_READ ) )
  2525. {
  2526. //
  2527. // First find the OOP Applications
  2528. //
  2529. if (mb.GetDataPaths(NULL,
  2530. MD_APP_PACKAGE_ID,
  2531. STRING_METADATA,
  2532. &bufDataPaths))
  2533. {
  2534. //
  2535. // Close metabase in case an application needs to use it.
  2536. // The destructor will close this if not closed, so ok
  2537. // to close in if statement.
  2538. //
  2539. mb.Close();
  2540. //
  2541. // For each OOP Application
  2542. //
  2543. for (pszCurrentPath = (LPSTR)bufDataPaths.QueryPtr();
  2544. *pszCurrentPath != '\0';
  2545. pszCurrentPath += (strlen(pszCurrentPath) + 1))
  2546. {
  2547. if (strPath.Copy(pszMDPath))
  2548. {
  2549. if (strPath.Append(pszCurrentPath))
  2550. {
  2551. strPath.SetLen(strlen(strPath.QueryStr()) - 1);
  2552. g_pWamDictator->CPUUpdateWamInfo(&strPath);
  2553. }
  2554. }
  2555. }
  2556. }
  2557. }
  2558. delete pContext;
  2559. }