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.

1784 lines
57 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1993.
  5. //
  6. // File: procssr.cxx
  7. //
  8. // Contents: CJobProcessor class implementation.
  9. //
  10. // Classes: CJobProcessor
  11. //
  12. // Functions: None.
  13. //
  14. // History: 25-Oct-95 MarkBl Created
  15. // 11/16/00 Dgrube remove (dwRet >= WAIT_OBJECT_0) &&
  16. // from "else if ((dwRet >= WAIT_OBJECT_0) && (dwRet < WAIT_ABANDONED_0))"
  17. // since dwRet is a DWORD. It would never occur and
  18. // is causing compile errors.
  19. //
  20. //-----------------------------------------------------------------------------
  21. //
  22. #include "..\pch\headers.hxx"
  23. #pragma hdrstop
  24. #include "globals.hxx"
  25. #include "svc_core.hxx"
  26. #include "..\inc\resource.h"
  27. // something to differentiate returns from RunDll32 from our returns
  28. #define ERROR_LEVEL_OFFSET 100
  29. // Parameters to CloseWindowEnumProc and ThreadWindowEnumProc
  30. struct ENUMPROCPARMS
  31. {
  32. DWORD dwProcessId; // IN - pid of the process being closed
  33. BOOL fWindowFound; // OUT - whether WM_CLOSE was sent to any window
  34. };
  35. BOOL CALLBACK CloseWindowEnumProc(HWND, LPARAM);
  36. BOOL CALLBACK ThreadWindowEnumProc(HWND, LPARAM);
  37. BOOL CloseWindowForProcess(CRun* pRun);
  38. //
  39. // Notes on the use of CRun's m_dwMaxRunTime and m_ftKill fields:
  40. //
  41. // m_ftKill is the time when the job processor thread monitoring the
  42. // job should try to kill the job, if it hasn't already terminated.
  43. // It is an absolute time. It is computed when the job is launched, based
  44. // on a combination of (1) the duration-end of triggers that have
  45. // TASK_TRIGGER_FLAG_KILL_AT_DURATION_END set and (2) the MaxRunTime set on
  46. // the job itself.
  47. // (1) can be predicted before the job runs, so it is calculated in
  48. // GetTriggerRunTimes() and stored in m_ftKill.
  49. // (2) is a relative time, so in many cases its end time cannot be
  50. // predicted until the job runs. It is temporarily stored in m_dwMaxRunTime
  51. // when the CRun object is created; but it is converted to an absolute time
  52. // and combined with m_ftKill when the job is launched in RunJobs().
  53. //
  54. // Once the job is launched, m_ftKill remains the same for the lifetime of
  55. // the CRun object, even if the job is killed because of
  56. // TASK_FLAG_KILL_ON_IDLE_END and restarted because of
  57. // TASK_FLAG_RESTART_ON_IDLE_RESUME.
  58. //
  59. // m_dwMaxRunTime is the remaining number of milliseconds that the
  60. // CJobProcessor::PerformTask() thread will wait for the job to terminate.
  61. // It is initialized to (m_ftKill - current time) in CJobProcessor::
  62. // SubmitJobs() and repeatedly adjusted downwards each time the job processor
  63. // thread wakes up. If the job is killed and restarted, m_dwMaxRunTime is
  64. // recomputed from the original m_ftKill and the new current time.
  65. //
  66. //+---------------------------------------------------------------------------
  67. //
  68. // Member: CJobProcessor::~CJobProcessor
  69. //
  70. // Synopsis: Job processor destructor. This object is reference counted,
  71. // ala class CTask inheritance. As a result, we are guaranteed
  72. // all of this is safe to do, as no outstanding references
  73. // remain.
  74. //
  75. // Arguments: N/A
  76. //
  77. // Notes: None.
  78. //
  79. //----------------------------------------------------------------------------
  80. CJobProcessor::~CJobProcessor()
  81. {
  82. TRACE3(CJobProcessor, ~CJobProcessor);
  83. if (_rgHandles != NULL)
  84. {
  85. //
  86. // Close the processor notification event handle & delete the handle
  87. // array.
  88. //
  89. if (_rgHandles)
  90. {
  91. if (_rgHandles[0])
  92. CloseHandle(_rgHandles[0]);
  93. delete _rgHandles;
  94. _rgHandles = NULL;
  95. }
  96. }
  97. DeleteCriticalSection(&_csProcessorCritSection);
  98. }
  99. //+---------------------------------------------------------------------------
  100. //
  101. // Member: CJobProcessor::Initialize
  102. //
  103. // Synopsis: Perform the initialization steps that would have otherwise
  104. // been performed in the constructor. This method enables return
  105. // of a status code if initialization should fail.
  106. //
  107. // Arguments: None.
  108. //
  109. // Notes: None.
  110. //
  111. //----------------------------------------------------------------------------
  112. HRESULT
  113. CJobProcessor::Initialize(void)
  114. {
  115. TRACE3(CJobProcessor, Initialize);
  116. HRESULT hr;
  117. schAssert(_rgHandles == NULL);
  118. //
  119. // Create the handle array with the processor notification event handle
  120. // as the sole element.
  121. //
  122. _rgHandles = new HANDLE[1];
  123. if (_rgHandles == NULL)
  124. {
  125. CHECK_HRESULT(E_OUTOFMEMORY);
  126. return(E_OUTOFMEMORY);
  127. }
  128. //
  129. // Create the processor notification event and assign its handle to the
  130. // handle array.
  131. //
  132. _rgHandles[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
  133. if (_rgHandles[0] == NULL)
  134. {
  135. hr = HRESULT_FROM_WIN32(GetLastError());
  136. CHECK_HRESULT(hr);
  137. return(hr);
  138. }
  139. //
  140. // Request a thread to service this object.
  141. //
  142. hr = RequestService(this);
  143. if (SUCCEEDED(hr))
  144. {
  145. this->InService();
  146. }
  147. return(hr);
  148. }
  149. //+---------------------------------------------------------------------------
  150. //
  151. // Member: CJobProcessor::IsIdle
  152. //
  153. // Synopsis: This member is called during job processor pool garbage
  154. // collection to determine if this processor can be removed
  155. // from the pool. This method is problematic, but this is OK,
  156. // since the worst that can happen is this processor may
  157. // be removed from the pool prematurely, requiring use of a
  158. // additional, redundant job processor object.
  159. //
  160. // Arguments: None.
  161. //
  162. // Notes: None.
  163. //
  164. //----------------------------------------------------------------------------
  165. BOOL
  166. CJobProcessor::IsIdle(void)
  167. {
  168. TRACE3(CJobProcessor, IsIdle);
  169. if ((_RequestQueue.GetCount() + _ProcessingQueue.GetCount()) == 0)
  170. {
  171. return(this->GetReferenceCount() == 1 ? TRUE : FALSE);
  172. }
  173. return(FALSE);
  174. }
  175. //+---------------------------------------------------------------------------
  176. //
  177. // Member: CJobProcessor::Next
  178. //
  179. // Synopsis: Return the next processor this object refers to. The returned
  180. // object is AddRef()'d to reflect the new reference.
  181. //
  182. // Arguments: None.
  183. //
  184. // Notes: The processor pool is locked to ensure this thread is the
  185. // sole thread accessing the pool throughout this operation.
  186. //
  187. //----------------------------------------------------------------------------
  188. CJobProcessor *
  189. CJobProcessor::Next(void)
  190. {
  191. TRACE3(CJobProcessor, Next);
  192. gpJobProcessorMgr->LockProcessorPool();
  193. CJobProcessor * pjpNext = (CJobProcessor *)CDLink::Next();
  194. if (pjpNext != NULL)
  195. {
  196. pjpNext->AddRef();
  197. }
  198. gpJobProcessorMgr->UnlockProcessorPool();
  199. return(pjpNext);
  200. }
  201. //+---------------------------------------------------------------------------
  202. //
  203. // Member: CJobProcessor::Prev
  204. //
  205. // Synopsis: Return the previous processor this object refers to. The
  206. // returned object is AddRef()'d to reflect the new reference.
  207. //
  208. // Arguments: None.
  209. //
  210. // Notes: The processor pool is locked to ensure this thread is the
  211. // sole thread accessing the pool throughout this operation.
  212. //
  213. //----------------------------------------------------------------------------
  214. CJobProcessor *
  215. CJobProcessor::Prev(void)
  216. {
  217. TRACE3(CJobProcessor, Prev);
  218. gpJobProcessorMgr->LockProcessorPool();
  219. CJobProcessor * pjpPrev = (CJobProcessor *)CDLink::Prev();
  220. if (pjpPrev != NULL)
  221. {
  222. pjpPrev->AddRef();
  223. }
  224. gpJobProcessorMgr->UnlockProcessorPool();
  225. return(pjpPrev);
  226. }
  227. //+---------------------------------------------------------------------------
  228. //
  229. // Member: CJobProcessor::PerformTask
  230. //
  231. // Synopsis: This is the function performed by the worker thread on the
  232. // job processor. The processor thread enters a wait on the array
  233. // of handles in the private data member, _rgHandles. The first
  234. // array element is a handle to the processor notification event.
  235. // This event is signals this thread that new jobs have been sub-
  236. // mitted to this processor. The remaining n-1 handles in the
  237. // array are job process handles signaled on job completion.
  238. // When a job completes, the persisted job object is updated with
  239. // the job's exit status code, completion time, etc.
  240. //
  241. // It's possible the wait for one or more jobs may time out. If
  242. // the processor notification event wait times out, the wait is
  243. // re-entered. If a job times out, its handle is removed from
  244. // wait handle array and the job's job info object removed from
  245. // the processing queue.
  246. //
  247. // Arguments: None.
  248. //
  249. // Notes: None.
  250. //
  251. //----------------------------------------------------------------------------
  252. void
  253. CJobProcessor::PerformTask(void)
  254. {
  255. #define CLOSE_WAIT_TIME (3 * 60 * 1000) // 3 mins (milliseconds).
  256. #define WAIT_TIME_DEFAULT (10 * 3 * 60 * 1000) // 30 mins (milliseconds).
  257. TRACE3(CJobProcessor, PerformTask);
  258. CRun * pRun;
  259. DWORD dwObjectIndex;
  260. //
  261. // Initialize this thread's keep-awake count.
  262. //
  263. InitThreadWakeCount();
  264. for (;;)
  265. {
  266. //
  267. // Wait for job completion, timeout, or processor notification.
  268. //
  269. // NB : ProcessQueue count + 1 since there is no processing queue
  270. // entry for the first handle, the new submission event
  271. // handle.
  272. //
  273. // There will never be a discrepancy between the processing
  274. // queue count and the actual number of handles in _rgHandles
  275. // since this thread exclusively updates the processing queue.
  276. //
  277. DWORD dwTimeoutInterval = WAIT_TIME_DEFAULT;
  278. DWORD cHandles = _ProcessingQueue.GetCount() + 1;
  279. if (cHandles > 1)
  280. {
  281. //
  282. // There are jobs to process.
  283. //
  284. // Scan job info objects in the processor queue for the minimum
  285. // value of the job's maximum run time. This will be the wait
  286. // time on WaitForMultipleObjects.
  287. //
  288. for (pRun = _ProcessingQueue.GetFirstElement();
  289. pRun != NULL;
  290. pRun = pRun->Next())
  291. {
  292. schDebugOut((DEB_USER3,
  293. "PerformTask(0x%x) Job " FMT_TSTR " remaining time %u ms\n",
  294. this,
  295. pRun->GetName(),
  296. pRun->GetMaxRunTime()));
  297. dwTimeoutInterval = min(dwTimeoutInterval,
  298. pRun->GetMaxRunTime());
  299. }
  300. }
  301. schDebugOut((DEB_USER3,
  302. "PerformTask(0x%x) Processor entering wait; p queue cnt(%d); " \
  303. "wait time %u ms\n",
  304. this,
  305. cHandles - 1,
  306. dwTimeoutInterval));
  307. DWORD dwWaitTime = GetTickCount();
  308. DWORD dwRet = WaitForMultipleObjects(cHandles,
  309. _rgHandles,
  310. FALSE,
  311. dwTimeoutInterval);
  312. //
  313. // Serialize processor data structure access.
  314. //
  315. EnterCriticalSection(&_csProcessorCritSection);
  316. //
  317. // (Note that GetTickCount() wrap is automatically taken care of
  318. // by 2's-complement subtraction.)
  319. //
  320. dwWaitTime = GetTickCount() - dwWaitTime;
  321. schDebugOut((DEB_USER3,
  322. "PerformTask(0x%x) Processor awake after %u ms\n",
  323. this,
  324. dwWaitTime));
  325. //
  326. // Decrement each job's max run time by the amount of time waited.
  327. // Skip jobs with zeroed max run time values.
  328. //
  329. for (pRun = _ProcessingQueue.GetFirstElement();
  330. pRun != NULL;
  331. pRun = pRun->Next())
  332. {
  333. //
  334. // NB : Jobs with infinite run times do not expire. Therefore, do
  335. // not decrease the max run time value.
  336. //
  337. if (pRun->GetMaxRunTime() != 0 &&
  338. pRun->GetMaxRunTime() != INFINITE)
  339. {
  340. if (pRun->GetMaxRunTime() > dwWaitTime)
  341. {
  342. pRun->SetMaxRunTime(pRun->GetMaxRunTime() - dwWaitTime);
  343. }
  344. else
  345. {
  346. pRun->SetMaxRunTime(0);
  347. }
  348. }
  349. }
  350. if (dwRet == WAIT_FAILED)
  351. {
  352. //
  353. // Wait attempt failed. Shutdown the processor & bail out.
  354. //
  355. // BUGBUG : Should probably log this.
  356. //
  357. schDebugOut((DEB_ERROR,
  358. "PerformTask(0x%x) Wait failure(0x%x) - processor " \
  359. "shutdown initiated\n",
  360. this,
  361. HRESULT_FROM_WIN32(GetLastError())));
  362. this->_Shutdown();
  363. LeaveCriticalSection(&_csProcessorCritSection);
  364. break;
  365. }
  366. if (dwRet == WAIT_TIMEOUT)
  367. {
  368. if (!_ProcessingQueue.GetCount() && !_RequestQueue.GetCount())
  369. {
  370. //
  371. // Shutdown this processor. The wait has expired and no jobs
  372. // are in service, nor are there new requests queued.
  373. //
  374. schDebugOut((DEB_TRACE,
  375. "PerformTask(0x%x) Processor idle - shutdown " \
  376. "initiated\n",
  377. this));
  378. this->_Shutdown();
  379. LeaveCriticalSection(&_csProcessorCritSection);
  380. break;
  381. }
  382. //
  383. // One or more jobs timed out (those with max run time values of
  384. // zero). Close associated event handle, overwrite event handle
  385. // array entry, then remove and destroy the associated job info
  386. // object from the processor queue.
  387. //
  388. schDebugOut((DEB_USER3,
  389. "PerformTask(0x%x) Wait timeout\n",
  390. this));
  391. CRun * pRunNext;
  392. DWORD i;
  393. for (pRun = _ProcessingQueue.GetFirstElement(), i = 1;
  394. pRun != NULL;
  395. pRun = pRunNext, i++)
  396. {
  397. pRunNext = pRun->Next();
  398. if (pRun->GetMaxRunTime() != 0)
  399. {
  400. continue;
  401. }
  402. //
  403. // Post a WM_CLOSE message to the job if this is the
  404. // first attempt at closure. If WM_CLOSE was issued
  405. // previously and the job is still running, resort to
  406. // TerminateProcess.
  407. //
  408. if (!(pRun->IsFlagSet(RUN_STATUS_CLOSE_PENDING)))
  409. {
  410. pRun->SetFlag(RUN_STATUS_TIMED_OUT);
  411. schDebugOut((DEB_ITRACE,
  412. "PerformTask(0x%x) Forced closure; issuing " \
  413. "WM_CLOSE to job " FMT_TSTR "\n",
  414. this,
  415. pRun->GetName()));
  416. //
  417. // Log job closure, post WM_CLOSE, then re-enter the
  418. // wait for closure.
  419. //
  420. SYSTEMTIME stFinished;
  421. GetLocalTime(&stFinished);
  422. g_pSched->JobPostProcessing(pRun, stFinished);
  423. /*******
  424. // Attach to the correct desktop prior to enumerating
  425. // the windows
  426. //
  427. HWINSTA hwinstaSave = NULL;
  428. HDESK hdeskSave = NULL;
  429. HWINSTA hwinsta = NULL;
  430. DWORD dwTreadId = GetCurrentThreadId( );
  431. if( NULL == dwTreadId )
  432. {
  433. schDebugOut((DEB_ERROR,
  434. "CJobProcessor::PerformTask, GetCurrentThreadId " ));
  435. }
  436. else
  437. {
  438. hdeskSave = GetThreadDesktop( dwTreadId );
  439. }
  440. if( NULL == hdeskSave )
  441. {
  442. schDebugOut((DEB_ERROR,
  443. "CJobProcessor::PerformTask, GetThreadDesktop " ));
  444. }
  445. else
  446. {
  447. hwinstaSave = GetProcessWindowStation( );
  448. }
  449. if( NULL == hwinstaSave )
  450. {
  451. schDebugOut((DEB_ERROR,
  452. "CJobProcessor::PerformTask, GetProcessWindowStation " ));
  453. }
  454. hwinsta = OpenWindowStation(
  455. pRun->GetStation( ),
  456. TRUE,
  457. MAXIMUM_ALLOWED );
  458. if( NULL == hwinsta )
  459. {
  460. schDebugOut((DEB_ERROR,
  461. "CJobProcessor::PerformTask, OpenWindowStation " ));
  462. }
  463. else if( !SetProcessWindowStation( hwinsta ) )
  464. {
  465. schDebugOut((DEB_ERROR,
  466. "CJobProcessor::PerformTask, SetProcessWindowStation " ));
  467. }
  468. HDESK hDesk = OpenDesktop(
  469. pRun->GetDesktop(),
  470. 0, //No hooks allowed
  471. TRUE, //No inheritance
  472. MAXIMUM_ALLOWED
  473. );
  474. if( !SetThreadDesktop( hDesk ) )
  475. {
  476. schDebugOut((DEB_ERROR,
  477. "CJobProcessor::PerformTask, OpenDesktop failed, " \
  478. "status = 0x%lx\n",
  479. GetLastError()));
  480. }
  481. else
  482. {
  483. // Success enumerate windows else SetMaxRunTime to 0
  484. // and ultimately kill the process (not very graceful)
  485. //
  486. EnumWindows(CloseWindowEnumProc, (LPARAM) &Parms);
  487. }
  488. ********************/
  489. pRun->SetFlag(RUN_STATUS_CLOSE_PENDING);
  490. BOOL fFoundWindow = CloseWindowForProcess(pRun);
  491. if (fFoundWindow)
  492. {
  493. pRun->SetMaxRunTime(CLOSE_WAIT_TIME);
  494. }
  495. else
  496. {
  497. schDebugOut((DEB_ITRACE, "PerformTask: no windows found\n"));
  498. //
  499. // If WM_CLOSE was not sent to any windows, there is no
  500. // point waiting for the job to terminate.
  501. // DCR: It would be polite, and perhaps help the app to
  502. // avoid data loss (depending on the app), to send some other
  503. // notification, such as a CTRL_C_EVENT. See bug 65251.
  504. //
  505. pRun->SetMaxRunTime(0);
  506. }
  507. }
  508. else
  509. {
  510. schDebugOut((DEB_ITRACE,
  511. "PerformTask(0x%x) 2nd forced closure; issuing " \
  512. "TerminateProcess on job " FMT_TSTR "\n",
  513. this,
  514. pRun->GetName()));
  515. DWORD dwExitCode = 0;
  516. GetExitCodeProcess(pRun->GetHandle(), &dwExitCode);
  517. if (dwExitCode == STILL_ACTIVE)
  518. {
  519. TerminateProcess(pRun->GetHandle(), (UINT)-1);
  520. }
  521. if (i < _ProcessingQueue.GetCount()) // Ignore last
  522. // entry.
  523. {
  524. CopyMemory(&_rgHandles[i],
  525. &_rgHandles[i + 1],
  526. sizeof(HANDLE) *
  527. (_ProcessingQueue.GetCount() - i));
  528. }
  529. i--; // Reflect overwritten array entry.
  530. //
  531. // Remove CRun object from the processing queue
  532. // and destroy it.
  533. //
  534. _ProcessingQueue.RemoveElement(pRun);
  535. if (pRun->IsFlagSet(TASK_FLAG_SYSTEM_REQUIRED))
  536. {
  537. //
  538. // This thread is monitoring one less system-
  539. // required job
  540. //
  541. WrapSetThreadExecutionState(FALSE,
  542. "processor - forced close of task");
  543. }
  544. if (pRun->IsFlagSet(RUN_STATUS_RESTART_ON_IDLE_RESUME)
  545. && pRun->GetWait() > 0)
  546. {
  547. //
  548. // Ask the main thread to move it back into the
  549. // idle wait queue
  550. //
  551. pRun->ClearFlag(JOB_INTERNAL_FLAG_MASK);
  552. pRun->SetMaxRunTime(INFINITE);
  553. g_pSched->SubmitIdleRun(pRun);
  554. //
  555. // Note that we changed (reduced) pRun's MaxRunTime
  556. // when we killed it. However we didn't mess with
  557. // the kill time. The MaxRunTime will be recomputed
  558. // based on the same kill time as before when this
  559. // run is next submitted to a processor.
  560. //
  561. }
  562. else
  563. {
  564. delete pRun;
  565. }
  566. }
  567. }
  568. }
  569. else if (dwRet < WAIT_ABANDONED_0)
  570. {
  571. //
  572. // One or more jobs completed.
  573. //
  574. dwObjectIndex = dwRet - WAIT_OBJECT_0;
  575. if (dwObjectIndex == 0)
  576. {
  577. //
  578. // Processor notification event signaled. Either new jobs
  579. // have been submitted or the service is stopping.
  580. //
  581. if (IsServiceStopping())
  582. {
  583. //
  584. // Service stop. Shutdown the processor.
  585. //
  586. schDebugOut((DEB_TRACE,
  587. "PerformTask(0x%x) Service stop - processor " \
  588. "shutdown initiated\n",
  589. this));
  590. this->_Shutdown();
  591. LeaveCriticalSection(&_csProcessorCritSection);
  592. break;
  593. }
  594. ResetEvent(_rgHandles[0]);
  595. //
  596. // Move jobs from request to processing queue.
  597. //
  598. _ProcessRequests();
  599. //
  600. // Unblock the thread that called SubmitJobs().
  601. // (We happen to know it's the thread in the main service
  602. // loop so we can use the global event. A cleaner model
  603. // would be to either pass the handle of the event to
  604. // SubmitJobs, or use an event private to SubmitJobs and
  605. // PerformTask.)
  606. //
  607. g_pSched->Unblock();
  608. }
  609. else if (dwObjectIndex < cHandles)
  610. {
  611. //
  612. // A job has finished (or closed).
  613. // Find the CRun object associated with the handle.
  614. //
  615. if ((pRun = _ProcessingQueue.FindJob(
  616. _rgHandles[dwObjectIndex])) != NULL)
  617. {
  618. pRun->ClearFlag(RUN_STATUS_RUNNING);
  619. if (!(pRun->GetFlags() & RUN_STATUS_CLOSE_PENDING))
  620. {
  621. schDebugOut((DEB_USER3,
  622. "PerformTask(0x%x) Job " FMT_TSTR " completed\n",
  623. this,
  624. pRun->GetName()));
  625. //
  626. // The job finished on its own. Log completion
  627. // status. Fetch job completion time for pending log
  628. // entry.
  629. //
  630. pRun->SetFlag(RUN_STATUS_FINISHED);
  631. SYSTEMTIME stFinished;
  632. GetLocalTime(&stFinished);
  633. //
  634. // Standard job post processing.
  635. //
  636. g_pSched->JobPostProcessing(pRun, stFinished);
  637. }
  638. else
  639. {
  640. // (NOTE: This may not be necessary - this info
  641. // is not used yet.)
  642. //
  643. pRun->SetFlag(RUN_STATUS_CLOSED);
  644. }
  645. //
  646. // Fix up handle array to reflect processed entry.
  647. //
  648. if (dwObjectIndex < _ProcessingQueue.GetCount())
  649. {
  650. CopyMemory(&_rgHandles[dwObjectIndex],
  651. &_rgHandles[dwObjectIndex + 1],
  652. sizeof(HANDLE) *
  653. (_ProcessingQueue.GetCount() - dwObjectIndex));
  654. }
  655. //
  656. // Remove CRun object from the processing queue and
  657. // destroy it.
  658. //
  659. _ProcessingQueue.RemoveElement(pRun);
  660. if (pRun->IsFlagSet(TASK_FLAG_SYSTEM_REQUIRED))
  661. {
  662. //
  663. // This thread is monitoring one less system-
  664. // required job
  665. //
  666. WrapSetThreadExecutionState(FALSE,
  667. "processor - last task exited");
  668. }
  669. if (pRun->IsFlagSet(RUN_STATUS_CLOSE_PENDING) &&
  670. pRun->IsFlagSet(RUN_STATUS_RESTART_ON_IDLE_RESUME) &&
  671. pRun->GetWait() > 0)
  672. {
  673. //
  674. // Ask the main thread to move it back into the
  675. // idle wait queue
  676. //
  677. pRun->ClearFlag(JOB_INTERNAL_FLAG_MASK);
  678. pRun->SetMaxRunTime(INFINITE);
  679. g_pSched->SubmitIdleRun(pRun);
  680. //
  681. // Note that we changed (reduced) pRun's MaxRunTime
  682. // when we killed it. However we didn't mess with
  683. // the kill time. The MaxRunTime will be reset to
  684. // match the same kill time as before when this run
  685. // is next submitted to a processor.
  686. //
  687. }
  688. else
  689. {
  690. delete pRun;
  691. }
  692. }
  693. }
  694. else
  695. {
  696. //
  697. // Index out of range. This should never happen.
  698. //
  699. schDebugOut((DEB_ERROR,
  700. "PerformTask(0x%x) Wait array index (%d) out of " \
  701. "range! Handle count(%d)\n",
  702. this,
  703. dwObjectIndex,
  704. cHandles));
  705. schAssert(0);
  706. LeaveCriticalSection(&_csProcessorCritSection);
  707. continue;
  708. }
  709. }
  710. else
  711. {
  712. //
  713. // Clueless how we got here. Just continue the wait.
  714. //
  715. schAssert(!"How did this branch get evaluated?");
  716. LeaveCriticalSection(&_csProcessorCritSection);
  717. continue;
  718. }
  719. LeaveCriticalSection(&_csProcessorCritSection);
  720. }
  721. }
  722. //+---------------------------------------------------------------------------
  723. //
  724. // Function: CloseWindowForProcess
  725. //
  726. // Synopsis: launches separate process to issue WM_CLOSE to main window
  727. //
  728. // Arguments: CRun for the job of interest
  729. //
  730. // Return value: true if a window was found.
  731. //
  732. // Notes:
  733. //
  734. //----------------------------------------------------------------------------
  735. BOOL CloseWindowForProcess(CRun* pRun)
  736. {
  737. // processID == 0 means we can't find / don't have a proc id
  738. if (pRun->GetProcessId() == 0)
  739. return FALSE;
  740. BOOL fWindowClosed = false;
  741. // okay - it might not be the default desktop, but is the best guess we have
  742. // if we guess wrong, no harm done - the PID is unique & we won't close the wrong proc
  743. BOOL fIsDefaultWinsta = (NULL == pRun->GetStation()) || (NULL == pRun->GetDesktop());
  744. // create process in proper winsta
  745. // process is rundll32 to invoke CloseProc
  746. WCHAR dllPath[MAX_PATH +2];
  747. WCHAR runDllPath[MAX_PATH +2];
  748. BOOL bLaunched = false;
  749. HANDLE hProc = INVALID_HANDLE_VALUE;
  750. if (ExpandEnvironmentStringsW(L"%windir%\\system32\\rundll32.exe", runDllPath, MAX_PATH +1) &&
  751. GetModuleFileNameW(g_hInstance, dllPath, MAX_PATH +1))
  752. {
  753. WCHAR templitt[] = L"%s %s,CloseProc %u";
  754. size_t cchCommandLine = wcslen(runDllPath) + wcslen(dllPath) + wcslen(templitt) + 20;
  755. WCHAR* pCommandLine = new WCHAR[cchCommandLine];
  756. size_t cchDesktop;
  757. WCHAR* pDesktop = NULL;
  758. if (fIsDefaultWinsta)
  759. pDesktop = L"WinSta0\\Default";
  760. else
  761. {
  762. cchDesktop = wcslen(pRun->GetStation()) + wcslen(pRun->GetDesktop()) + 5;
  763. pDesktop = new WCHAR[cchDesktop];
  764. }
  765. if (pCommandLine && pDesktop)
  766. {
  767. StringCchPrintf(pCommandLine, cchCommandLine, templitt, runDllPath, dllPath, pRun->GetProcessId());
  768. if (!fIsDefaultWinsta)
  769. {
  770. StringCchCopy(pDesktop, cchDesktop, pRun->GetStation());
  771. StringCchCat(pDesktop, cchDesktop, L"\\");
  772. StringCchCat(pDesktop, cchDesktop, pRun->GetDesktop());
  773. }
  774. // else - pDesktop was init'd above
  775. PROCESS_INFORMATION procInfo;
  776. STARTUPINFO startInfo;
  777. SecureZeroMemory(&startInfo, sizeof(startInfo));
  778. startInfo.lpDesktop = pDesktop;
  779. startInfo.cb = sizeof(STARTUPINFO);
  780. bLaunched = CreateProcessW(NULL, pCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS,
  781. NULL, NULL, &startInfo, &procInfo);
  782. hProc = procInfo.hProcess;
  783. }
  784. // if fIsDefaultWinsta, then pDesktop is pointing at static memory
  785. if (pDesktop && !fIsDefaultWinsta) delete[] pDesktop;
  786. if (pCommandLine) delete[] pCommandLine;
  787. }
  788. if (bLaunched)
  789. {
  790. if (WaitForSingleObject(hProc, 60000) == WAIT_OBJECT_0)
  791. {
  792. DWORD exitCode;
  793. if (GetExitCodeProcess(hProc, &exitCode))
  794. fWindowClosed = exitCode - ERROR_LEVEL_OFFSET;
  795. }
  796. CloseHandle(hProc);
  797. }
  798. return fWindowClosed;
  799. }
  800. //+---------------------------------------------------------------------------
  801. //
  802. // Function: CloseProcEx
  803. //
  804. // Synopsis: Entry point used with RunDll32
  805. // closes down window via WM_CLOSE
  806. //
  807. // Arguments: [hwnd] -- ignored
  808. // [hinst] -- uninteresting
  809. // [nCmdShow] -- boring
  810. // [lpszCmdLine] -- command line from invocation
  811. //
  812. // Notes: command line should be proc id.
  813. // CloseProc in main is a straight passthrough to this one.
  814. //
  815. //----------------------------------------------------------------------------
  816. extern "C" void CALLBACK CloseProcEx(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
  817. {
  818. BOOL fFoundWindow = FALSE;
  819. ENUMPROCPARMS params;
  820. params.fWindowFound = 0;
  821. if (lpszCmdLine && strlen(lpszCmdLine))
  822. {
  823. if (sscanf(lpszCmdLine, "%u", &params.dwProcessId)) // SEC:REVIEWED 2002-04-30
  824. // this function does not involve an unbounded string copy
  825. {
  826. EnumWindows(CloseWindowEnumProc, (LPARAM) &params);
  827. fFoundWindow = params.fWindowFound;
  828. }
  829. }
  830. ExitProcess(fFoundWindow +ERROR_LEVEL_OFFSET);
  831. }
  832. //+---------------------------------------------------------------------------
  833. //
  834. // Function: CloseWindowEnumProc
  835. //
  836. // Synopsis:
  837. //
  838. // Arguments: [hWnd] --
  839. // [lParam] --
  840. //
  841. // Notes:
  842. //
  843. //----------------------------------------------------------------------------
  844. BOOL CALLBACK
  845. CloseWindowEnumProc(HWND hWnd, LPARAM lParam)
  846. {
  847. DWORD dwProcessId, dwThreadId;
  848. ENUMPROCPARMS * pParms = (ENUMPROCPARMS *) lParam;
  849. dwThreadId = GetWindowThreadProcessId(hWnd, &dwProcessId);
  850. if (dwProcessId == pParms->dwProcessId)
  851. {
  852. //
  853. // Enumerate and close each owned, non-child window. This will close
  854. // open dialogs along with the main window(s).
  855. //
  856. DWORD dwErr;
  857. if (!EnumThreadWindows(dwThreadId, ThreadWindowEnumProc, lParam))
  858. dwErr = GetLastError();
  859. // some processes, e.g. cmd.exe don't get enumerated by EnumThreadWindows
  860. // if so, we'll try one close message to the top window.
  861. if ((pParms->fWindowFound == false) && IsWindow(hWnd))
  862. {
  863. PostMessage(hWnd, WM_CLOSE, 0, 0);
  864. pParms->fWindowFound = true;
  865. }
  866. return FALSE;
  867. }
  868. return(TRUE);
  869. }
  870. //+---------------------------------------------------------------------------
  871. //
  872. // Function: ThreadWindowEnumProc
  873. //
  874. // Synopsis: Enumeration procedure.
  875. //
  876. // Arguments: [hWnd] -- The window handle.
  877. // [lParam] -- The process ID.
  878. //
  879. //----------------------------------------------------------------------------
  880. BOOL CALLBACK
  881. ThreadWindowEnumProc(HWND hWnd, LPARAM lParam)
  882. {
  883. DWORD dwProcessId;
  884. ENUMPROCPARMS * pParms = (ENUMPROCPARMS *) lParam;
  885. GetWindowThreadProcessId(hWnd, &dwProcessId);
  886. if (dwProcessId == pParms->dwProcessId)
  887. {
  888. //
  889. // Close any dialogs.
  890. //
  891. //
  892. // The most common dialog we are likely to see at this point is a
  893. // "save changes" dialog. First try to send no to close that dialog
  894. // and then try a cancel.
  895. //
  896. if( !PostMessage(hWnd, WM_COMMAND, 0, MAKEWPARAM(IDNO, 0)) )
  897. {
  898. schDebugOut((DEB_ERROR,
  899. "CJobProcessor::PerformTask - ThreadWindowEnumProc, PMsg1, " \
  900. "status = 0x%lx\n",
  901. GetLastError()));
  902. }
  903. if( !PostMessage(hWnd, WM_COMMAND, 0, MAKEWPARAM(IDCANCEL, 0)) )
  904. {
  905. schDebugOut((DEB_ERROR,
  906. "CJobProcessor::PerformTask - ThreadWindowEnumProc, PMsg2, " \
  907. "status = 0x%lx\n",
  908. GetLastError()));
  909. }
  910. //
  911. // Close any non-child windows.
  912. //
  913. if( !PostMessage(hWnd, WM_CLOSE, 0, 0) )
  914. {
  915. schDebugOut((DEB_ERROR,
  916. "CJobProcessor::PerformTask - ThreadWindowEnumProc, PMsg3, " \
  917. "status = 0x%lx\n",
  918. GetLastError()));
  919. }
  920. //
  921. // Tell the calling function that we found a matching window.
  922. //
  923. pParms->fWindowFound = TRUE;
  924. }
  925. return TRUE;
  926. }
  927. //+---------------------------------------------------------------------------
  928. //
  929. // Member: CJobProcessor::SubmitJobs
  930. //
  931. // Synopsis: This method is used to submit new jobs to this processor.
  932. //
  933. // Each processor can handle a maximum of (MAXIMUM_WAIT_OBJECTS
  934. // - 1) jobs (from the WaitForMultipleObjects constraint of
  935. // at most MAXIMUM_WAIT_OBJECTS). Subject to processor capacity,
  936. // all, or a subset of the jobs passed may be taken.
  937. //
  938. // Arguments: [pRunList] -- Submitted job linked list object. Jobs taken are
  939. // transferred from this list to a private one.
  940. //
  941. // Returns: S_OK -- No submissions taken (as a result of a normal
  942. // condition, such as the job processor already full,
  943. // or the job processor shutting down).
  944. // S_SCHED_JOBS_ACCEPTED -- Some submissions taken.
  945. // On return, GetFirstJob() will return NULL if all
  946. // submissions were taken.
  947. // S_FALSE -- The service is shutting down. Call Shutdown()
  948. // on this processor immediately after this return
  949. // code. Submissions were likely taken, but they will
  950. // not execute.
  951. // HRESULT -- On error.
  952. //
  953. // Notes: None.
  954. //
  955. //----------------------------------------------------------------------------
  956. HRESULT
  957. CJobProcessor::SubmitJobs(CRunList * pRunList)
  958. {
  959. TRACE3(CJobProcessor, SubmitJobs);
  960. schAssert(pRunList != NULL);
  961. HRESULT hr = S_OK;
  962. BOOL fJobsAccepted = FALSE;
  963. schDebugOut((DEB_USER3,
  964. "SubmitJobs(0x%x) pRunList(0x%x)\n",
  965. this,
  966. pRunList));
  967. //
  968. // Serialize processor data structure access.
  969. //
  970. EnterCriticalSection(&_csProcessorCritSection);
  971. FILETIME ftNow = GetLocalTimeAsFileTime();
  972. schDebugOut((DEB_USER3, "SubmitJobs: Time now = %lx %lx\n",
  973. ftNow.dwLowDateTime, ftNow.dwHighDateTime));
  974. //
  975. // Add as many jobs as this processor will allow to the request queue.
  976. // See synopsis for details.
  977. //
  978. // NB : Adding one to the request/processing queue sum to reflect the new
  979. // processor notification event handle. For this handle array entry,
  980. // there is no processing queue entry.
  981. //
  982. CRun * pRun = pRunList->GetFirstJob();
  983. //
  984. // First, check if this processor is in the process of shutting down.
  985. // The data member, _rgHandles, is utilized as a flag to indicate this.
  986. // If it is NULL, this processor has shutdown and will take no more jobs.
  987. //
  988. if (_rgHandles != NULL)
  989. {
  990. while ( !pRun->IsNull() &&
  991. (MAXIMUM_WAIT_OBJECTS - (this->_RequestQueue.GetCount() +
  992. this->_ProcessingQueue.GetCount() +
  993. 1) ))
  994. {
  995. CRun * pRunNext = pRun->Next();
  996. pRun->UnLink();
  997. schDebugOut((DEB_USER3,
  998. "SubmitJobs: pRun(%#lx) (" FMT_TSTR ") KillTime = %lx %lx\n",
  999. pRun, pRun->GetName(), pRun->GetKillTime().dwLowDateTime,
  1000. pRun->GetKillTime().dwHighDateTime));
  1001. //
  1002. // Compute the max run time (the time we will wait for
  1003. // this job to complete) based on the kill time
  1004. //
  1005. DWORDLONG MaxRunTime;
  1006. if (FTto64(pRun->GetKillTime()) < FTto64(ftNow))
  1007. {
  1008. MaxRunTime = 0;
  1009. }
  1010. else
  1011. {
  1012. MaxRunTime = (FTto64(pRun->GetKillTime()) - FTto64(ftNow)) /
  1013. FILETIMES_PER_MILLISECOND;
  1014. MaxRunTime = min(MaxRunTime, MAXULONG);
  1015. }
  1016. pRun->SetMaxRunTime((DWORD) MaxRunTime);
  1017. schDebugOut((DEB_USER3, "SubmitJobs: MaxRunTime = %lu\n", MaxRunTime));
  1018. _RequestQueue.AddElement(pRun);
  1019. fJobsAccepted = TRUE;
  1020. pRun = pRunNext;
  1021. }
  1022. //
  1023. // Is there a thread servicing this object? If not, request one.
  1024. //
  1025. if (!this->IsInService())
  1026. {
  1027. //
  1028. // NB : A RequestService() return code of S_FALSE indicates the
  1029. // service is shutting down. Simply propagate this return
  1030. // code. It will then be the caller's responsibility to
  1031. // shut down this processor.
  1032. //
  1033. hr = RequestService(this);
  1034. if (SUCCEEDED(hr) && hr != S_FALSE)
  1035. {
  1036. this->InService();
  1037. }
  1038. }
  1039. //
  1040. // Set the processor notification event.
  1041. //
  1042. schDebugOut((DEB_USER3,
  1043. "CJobProcessor::SubmitJobs(0x%x) Signalling processor thread\n"));
  1044. //
  1045. // A NOP if RequestService() failed above.
  1046. //
  1047. SetEvent(_rgHandles[0]);
  1048. }
  1049. LeaveCriticalSection(&_csProcessorCritSection);
  1050. if (hr == S_OK && fJobsAccepted)
  1051. {
  1052. hr = S_SCHED_JOBS_ACCEPTED;
  1053. }
  1054. return(hr);
  1055. }
  1056. //+---------------------------------------------------------------------------
  1057. //
  1058. // Member: CJobProcessor::KillJob
  1059. //
  1060. // Synopsis: Kill all instances of the job indicated, if in service by
  1061. // this processor.
  1062. //
  1063. // Arguments: [ptszJobName] -- Job name.
  1064. //
  1065. // Returns: None.
  1066. //
  1067. // Notes: None.
  1068. //
  1069. //----------------------------------------------------------------------------
  1070. void
  1071. CJobProcessor::KillJob(LPTSTR ptszJobName)
  1072. {
  1073. TRACE(CJobProcessor, KillJob);
  1074. BOOL fContractInitiated = FALSE;
  1075. //
  1076. // Serialize processor data structure access.
  1077. //
  1078. EnterCriticalSection(&_csProcessorCritSection);
  1079. //
  1080. // Is the job serviced by this processor?
  1081. // Find associated job info object(s) in the processing queue.
  1082. //
  1083. // NB : Rarely, but it is possible there may be more than one instance.
  1084. //
  1085. CRun * pRun;
  1086. for (pRun = _ProcessingQueue.GetFirstElement(); pRun != NULL;
  1087. pRun = pRun->Next())
  1088. {
  1089. //
  1090. // The abort flag check addresses the case where more than one user
  1091. // simultaneously aborts the same job.
  1092. //
  1093. if (!lstrcmpi(ptszJobName, pRun->GetName()) &&
  1094. !(pRun->GetFlags() & RUN_STATUS_ABORTED))
  1095. {
  1096. //
  1097. // Set flags for immediate timeout and closure.
  1098. //
  1099. pRun->SetMaxRunTime(0);
  1100. pRun->SetFlag(RUN_STATUS_ABORTED);
  1101. fContractInitiated = TRUE;
  1102. }
  1103. }
  1104. if (fContractInitiated)
  1105. {
  1106. //
  1107. // This logic will induce the PerformTask thread to respond as
  1108. // follows:
  1109. // - The wait will unblock and the next wait time re-calculated;
  1110. // this value will be zero since the min value is taken.
  1111. // - The wait is re-entered and immediately times out.
  1112. // - Jobs with max run times of zero are closed in the
  1113. // WAIT_TIMEOUT condition. As a result, the jobs are killed.
  1114. //
  1115. SetEvent(_rgHandles[0]);
  1116. }
  1117. LeaveCriticalSection(&_csProcessorCritSection);
  1118. }
  1119. //+---------------------------------------------------------------------------
  1120. //
  1121. // Member: CJobProcessor::KillIfFlagSet
  1122. //
  1123. // Synopsis: Kill all jobs that have the passed in flag set, if in service
  1124. // by this processor.
  1125. //
  1126. // Arguments: [dwFlag] - Job flag value, one of TASK_FLAG_KILL_ON_IDLE_END
  1127. // or TASK_FLAG_KILL_IF_GOING_ON_BATTERIES.
  1128. // Returns: None.
  1129. //
  1130. // Notes: None.
  1131. //
  1132. //----------------------------------------------------------------------------
  1133. void
  1134. CJobProcessor::KillIfFlagSet(DWORD dwFlag)
  1135. {
  1136. TRACE(CJobProcessor, KillIfFlagSet);
  1137. BOOL fContractInitiated = FALSE;
  1138. //
  1139. // Serialize processor data structure access.
  1140. //
  1141. EnterCriticalSection(&_csProcessorCritSection);
  1142. //
  1143. // Is the job serviced by this processor?
  1144. // Find associated job info object(s) in the processing queue.
  1145. //
  1146. CRun * pRun;
  1147. for (pRun = _ProcessingQueue.GetFirstElement(); pRun != NULL;
  1148. pRun = pRun->Next())
  1149. {
  1150. //
  1151. // The abort flag check addresses the case where more than one user
  1152. // simultaneously aborts the same job.
  1153. //
  1154. if ((pRun->GetFlags() & dwFlag) &&
  1155. !(pRun->GetFlags() & RUN_STATUS_ABORTED))
  1156. {
  1157. //
  1158. // Set flags for immediate timeout and closure.
  1159. //
  1160. pRun->SetMaxRunTime(0);
  1161. pRun->SetFlag(RUN_STATUS_ABORTED);
  1162. if (dwFlag == TASK_FLAG_KILL_ON_IDLE_END &&
  1163. pRun->IsFlagSet(TASK_FLAG_RESTART_ON_IDLE_RESUME) &&
  1164. ! pRun->IsIdleTriggered())
  1165. {
  1166. //
  1167. // Note that this is the only case in which we set
  1168. // RUN_STATUS_RESTART_ON_IDLE_RESUME. If a job is terminated
  1169. // because a user explicitly terminated it, for example, we
  1170. // don't want to restart it on idle resume.
  1171. //
  1172. pRun->SetFlag(RUN_STATUS_RESTART_ON_IDLE_RESUME);
  1173. }
  1174. fContractInitiated = TRUE;
  1175. }
  1176. }
  1177. if (fContractInitiated)
  1178. {
  1179. //
  1180. // This logic will induce the PerformTask thread to respond as
  1181. // follows:
  1182. // - The wait will unblock and the next wait time re-calculated;
  1183. // this value will be zero since the min value is taken.
  1184. // - The wait is re-entered and immediately times out.
  1185. // - Jobs with max run times of zero are closed in the
  1186. // WAIT_TIMEOUT condition. As a result, the jobs are killed.
  1187. //
  1188. SetEvent(_rgHandles[0]);
  1189. }
  1190. LeaveCriticalSection(&_csProcessorCritSection);
  1191. }
  1192. //+---------------------------------------------------------------------------
  1193. //
  1194. // Member: CJobProcessor::Shutdown
  1195. //
  1196. // Synopsis: Effect processor shutdown. Do so by signalling the
  1197. // PerformTask thread. The thread will check the global service
  1198. // status flag. If the service is stopped (actually, in the
  1199. // process of stopping), the thread will execute the processor
  1200. // shutdown code & relinquish itself.
  1201. //
  1202. // Arguments: None.
  1203. //
  1204. // Returns: None.
  1205. //
  1206. // Notes: None.
  1207. //
  1208. //----------------------------------------------------------------------------
  1209. void
  1210. CJobProcessor::Shutdown(void)
  1211. {
  1212. TRACE3(CJobProcessor, Shutdown);
  1213. EnterCriticalSection(&_csProcessorCritSection);
  1214. if (_rgHandles != NULL)
  1215. {
  1216. SetEvent(_rgHandles[0]);
  1217. }
  1218. LeaveCriticalSection(&_csProcessorCritSection);
  1219. }
  1220. //+---------------------------------------------------------------------------
  1221. //
  1222. // Member: CJobProcessor::_EmptyJobQueue
  1223. //
  1224. // Synopsis: Empty respective job queue and log, per job, the reason why.
  1225. //
  1226. // Arguments: [JobQueue] -- Reference to CJobQueue instance.
  1227. // [dwMsgId] -- Why each job was abandoned. A value of zero
  1228. // indicates no reason; nothing is logged.
  1229. //
  1230. // Notes: Must be in the processor critical section for the duration
  1231. // of this method!
  1232. //
  1233. //----------------------------------------------------------------------------
  1234. void
  1235. CJobProcessor::_EmptyJobQueue(CJobQueue & JobQueue, DWORD dwMsgId)
  1236. {
  1237. TRACE3(CJobProcessor, _EmptyJobQueue);
  1238. CRun * pRun;
  1239. for (pRun = JobQueue.RemoveElement(); pRun != NULL;
  1240. pRun = JobQueue.RemoveElement())
  1241. {
  1242. if (!dwMsgId)
  1243. {
  1244. //
  1245. // BUGBUG : Log job info + reason why the job was abandoned.
  1246. // Should logging be per job? Per incident w/ job list?
  1247. //
  1248. }
  1249. delete pRun;
  1250. }
  1251. }
  1252. //+---------------------------------------------------------------------------
  1253. //
  1254. // Member: CJobProcessor::_ProcessRequests
  1255. //
  1256. // Synopsis: Transfer submitted jobs from the request queue to the
  1257. // processing queue and rebuild the wait handle array.
  1258. //
  1259. // Arguments: None.
  1260. //
  1261. // Notes: Must be in the processor critical section for the duration
  1262. // of this method!
  1263. //
  1264. //----------------------------------------------------------------------------
  1265. void
  1266. CJobProcessor::_ProcessRequests(void)
  1267. {
  1268. TRACE3(CJobProcessor, _ProcessRequests);
  1269. if (_RequestQueue.GetCount())
  1270. {
  1271. //
  1272. // Sum request, processing queue counts.
  1273. //
  1274. DWORD cJobs = _RequestQueue.GetCount() +
  1275. _ProcessingQueue.GetCount() + 1;
  1276. schDebugOut((DEB_USER3,
  1277. "CJobProcessor::_ProcessRequests(0x%x) Total job count(%d) = " \
  1278. "request(%d) + processing(%d) + 1\n",
  1279. this,
  1280. cJobs,
  1281. _RequestQueue.GetCount(),
  1282. _ProcessingQueue.GetCount()));
  1283. //
  1284. // Logic in SubmitJobs should prevent this from becoming false.
  1285. //
  1286. schAssert(cJobs <= MAXIMUM_WAIT_OBJECTS);
  1287. HANDLE * rgHandles = new HANDLE[cJobs];
  1288. if (rgHandles == NULL)
  1289. {
  1290. //
  1291. // Leave request, processing queues as-is.
  1292. //
  1293. LogServiceError(IDS_FATAL_ERROR,
  1294. ERROR_NOT_ENOUGH_MEMORY,
  1295. IDS_HELP_HINT_CLOSE_APPS);
  1296. ERR_OUT("JobProcessor: ProcessRequests", E_OUTOFMEMORY);
  1297. return;
  1298. }
  1299. //
  1300. // Copy existing handles.
  1301. //
  1302. CopyMemory(rgHandles,
  1303. _rgHandles,
  1304. sizeof(HANDLE) * (_ProcessingQueue.GetCount() + 1));
  1305. //
  1306. // Copy new job handles from request queue and transfer request
  1307. // queue contents to the tail of the processing queue.
  1308. //
  1309. for (DWORD i = _ProcessingQueue.GetCount() + 1; i < cJobs; i++)
  1310. {
  1311. CRun * pRun = _RequestQueue.RemoveElement();
  1312. Win4Assert( pRun != NULL );
  1313. rgHandles[i] = pRun->GetHandle();
  1314. _ProcessingQueue.AddElement(pRun);
  1315. if (pRun->IsFlagSet(TASK_FLAG_SYSTEM_REQUIRED))
  1316. {
  1317. //
  1318. // Increment the count of running system_required jobs
  1319. // handled by this thread. If this is the first such
  1320. // job, tell the system not to sleep until further notice.
  1321. //
  1322. WrapSetThreadExecutionState(TRUE, "processor - new job");
  1323. }
  1324. }
  1325. delete _rgHandles;
  1326. _rgHandles = rgHandles;
  1327. }
  1328. }
  1329. //+---------------------------------------------------------------------------
  1330. //
  1331. // Member: CJobProcessor::_Shutdown
  1332. //
  1333. // Synopsis: Take no more requests and dump whatever jobs remain in the
  1334. // request & processing queues.
  1335. //
  1336. // Arguments: None.
  1337. //
  1338. // Notes: Must be in the processor critical section for the duration
  1339. // of this method!
  1340. //
  1341. //----------------------------------------------------------------------------
  1342. void
  1343. CJobProcessor::_Shutdown(void)
  1344. {
  1345. TRACE3(CJobProcessor, _Shutdown);
  1346. //
  1347. // Utilizing the handle array member as a flag to indicate that this
  1348. // processor will take no more new jobs. Set this member to NULL on
  1349. // shutdown.
  1350. //
  1351. // First close the processor notification event handle & delete the
  1352. // array.
  1353. //
  1354. // No need to keep the machine awake for this thread any more
  1355. if (pfnSetThreadExecutionState != NULL)
  1356. {
  1357. schDebugOut((DEB_USER5, "RESETTING sys-required state: processor shutdown\n"));
  1358. (pfnSetThreadExecutionState)(ES_CONTINUOUS);
  1359. }
  1360. CloseHandle(_rgHandles[0]);
  1361. _rgHandles[0] = NULL;
  1362. delete _rgHandles;
  1363. _rgHandles = NULL;
  1364. //
  1365. // Now, empty request & processing queues.
  1366. //
  1367. // BUGBUG : Log job abandoned message.
  1368. //
  1369. this->_EmptyJobQueue(_RequestQueue);
  1370. this->_EmptyJobQueue(_ProcessingQueue);
  1371. }
  1372. //+---------------------------------------------------------------------------
  1373. //
  1374. // Member: CSchedWorker::JobPostProcessing
  1375. //
  1376. // Synopsis: Set the exit code, current status, and NextRunTime on the
  1377. // job object and log the run exit.
  1378. //
  1379. // Arguments: [pRun] -- Job run information object.
  1380. // [stFinished] -- Job finish time (local time). For logging.
  1381. //
  1382. // Notes: None.
  1383. //
  1384. //----------------------------------------------------------------------------
  1385. void
  1386. CSchedWorker::JobPostProcessing(
  1387. CRun * pRun,
  1388. SYSTEMTIME & stFinished)
  1389. {
  1390. TRACE3(CSchedWorker, JobPostProcessing);
  1391. schDebugOut((DEB_ITRACE,
  1392. "JobPostProcessing pRun(0x%x) flags(0x%x)\n",
  1393. pRun,
  1394. pRun->GetFlags()));
  1395. DWORD dwExitCode;
  1396. CJob * pJob = NULL;
  1397. //
  1398. // Instantiate the job so that the exit status can be saved.
  1399. //
  1400. // Note: if any of the variable length properties or the triggers are
  1401. // needed, then a full activation will be necessary.
  1402. //
  1403. // Important: the running instance count must be protected by the
  1404. // critical section here, where it is decremented, and in RunJobs, where
  1405. // it is incremented. These are the only sections of code that change
  1406. // the running instance count on the file object.
  1407. //
  1408. EnterCriticalSection(&m_SvcCriticalSection);
  1409. HRESULT hr = ActivateWithRetry(pRun->GetName(), &pJob, FALSE);
  1410. if (FAILED(hr))
  1411. {
  1412. //
  1413. // The job object may have been deleted. We can't supply LogTaskError
  1414. // with the name of the run target, since that's on the job object,
  1415. // which we just failed to load.
  1416. //
  1417. LogTaskError(pRun->GetName(),
  1418. NULL,
  1419. IDS_LOG_SEVERITY_WARNING,
  1420. IDS_LOG_JOB_WARNING_CANNOT_LOAD,
  1421. NULL,
  1422. (DWORD)hr);
  1423. ERR_OUT("JobPostProcessing Activate", hr);
  1424. LeaveCriticalSection(&m_SvcCriticalSection);
  1425. return;
  1426. }
  1427. //
  1428. // Isolate the executable name.
  1429. //
  1430. TCHAR tszExeName[MAX_PATH + 1];
  1431. GetExeNameFromCmdLine(pJob->GetCommand(), MAX_PATH + 1, tszExeName);
  1432. if (pRun->GetFlags() & RUN_STATUS_FINISHED)
  1433. {
  1434. //
  1435. // Only check the exit code if the job completed normally, that is,
  1436. // it wasn't timed-out or aborted.
  1437. //
  1438. if (!GetExitCodeProcess(pRun->GetHandle(), &dwExitCode))
  1439. {
  1440. LogTaskError(pRun->GetName(),
  1441. tszExeName,
  1442. IDS_LOG_SEVERITY_WARNING,
  1443. IDS_CANT_GET_EXITCODE,
  1444. NULL,
  1445. GetLastError());
  1446. ERR_OUT("GetExitCodeProcess", GetLastError());
  1447. }
  1448. }
  1449. else
  1450. {
  1451. //
  1452. // BUGBUG : What is written on the job when not complete?
  1453. //
  1454. }
  1455. //
  1456. // PostRunUpdate updates the flags and instance count, so always call it.
  1457. //
  1458. pJob->PostRunUpdate(dwExitCode, pRun->GetFlags() & RUN_STATUS_FINISHED);
  1459. //
  1460. // If the last run and the delete flag is set, delete the job object.
  1461. //
  1462. if (pJob->IsFlagSet(JOB_I_FLAG_NO_MORE_RUNS) &&
  1463. pJob->IsFlagSet(TASK_FLAG_DELETE_WHEN_DONE))
  1464. {
  1465. hr = pJob->Delete();
  1466. if (FAILED(hr))
  1467. {
  1468. LogTaskError(pRun->GetName(),
  1469. tszExeName,
  1470. IDS_LOG_SEVERITY_WARNING,
  1471. IDS_CANT_DELETE_JOB,
  1472. NULL,
  1473. GetLastError());
  1474. ERR_OUT("JobPostProcessing, delete-when-done", hr);
  1475. }
  1476. }
  1477. else
  1478. {
  1479. //
  1480. // Write the updated status to the job object. If there are sharing
  1481. // violations, retry two times.
  1482. //
  1483. hr = pJob->SaveWithRetry(NULL,
  1484. FALSE,
  1485. SAVEP_RUNNING_INSTANCE_COUNT |
  1486. SAVEP_PRESERVE_NET_SCHEDULE);
  1487. if (FAILED(hr))
  1488. {
  1489. LogTaskError(pRun->GetName(),
  1490. tszExeName,
  1491. IDS_LOG_SEVERITY_WARNING,
  1492. IDS_CANT_UPDATE_JOB,
  1493. NULL,
  1494. GetLastError());
  1495. ERR_OUT("JobPostProcessing, Saving run-completion-status", hr);
  1496. }
  1497. }
  1498. LeaveCriticalSection(&m_SvcCriticalSection);
  1499. if (pRun->GetFlags() & RUN_STATUS_FINISHED)
  1500. {
  1501. // Log job finish time & result.
  1502. //
  1503. LogTaskStatus(pRun->GetName(),
  1504. tszExeName,
  1505. IDS_LOG_JOB_STATUS_FINISHED,
  1506. dwExitCode);
  1507. }
  1508. else if (pRun->GetFlags() & RUN_STATUS_ABORTED)
  1509. {
  1510. // Log job closure on abort warning.
  1511. //
  1512. LogTaskError(pRun->GetName(),
  1513. tszExeName,
  1514. IDS_LOG_SEVERITY_WARNING,
  1515. IDS_LOG_JOB_WARNING_ABORTED,
  1516. &stFinished);
  1517. }
  1518. else if (pRun->GetFlags() & RUN_STATUS_TIMED_OUT)
  1519. {
  1520. // Log job closure on timeout warning.
  1521. //
  1522. LogTaskError(pRun->GetName(),
  1523. tszExeName,
  1524. IDS_LOG_SEVERITY_WARNING,
  1525. IDS_LOG_JOB_WARNING_TIMEOUT,
  1526. &stFinished,
  1527. 0,
  1528. IDS_HELP_HINT_TIMEOUT);
  1529. }
  1530. pJob->Release();
  1531. }