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.

1967 lines
48 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Copyright (c) 1997-1999 Microsoft Corporation
  4. //
  5. // File: jobmgr.cpp
  6. //
  7. // Contents: Job scheduler
  8. //
  9. // History:
  10. //
  11. //---------------------------------------------------------------------------
  12. #include "pch.cpp"
  13. #include <process.h>
  14. #include "server.h"
  15. #include "jobmgr.h"
  16. #include "debug.h"
  17. //------------------------------------------------------------
  18. //
  19. //
  20. CLASS_PRIVATE BOOL
  21. CWorkManager::SignalJobRunning(
  22. IN CWorkObject *ptr
  23. )
  24. /*++
  25. Abstract:
  26. Class private routine for work object to 'signal'
  27. work manger it has started processing.
  28. Parameter:
  29. ptr : Pointer to CWorkObject that is ready to run.
  30. Returns:
  31. TRUE/FALSE
  32. --*/
  33. {
  34. DWORD dwStatus = ERROR_SUCCESS;
  35. INPROCESSINGJOBLIST::iterator it;
  36. if(ptr != NULL)
  37. {
  38. m_InProcessingListLock.Lock();
  39. DBGPrintf(
  40. DBG_INFORMATION,
  41. DBG_FACILITY_WORKMGR,
  42. DBGLEVEL_FUNCTION_TRACE,
  43. _TEXT("WorkManager : SignalJobRunning() Job %p ...\n"),
  44. ptr
  45. );
  46. //
  47. // find our pointer in processing list
  48. //
  49. it = m_InProcessingList.find(ptr);
  50. if(it != m_InProcessingList.end())
  51. {
  52. // TODO - make processing thread handle a list.
  53. if((*it).second.m_hThread == NULL)
  54. {
  55. HANDLE hHandle;
  56. BOOL bSuccess;
  57. bSuccess = DuplicateHandle(
  58. GetCurrentProcess(),
  59. GetCurrentThread(),
  60. GetCurrentProcess(),
  61. &hHandle,
  62. DUPLICATE_SAME_ACCESS,
  63. FALSE,
  64. 0
  65. );
  66. if(bSuccess == FALSE)
  67. {
  68. //
  69. // non-critical error, if we fail, we won't be able to
  70. // cancel our rpc call.
  71. //
  72. SetLastError(dwStatus = GetLastError());
  73. DBGPrintf(
  74. DBG_INFORMATION,
  75. DBG_FACILITY_WORKMGR,
  76. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  77. _TEXT("WorkManager : SignalJobRunning() duplicate handle return %d...\n"),
  78. dwStatus
  79. );
  80. }
  81. else
  82. {
  83. //
  84. // set processing thread handle of job.
  85. //
  86. (*it).second.m_hThread = hHandle;
  87. }
  88. }
  89. }
  90. else
  91. {
  92. DBGPrintf(
  93. DBG_INFORMATION,
  94. DBG_FACILITY_WORKMGR,
  95. DBGLEVEL_FUNCTION_TRACE,
  96. _TEXT("WorkManager : SignalJobRunning can't find job %p in processing list...\n"),
  97. ptr
  98. );
  99. //
  100. // timing problem, job might be re-scheduled and actually execute before we have
  101. // time to remove from our in-processing list.
  102. //
  103. //TLSASSERT(FALSE);
  104. }
  105. m_InProcessingListLock.UnLock();
  106. }
  107. else
  108. {
  109. SetLastError(dwStatus = ERROR_INVALID_DATA);
  110. }
  111. return dwStatus;
  112. }
  113. //----------------------------------------------------------------
  114. //
  115. CLASS_PRIVATE void
  116. CWorkManager::CancelInProcessingJob()
  117. /*++
  118. Abstract:
  119. Class private : cancel job currently in processing state,
  120. only invoked at the time of service shutdown.
  121. Parameter:
  122. None.
  123. Return:
  124. None.
  125. --*/
  126. {
  127. INPROCESSINGJOBLIST::iterator it;
  128. DBGPrintf(
  129. DBG_INFORMATION,
  130. DBG_FACILITY_WORKMGR,
  131. DBGLEVEL_FUNCTION_TRACE,
  132. _TEXT("WorkManager : CancelInProcessingJob...\n")
  133. );
  134. m_InProcessingListLock.Lock();
  135. for(it = m_InProcessingList.begin();
  136. it != m_InProcessingList.end();
  137. it++ )
  138. {
  139. if((*it).second.m_hThread != NULL)
  140. {
  141. // cancel everything and ignore error.
  142. (VOID)RpcCancelThread((*it).second.m_hThread);
  143. }
  144. }
  145. m_InProcessingListLock.UnLock();
  146. return;
  147. }
  148. //------------------------------------------------------------
  149. //
  150. //
  151. CLASS_PRIVATE DWORD
  152. CWorkManager::AddJobToProcessingList(
  153. IN CWorkObject *ptr
  154. )
  155. /*++
  156. Abstract:
  157. Class private, move job from wait queue to in-process queue.
  158. Parameter:
  159. ptr : Pointer to job.
  160. Parameter:
  161. ERROR_SUCESS or error code.
  162. --*/
  163. {
  164. DWORD dwStatus = ERROR_SUCCESS;
  165. INPROCESSINGJOBLIST::iterator it;
  166. WorkMangerInProcessJob job;
  167. if(ptr == NULL)
  168. {
  169. SetLastError(dwStatus = ERROR_INVALID_DATA);
  170. }
  171. else
  172. {
  173. DBGPrintf(
  174. DBG_INFORMATION,
  175. DBG_FACILITY_WORKMGR,
  176. DBGLEVEL_FUNCTION_TRACE,
  177. _TEXT("WorkManager : Add Job <%s> to processing list...\n"),
  178. ptr->GetJobDescription()
  179. );
  180. m_InProcessingListLock.Lock();
  181. it = m_InProcessingList.find(ptr);
  182. if(it != m_InProcessingList.end())
  183. {
  184. // increase the reference counter.
  185. InterlockedIncrement(&((*it).second.m_refCounter));
  186. }
  187. else
  188. {
  189. job.m_refCounter = 1;
  190. job.m_hThread = NULL; // job not run yet.
  191. m_InProcessingList[ptr] = job;
  192. }
  193. ResetEvent(m_hJobInProcessing);
  194. m_InProcessingListLock.UnLock();
  195. }
  196. return dwStatus;
  197. }
  198. //------------------------------------------------------------
  199. //
  200. //
  201. CLASS_PRIVATE DWORD
  202. CWorkManager::RemoveJobFromProcessingList(
  203. IN CWorkObject *ptr
  204. )
  205. /*++
  206. Abstract:
  207. Class private, remove a job from in-processing list.
  208. Parameter:
  209. ptr : Pointer to job to be removed from list.
  210. Returns:
  211. ERROR_SUCCESS or error code.
  212. --*/
  213. {
  214. DWORD dwStatus = ERROR_SUCCESS;
  215. INPROCESSINGJOBLIST::iterator it;
  216. if(ptr != NULL)
  217. {
  218. m_InProcessingListLock.Lock();
  219. DBGPrintf(
  220. DBG_INFORMATION,
  221. DBG_FACILITY_WORKMGR,
  222. DBGLEVEL_FUNCTION_TRACE,
  223. _TEXT("WorkManager : RemoveJobFromProcessingList Job %p from processing list...\n"),
  224. ptr
  225. );
  226. it = m_InProcessingList.find(ptr);
  227. if(it != m_InProcessingList.end())
  228. {
  229. // decrease the reference counter
  230. InterlockedDecrement(&((*it).second.m_refCounter));
  231. if((*it).second.m_refCounter <= 0)
  232. {
  233. // close thread handle.
  234. if((*it).second.m_hThread != NULL)
  235. {
  236. CloseHandle((*it).second.m_hThread);
  237. }
  238. m_InProcessingList.erase(it);
  239. }
  240. else
  241. {
  242. DBGPrintf(
  243. DBG_INFORMATION,
  244. DBG_FACILITY_WORKMGR,
  245. DBGLEVEL_FUNCTION_TRACE,
  246. _TEXT("WorkManager : RemoveJobFromProcessingList job %p reference counter = %d...\n"),
  247. ptr,
  248. (*it).second
  249. );
  250. }
  251. }
  252. else
  253. {
  254. DBGPrintf(
  255. DBG_INFORMATION,
  256. DBG_FACILITY_WORKMGR,
  257. DBGLEVEL_FUNCTION_TRACE,
  258. _TEXT("WorkManager : RemoveJobFromProcessingList can't find job %p in processing list...\n"),
  259. ptr
  260. );
  261. //
  262. // timing problem, job might be re-scheduled and actually execute before we have
  263. // time to remove from our in-processing list.
  264. //
  265. //TLSASSERT(FALSE);
  266. }
  267. if(m_InProcessingList.empty() == TRUE)
  268. {
  269. //
  270. // Inform Work Manager that no job is in processing.
  271. //
  272. SetEvent(m_hJobInProcessing);
  273. }
  274. m_InProcessingListLock.UnLock();
  275. }
  276. else
  277. {
  278. SetLastError(dwStatus = ERROR_INVALID_DATA);
  279. }
  280. return dwStatus;
  281. }
  282. //------------------------------------------------------------
  283. //
  284. //
  285. CLASS_PRIVATE BOOL
  286. CWorkManager::WaitForObjectOrShutdown(
  287. IN HANDLE hHandle
  288. )
  289. /*++
  290. Abstract:
  291. Class private, Wait for a sync. handle or service shutdown
  292. event.
  293. Parameter:
  294. hHandle : handle to wait for,
  295. Returns:
  296. TRUE if sucessful, FALSE if service shutdown or error.
  297. --*/
  298. {
  299. HANDLE handles[] = {hHandle, m_hShutdown};
  300. DWORD dwStatus;
  301. dwStatus = WaitForMultipleObjects(
  302. sizeof(handles)/sizeof(handles[0]),
  303. handles,
  304. FALSE,
  305. INFINITE
  306. );
  307. return (dwStatus == WAIT_OBJECT_0);
  308. }
  309. //------------------------------------------------------------
  310. //
  311. //
  312. CLASS_PRIVATE DWORD
  313. CWorkManager::RunJob(
  314. IN CWorkObject* ptr,
  315. IN BOOL bImmediate
  316. )
  317. /*++
  318. Abstract:
  319. Process a job object via QueueUserWorkItem() Win32 API subject
  320. to our max. concurrent job limitation.
  321. Parameter:
  322. ptr : Pointer to CWorkObject.
  323. bImmediate : TRUE if job must be process immediately,
  324. FALSE otherwise.
  325. Returns:
  326. ERROR_SUCCESS or Error code.
  327. --*/
  328. {
  329. BOOL bSuccess;
  330. DWORD dwStatus = ERROR_SUCCESS;
  331. if(ptr != NULL)
  332. {
  333. DBGPrintf(
  334. DBG_INFORMATION,
  335. DBG_FACILITY_WORKMGR,
  336. DBGLEVEL_FUNCTION_TRACE,
  337. _TEXT("WorkManager : RunJob <%s>...\n"),
  338. ptr->GetJobDescription()
  339. );
  340. //
  341. // Wait if we exceed max. concurrent job
  342. //
  343. bSuccess = (bImmediate) ? bImmediate : m_hMaxJobLock.AcquireEx(m_hShutdown);
  344. if(bSuccess == TRUE)
  345. {
  346. DWORD dwFlag;
  347. DWORD dwJobRunningAttribute;
  348. dwJobRunningAttribute = ptr->GetJobRunningAttribute();
  349. dwFlag = TranslateJobRunningAttributeToThreadPoolFlag(
  350. dwJobRunningAttribute
  351. );
  352. dwStatus = AddJobToProcessingList(ptr);
  353. if(dwStatus == ERROR_SUCCESS)
  354. {
  355. DBGPrintf(
  356. DBG_INFORMATION,
  357. DBG_FACILITY_WORKMGR,
  358. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  359. _TEXT("RunJob() : queuing user work item %p...\n"),
  360. ptr
  361. );
  362. // need immediate attention.
  363. bSuccess = QueueUserWorkItem(
  364. CWorkManager::ExecuteWorkObject,
  365. ptr,
  366. dwFlag
  367. );
  368. if(bSuccess == FALSE)
  369. {
  370. dwStatus = GetLastError();
  371. DBGPrintf(
  372. DBG_ERROR,
  373. DBG_FACILITY_WORKMGR,
  374. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  375. _TEXT("RunJob() : queuing user work item %p failed with 0x%08x...\n"),
  376. ptr,
  377. dwStatus
  378. );
  379. //TLSASSERT(dwStatus == ERROR_SUCCESS);
  380. dwStatus = RemoveJobFromProcessingList(ptr);
  381. }
  382. }
  383. else
  384. {
  385. bSuccess = FALSE;
  386. }
  387. if(bSuccess == FALSE)
  388. {
  389. dwStatus = GetLastError();
  390. //TLSASSERT(FALSE);
  391. }
  392. //
  393. // release max. concurrent job lock
  394. //
  395. if(bImmediate == FALSE)
  396. {
  397. m_hMaxJobLock.Release();
  398. }
  399. }
  400. else
  401. {
  402. dwStatus = TLS_I_WORKMANAGER_SHUTDOWN;
  403. }
  404. }
  405. else
  406. {
  407. SetLastError(dwStatus = ERROR_INVALID_PARAMETER);
  408. }
  409. return dwStatus;
  410. }
  411. //------------------------------------------------------------
  412. //
  413. //
  414. CLASS_PRIVATE DWORD
  415. CWorkManager::ProcessScheduledJob()
  416. /*++
  417. Abstract:
  418. Class private, process a scheduled job.
  419. Parameter:
  420. None.
  421. Return:
  422. ERROR_SUCCESS or error code.
  423. --*/
  424. {
  425. DWORD dwStatus = ERROR_SUCCESS;
  426. BOOL bSuccess = TRUE;
  427. BOOL bFlag = FALSE;
  428. DBGPrintf(
  429. DBG_INFORMATION,
  430. DBG_FACILITY_WORKMGR,
  431. DBGLEVEL_FUNCTION_TRACE,
  432. _TEXT("CWorkManager::ProcessScheduledJob(), %d %d\n"),
  433. GetNumberJobInStorageQueue(),
  434. GetNumberJobInMemoryQueue()
  435. );
  436. if(GetNumberJobInStorageQueue() != 0 && IsShuttingDown() == FALSE)
  437. {
  438. //
  439. // Could have use work item to process both
  440. // queue but this uses one extra handle, and
  441. // work manager thread will just doing nothing
  442. //
  443. ResetEvent(m_hInStorageWait);
  444. //
  445. // Queue a user work item to thread pool to process
  446. // in storage job
  447. //
  448. bSuccess = QueueUserWorkItem(
  449. CWorkManager::ProcessInStorageScheduledJob,
  450. this,
  451. WT_EXECUTELONGFUNCTION
  452. );
  453. if(bSuccess == FALSE)
  454. {
  455. dwStatus = GetLastError();
  456. DBGPrintf(
  457. DBG_ERROR,
  458. DBG_FACILITY_WORKMGR,
  459. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  460. _TEXT("CWorkManager::ProcessScheduledJob() queue user work iterm returns 0x%08x\n"),
  461. dwStatus
  462. );
  463. TLSASSERT(dwStatus == ERROR_SUCCESS);
  464. }
  465. else
  466. {
  467. bFlag = TRUE;
  468. }
  469. }
  470. if(bSuccess == TRUE)
  471. {
  472. dwStatus = ProcessInMemoryScheduledJob(this);
  473. if(bFlag == TRUE)
  474. {
  475. if(WaitForObjectOrShutdown(m_hInStorageWait) == FALSE)
  476. {
  477. dwStatus = TLS_I_WORKMANAGER_SHUTDOWN;
  478. }
  479. }
  480. }
  481. return dwStatus;
  482. }
  483. //------------------------------------------------------------
  484. //
  485. //
  486. CLASS_PRIVATE CLASS_STATIC DWORD WINAPI
  487. CWorkManager::ProcessInMemoryScheduledJob(
  488. IN PVOID pContext
  489. )
  490. /*++
  491. Abstract:
  492. Static class private, process in-memory scheduled jobs.
  493. WorkManagerThread kick off two threads, one to process
  494. in-memory job and the other to process persistent job.
  495. Parameter:
  496. pContext : Pointer to work manager object.
  497. Return:
  498. ERROR_SUCCESS or error code.
  499. --*/
  500. {
  501. DWORD ulCurrentTime;
  502. DWORD dwJobTime;
  503. CWorkObject* pInMemoryWorkObject = NULL;
  504. BOOL bSuccess = TRUE;
  505. BOOL dwStatus = ERROR_SUCCESS;
  506. CWorkManager* pWkMgr = (CWorkManager *)pContext;
  507. DBGPrintf(
  508. DBG_INFORMATION,
  509. DBG_FACILITY_WORKMGR,
  510. DBGLEVEL_FUNCTION_TRACE,
  511. _TEXT("CWorkManager::ProcessInMemoryScheduledJob()\n")
  512. );
  513. if(pWkMgr == NULL)
  514. {
  515. SetLastError(ERROR_INVALID_PARAMETER);
  516. TLSASSERT(pWkMgr != NULL);
  517. return ERROR_INVALID_PARAMETER;
  518. }
  519. do {
  520. if(pWkMgr->IsShuttingDown() == TRUE)
  521. {
  522. dwStatus = TLS_I_WORKMANAGER_SHUTDOWN;
  523. break;
  524. }
  525. ulCurrentTime = time(NULL);
  526. dwJobTime = ulCurrentTime;
  527. pInMemoryWorkObject = pWkMgr->GetNextJobInMemoryQueue(&dwJobTime);
  528. if(pInMemoryWorkObject != NULL)
  529. {
  530. // TLSASSERT(dwJobTime <= ulCurrentTime);
  531. if(dwJobTime <= ulCurrentTime)
  532. {
  533. dwStatus = pWkMgr->RunJob(
  534. pInMemoryWorkObject,
  535. FALSE
  536. );
  537. if(dwStatus != ERROR_SUCCESS)
  538. {
  539. //
  540. // consider to re-schedule job again.
  541. //
  542. pInMemoryWorkObject->EndJob();
  543. if(pInMemoryWorkObject->CanBeDelete() == TRUE)
  544. {
  545. pInMemoryWorkObject->SelfDestruct();
  546. }
  547. }
  548. }
  549. else
  550. {
  551. //
  552. // Very expansive operation, GetNextJobInMemoryQueue() must be
  553. // wrong.
  554. //
  555. dwStatus = pWkMgr->AddJobIntoMemoryQueue(
  556. dwJobTime,
  557. pInMemoryWorkObject
  558. );
  559. if(dwStatus != ERROR_SUCCESS)
  560. {
  561. //
  562. // delete the job
  563. //
  564. pInMemoryWorkObject->EndJob();
  565. if(pInMemoryWorkObject->CanBeDelete() == TRUE)
  566. {
  567. pInMemoryWorkObject->SelfDestruct();
  568. }
  569. }
  570. }
  571. }
  572. } while(dwStatus == ERROR_SUCCESS && (pInMemoryWorkObject != NULL && dwJobTime <= ulCurrentTime));
  573. return dwStatus;
  574. }
  575. //------------------------------------------------------------
  576. //
  577. //
  578. CLASS_PRIVATE CLASS_STATIC DWORD WINAPI
  579. CWorkManager::ProcessInStorageScheduledJob(
  580. IN PVOID pContext
  581. )
  582. /*++
  583. Abstract:
  584. Static class private, process scheduled persistent jobs.
  585. WorkManagerThread kick off two threads, one to process
  586. in-memory job and the other to process persistent job.
  587. Parameter:
  588. pContext : Pointer to work manager object.
  589. Return:
  590. ERROR_SUCCESS or error code.
  591. --*/
  592. {
  593. DWORD ulCurrentTime = 0;
  594. DWORD dwJobScheduledTime = 0;
  595. CWorkObject* pInStorageWorkObject = NULL;
  596. DWORD dwStatus = ERROR_SUCCESS;
  597. BOOL bSuccess = TRUE;
  598. CWorkManager* pWkMgr = (CWorkManager *)pContext;
  599. DBGPrintf(
  600. DBG_INFORMATION,
  601. DBG_FACILITY_WORKMGR,
  602. DBGLEVEL_FUNCTION_TRACE,
  603. _TEXT("CWorkManager::ProcessInStorageScheduledJob()\n")
  604. );
  605. if(pWkMgr == NULL)
  606. {
  607. TLSASSERT(pWkMgr != NULL);
  608. SetLastError(ERROR_INVALID_PARAMETER);
  609. return ERROR_INVALID_PARAMETER;
  610. }
  611. TLSASSERT(pWkMgr->m_pPersistentWorkStorage != NULL);
  612. if(pWkMgr->m_pPersistentWorkStorage->GetNumJobs() > 0)
  613. {
  614. do
  615. {
  616. if(pWkMgr->IsShuttingDown() == TRUE)
  617. {
  618. dwStatus = TLS_I_WORKMANAGER_SHUTDOWN;
  619. break;
  620. }
  621. ulCurrentTime = time(NULL);
  622. pInStorageWorkObject = pWkMgr->m_pPersistentWorkStorage->GetNextJob(&dwJobScheduledTime);
  623. if(pInStorageWorkObject == NULL)
  624. {
  625. //
  626. // Something wrong in persistent storage???
  627. //
  628. DBGPrintf(
  629. DBG_WARNING,
  630. DBG_FACILITY_WORKMGR,
  631. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  632. _TEXT("CWorkManager::ProcessInStorageScheduledJob() : Persistent work storage return NULL job\n")
  633. );
  634. break;
  635. }
  636. else if(dwJobScheduledTime > ulCurrentTime)
  637. {
  638. DBGPrintf(
  639. DBG_WARNING,
  640. DBG_FACILITY_WORKMGR,
  641. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  642. _TEXT("CWorkManager::ProcessInStorageScheduledJob() : return job back to persistent storage\n")
  643. );
  644. pWkMgr->m_pPersistentWorkStorage->EndProcessingJob(
  645. ENDPROCESSINGJOB_RETURN,
  646. dwJobScheduledTime,
  647. pInStorageWorkObject
  648. );
  649. }
  650. else
  651. {
  652. pInStorageWorkObject->SetScheduledTime(dwJobScheduledTime);
  653. pWkMgr->m_pPersistentWorkStorage->BeginProcessingJob(pInStorageWorkObject);
  654. dwStatus = pWkMgr->RunJob(
  655. pInStorageWorkObject,
  656. FALSE
  657. );
  658. if(dwStatus != ERROR_SUCCESS)
  659. {
  660. DBGPrintf(
  661. DBG_WARNING,
  662. DBG_FACILITY_WORKMGR,
  663. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  664. _TEXT("CWorkManager::ProcessInStorageScheduledJob() : unable to queue job, return job back ") \
  665. _TEXT("to persistent storage\n")
  666. );
  667. pWkMgr->m_pPersistentWorkStorage->EndProcessingJob(
  668. ENDPROCESSINGJOB_RETURN,
  669. pInStorageWorkObject->GetScheduledTime(),
  670. pInStorageWorkObject
  671. );
  672. }
  673. }
  674. } while(dwStatus == ERROR_SUCCESS && ulCurrentTime >= dwJobScheduledTime);
  675. }
  676. //
  677. // Signal we are done
  678. //
  679. SetEvent(pWkMgr->m_hInStorageWait);
  680. return dwStatus;
  681. }
  682. //------------------------------------------------------------
  683. //
  684. //
  685. CLASS_PRIVATE CLASS_STATIC
  686. unsigned int __stdcall
  687. CWorkManager::WorkManagerThread(
  688. IN PVOID pContext
  689. )
  690. /*++
  691. Abstract:
  692. Static class private, this is the work manager thread to handle
  693. job scheduling and process scheduled job. WorkManagerThread()
  694. will not terminate until m_hShutdown event is signal.
  695. Parameter:
  696. pContext : Pointer to work manager object.
  697. Returns:
  698. ERROR_SUCCESS
  699. --*/
  700. {
  701. DWORD dwTimeToNextJob = INFINITE;
  702. CWorkManager* pWkMgr = (CWorkManager *)pContext;
  703. DWORD dwHandleFlag;
  704. TLSASSERT(pWkMgr != NULL);
  705. TLSASSERT(GetHandleInformation(pWkMgr->m_hNewJobArrive, &dwHandleFlag) == TRUE);
  706. TLSASSERT(GetHandleInformation(pWkMgr->m_hShutdown, &dwHandleFlag) == TRUE);
  707. HANDLE m_hWaitHandles[] = {pWkMgr->m_hShutdown, pWkMgr->m_hNewJobArrive};
  708. DWORD dwWaitStatus = WAIT_TIMEOUT;
  709. DWORD dwStatus = ERROR_SUCCESS;
  710. //
  711. // Get the time to next job
  712. //
  713. while(dwWaitStatus != WAIT_OBJECT_0 && dwStatus == ERROR_SUCCESS)
  714. {
  715. DBGPrintf(
  716. DBG_INFORMATION,
  717. DBG_FACILITY_WORKMGR,
  718. DBGLEVEL_FUNCTION_TRACE,
  719. _TEXT("CWorkManager::WorkManagerThread() : Time to next job %d\n"),
  720. dwTimeToNextJob
  721. );
  722. dwWaitStatus = WaitForMultipleObjectsEx(
  723. sizeof(m_hWaitHandles) / sizeof(m_hWaitHandles[0]),
  724. m_hWaitHandles,
  725. FALSE,
  726. dwTimeToNextJob * 1000,
  727. TRUE // we might need this thread to do some work
  728. );
  729. switch( dwWaitStatus )
  730. {
  731. case WAIT_OBJECT_0:
  732. dwStatus = ERROR_SUCCESS;
  733. DBGPrintf(
  734. DBG_INFORMATION,
  735. DBG_FACILITY_WORKMGR,
  736. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  737. _TEXT("CWorkManager::WorkManagerThread() : shutdown ...\n")
  738. );
  739. break;
  740. case WAIT_OBJECT_0 + 1:
  741. // still a possibility that we might not catch a new job
  742. ResetEvent(pWkMgr->m_hNewJobArrive);
  743. // New Job arrived
  744. dwTimeToNextJob = pWkMgr->GetTimeToNextJob();
  745. break;
  746. case WAIT_TIMEOUT:
  747. // Time to process job.
  748. dwStatus = pWkMgr->ProcessScheduledJob();
  749. dwTimeToNextJob = pWkMgr->GetTimeToNextJob();
  750. break;
  751. default:
  752. DBGPrintf(
  753. DBG_ERROR,
  754. DBG_FACILITY_WORKMGR,
  755. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  756. _TEXT("CWorkManager::WorkManagerThread() : unexpected return %d\n"),
  757. dwStatus
  758. );
  759. dwStatus = TLS_E_WORKMANAGER_INTERNAL;
  760. TLSASSERT(FALSE);
  761. }
  762. }
  763. if(dwStatus != ERROR_SUCCESS && dwStatus != TLS_I_WORKMANAGER_SHUTDOWN)
  764. {
  765. DBGPrintf(
  766. DBG_ERROR,
  767. DBG_FACILITY_WORKMGR,
  768. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  769. _TEXT("CWorkManager::WorkManagerThread() : unexpected return %d, generate console event\n"),
  770. dwStatus
  771. );
  772. // immediately shut down server
  773. GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
  774. }
  775. _endthreadex(dwStatus);
  776. return dwStatus;
  777. }
  778. //----------------------------------------------------------------
  779. //
  780. //
  781. CLASS_PRIVATE CLASS_STATIC
  782. DWORD WINAPI
  783. CWorkManager::ExecuteWorkObject(
  784. IN PVOID pContext
  785. )
  786. /*++
  787. Abstract:
  788. Static class private, execute a work object.
  789. Parameter:
  790. pContext : Pointer to work object to be process.
  791. Returns:
  792. ERROR_SUCCESS or error code.
  793. --*/
  794. {
  795. DWORD dwStatus = ERROR_SUCCESS;
  796. CWorkObject* pWorkObject = (CWorkObject *)pContext;
  797. DWORD dwJobRescheduleTime;
  798. BOOL bStorageJobCompleted;
  799. CWorkManager* pWkMgr = NULL;
  800. BOOL bPersistentJob = FALSE;
  801. if(pContext == NULL)
  802. {
  803. TLSASSERT(FALSE);
  804. dwStatus = ERROR_INVALID_PARAMETER;
  805. return dwStatus;
  806. }
  807. DBGPrintf(
  808. DBG_INFORMATION,
  809. DBG_FACILITY_WORKMGR,
  810. DBGLEVEL_FUNCTION_TRACE,
  811. _TEXT("CWorkManager::ExecuteWorkObject() : executing %p <%s>\n"),
  812. pWorkObject,
  813. pWorkObject->GetJobDescription()
  814. );
  815. //
  816. // Set RPC cancel timeout, thread dependent.
  817. (VOID)RpcMgmtSetCancelTimeout(DEFAULT_RPCCANCEL_TIMEOUT);
  818. bPersistentJob = pWorkObject->IsWorkPersistent();
  819. pWkMgr = pWorkObject->GetWorkManager();
  820. if(pWkMgr != NULL)
  821. {
  822. pWkMgr->SignalJobRunning(pWorkObject); // tell work manager that we are running
  823. pWorkObject->ExecuteWorkObject();
  824. if(bPersistentJob == TRUE)
  825. {
  826. //
  827. // Persistent work object, let work storage handle
  828. // its re-scheduling
  829. //
  830. bStorageJobCompleted = pWorkObject->IsJobCompleted();
  831. pWorkObject->GetWorkManager()->m_pPersistentWorkStorage->EndProcessingJob(
  832. ENDPROCESSINGJOB_SUCCESS,
  833. pWorkObject->GetScheduledTime(),
  834. pWorkObject
  835. );
  836. if(bStorageJobCompleted == FALSE)
  837. {
  838. //
  839. // This job might be re-scheduled
  840. // before our work manager thread wakes up,
  841. // so signal job is ready
  842. //
  843. pWkMgr->SignalJobArrive();
  844. }
  845. }
  846. else
  847. {
  848. //
  849. // Reschedule job if necessary
  850. //
  851. dwJobRescheduleTime = pWorkObject->GetSuggestedScheduledTime();
  852. if(dwJobRescheduleTime != INFINITE)
  853. {
  854. dwStatus = pWorkObject->ScheduleJob(dwJobRescheduleTime);
  855. }
  856. if(dwJobRescheduleTime == INFINITE || dwStatus != ERROR_SUCCESS)
  857. {
  858. //
  859. // if can't schedule job again, go ahead and delete it.
  860. //
  861. pWorkObject->EndJob();
  862. if(pWorkObject->CanBeDelete() == TRUE)
  863. {
  864. pWorkObject->SelfDestruct();
  865. }
  866. }
  867. }
  868. }
  869. if(pWkMgr)
  870. {
  871. // Delete this job from in-processing list.
  872. pWkMgr->EndProcessingScheduledJob(pWorkObject);
  873. }
  874. return dwStatus;
  875. }
  876. //----------------------------------------------------------------
  877. //
  878. //
  879. CWorkManager::CWorkManager() :
  880. m_hWorkMgrThread(NULL),
  881. m_hNewJobArrive(NULL),
  882. m_hShutdown(NULL),
  883. m_hInStorageWait(NULL),
  884. m_hJobInProcessing(NULL),
  885. m_dwNextInMemoryJobTime(WORKMANAGER_WAIT_FOREVER),
  886. m_dwNextInStorageJobTime(WORKMANAGER_WAIT_FOREVER),
  887. m_dwMaxCurrentJob(DEFAULT_NUM_CONCURRENTJOB),
  888. m_dwDefaultInterval(DEFAULT_WORK_INTERVAL)
  889. {
  890. }
  891. //----------------------------------------------------------------
  892. //
  893. CWorkManager::~CWorkManager()
  894. {
  895. Shutdown();
  896. if(m_hNewJobArrive != NULL)
  897. {
  898. CloseHandle(m_hNewJobArrive);
  899. }
  900. if(m_hWorkMgrThread != NULL)
  901. {
  902. CloseHandle(m_hWorkMgrThread);
  903. }
  904. if(m_hShutdown != NULL)
  905. {
  906. CloseHandle(m_hShutdown);
  907. }
  908. if(m_hInStorageWait != NULL)
  909. {
  910. CloseHandle(m_hInStorageWait);
  911. }
  912. if(m_hJobInProcessing != NULL)
  913. {
  914. CloseHandle(m_hJobInProcessing);
  915. }
  916. }
  917. //----------------------------------------------------------------
  918. //
  919. DWORD
  920. CWorkManager::Startup(
  921. IN CWorkStorage* pPersistentWorkStorage,
  922. IN DWORD dwWorkInterval, // DEFAULT_WORK_INTERVAL
  923. IN DWORD dwNumConcurrentJob // DEFAULT_NUM_CONCURRENTJOB
  924. )
  925. /*++
  926. Abstract:
  927. Initialize work manager
  928. Parameters:
  929. pPersistentWorkStorage : A C++ object that derived from CPersistentWorkStorage class
  930. dwWorkInterval : Default schedule job interval
  931. dwNumConcurrentJob : Max. number of concurrent job to be fired at the same time
  932. Return:
  933. ERROR_SUCCESS or Erro Code.
  934. --*/
  935. {
  936. DWORD index;
  937. DWORD dwStatus = ERROR_SUCCESS;
  938. unsigned dump;
  939. BOOL bSuccess;
  940. unsigned threadid;
  941. #ifdef __TEST_WORKMGR__
  942. _set_new_handler(handle_new_failed);
  943. #endif
  944. if(dwNumConcurrentJob == 0 || dwWorkInterval == 0 || pPersistentWorkStorage == NULL)
  945. {
  946. SetLastError(dwStatus = ERROR_INVALID_PARAMETER);
  947. goto cleanup;
  948. }
  949. if(m_hMaxJobLock.IsGood() == FALSE)
  950. {
  951. if(m_hMaxJobLock.Init(dwNumConcurrentJob, dwNumConcurrentJob) == FALSE)
  952. {
  953. //
  954. // out of resource
  955. //
  956. dwStatus = GetLastError();
  957. goto cleanup;
  958. }
  959. }
  960. m_dwDefaultInterval = dwWorkInterval;
  961. m_dwMaxCurrentJob = dwNumConcurrentJob;
  962. m_pPersistentWorkStorage = pPersistentWorkStorage;
  963. if(m_hJobInProcessing == NULL)
  964. {
  965. //
  966. // initial state is signal, no job in processing
  967. //
  968. m_hJobInProcessing = CreateEvent(
  969. NULL,
  970. TRUE,
  971. TRUE,
  972. NULL
  973. );
  974. if(m_hJobInProcessing == NULL)
  975. {
  976. dwStatus = GetLastError();
  977. goto cleanup;
  978. }
  979. }
  980. if(m_hShutdown == NULL)
  981. {
  982. //
  983. // Create a handle for signaling shutdown
  984. //
  985. m_hShutdown = CreateEvent(
  986. NULL,
  987. TRUE,
  988. FALSE,
  989. NULL
  990. );
  991. if(m_hShutdown == NULL)
  992. {
  993. dwStatus = GetLastError();
  994. goto cleanup;
  995. }
  996. }
  997. if(m_hNewJobArrive == NULL)
  998. {
  999. //
  1000. // initial state is signal so work manager thread can
  1001. // update wait time
  1002. //
  1003. m_hNewJobArrive = CreateEvent(
  1004. NULL,
  1005. TRUE,
  1006. TRUE,
  1007. NULL
  1008. );
  1009. if(m_hNewJobArrive == NULL)
  1010. {
  1011. dwStatus = GetLastError();
  1012. goto cleanup;
  1013. }
  1014. }
  1015. if(m_hInStorageWait == NULL)
  1016. {
  1017. m_hInStorageWait = CreateEvent(
  1018. NULL,
  1019. TRUE,
  1020. TRUE, // signal state
  1021. NULL
  1022. );
  1023. if(m_hInStorageWait == NULL)
  1024. {
  1025. dwStatus = GetLastError();
  1026. goto cleanup;
  1027. }
  1028. }
  1029. //
  1030. // Startup Work Storage first.
  1031. //
  1032. if(m_pPersistentWorkStorage->Startup(this) == FALSE)
  1033. {
  1034. DBGPrintf(
  1035. DBG_ERROR,
  1036. DBG_FACILITY_WORKMGR,
  1037. DBGLEVEL_FUNCTION_DETAILSIMPLE,
  1038. _TEXT("CWorkManager::Startup() : Persistent storage has failed to startup - 0x%08x\n"),
  1039. GetLastError()
  1040. );
  1041. dwStatus = GetLastError();
  1042. if (dwStatus == ERROR_SUCCESS)
  1043. dwStatus = TLS_E_WORKMANAGER_PERSISTENJOB;
  1044. goto cleanup;
  1045. }
  1046. //
  1047. // Get time to next persistent job.
  1048. //
  1049. if(UpdateTimeToNextPersistentJob() == FALSE)
  1050. {
  1051. dwStatus = TLS_E_WORKMANAGER_PERSISTENJOB;
  1052. goto cleanup;
  1053. }
  1054. if(m_hWorkMgrThread == NULL)
  1055. {
  1056. //
  1057. // Create work manager thread, suspended first
  1058. //
  1059. m_hWorkMgrThread = (HANDLE)_beginthreadex(
  1060. NULL,
  1061. 0,
  1062. CWorkManager::WorkManagerThread,
  1063. this,
  1064. 0,
  1065. &threadid
  1066. );
  1067. if(m_hWorkMgrThread == NULL)
  1068. {
  1069. dwStatus = GetLastError();
  1070. goto cleanup;
  1071. }
  1072. }
  1073. cleanup:
  1074. return dwStatus;
  1075. }
  1076. //----------------------------------------------------------------
  1077. void
  1078. CWorkManager::Shutdown()
  1079. /*++
  1080. Abstract:
  1081. Shutdown work manager.
  1082. Parameter:
  1083. None.
  1084. Return:
  1085. None.
  1086. --*/
  1087. {
  1088. HANDLE handles[] = {m_hInStorageWait, m_hJobInProcessing};
  1089. DWORD dwStatus;
  1090. //
  1091. // Signal we are shuting down
  1092. //
  1093. if(m_hShutdown != NULL)
  1094. {
  1095. SetEvent(m_hShutdown);
  1096. }
  1097. //
  1098. // Wait for dispatch thread to terminate so no job can be
  1099. // dispatched.
  1100. //
  1101. if(m_hWorkMgrThread != NULL)
  1102. {
  1103. dwStatus = WaitForSingleObject(
  1104. m_hWorkMgrThread,
  1105. INFINITE
  1106. );
  1107. TLSASSERT(dwStatus != WAIT_FAILED);
  1108. CloseHandle(m_hWorkMgrThread);
  1109. m_hWorkMgrThread = NULL;
  1110. }
  1111. //
  1112. // Cancel all in progress job
  1113. //
  1114. CancelInProcessingJob();
  1115. //
  1116. // Inform all existing job to shutdown.
  1117. //
  1118. DeleteAllJobsInMemoryQueue();
  1119. //
  1120. // Wait for all processing job to terminate
  1121. //
  1122. if(m_hInStorageWait != NULL && m_hJobInProcessing != NULL)
  1123. {
  1124. dwStatus = WaitForMultipleObjects(
  1125. sizeof(handles)/sizeof(handles[0]),
  1126. handles,
  1127. TRUE,
  1128. INFINITE
  1129. );
  1130. TLSASSERT(dwStatus != WAIT_FAILED);
  1131. CloseHandle(m_hInStorageWait);
  1132. m_hInStorageWait = NULL;
  1133. CloseHandle(m_hJobInProcessing);
  1134. m_hJobInProcessing = NULL;
  1135. }
  1136. if(m_pPersistentWorkStorage != NULL)
  1137. {
  1138. //
  1139. // Signal we are shutting down, no job is in
  1140. // processing and we are not taking any
  1141. // new job
  1142. //
  1143. m_pPersistentWorkStorage->Shutdown();
  1144. m_pPersistentWorkStorage = NULL;
  1145. }
  1146. TLSASSERT( GetNumberJobInProcessing() == 0 );
  1147. // TLSASSERT( GetNumberJobInMemoryQueue() == 0 );
  1148. if(m_hNewJobArrive != NULL)
  1149. {
  1150. CloseHandle(m_hNewJobArrive);
  1151. m_hNewJobArrive = NULL;
  1152. }
  1153. if(m_hWorkMgrThread != NULL)
  1154. {
  1155. CloseHandle(m_hWorkMgrThread);
  1156. m_hWorkMgrThread = NULL;
  1157. }
  1158. if(m_hShutdown != NULL)
  1159. {
  1160. CloseHandle(m_hShutdown);
  1161. m_hShutdown = NULL;
  1162. }
  1163. if(m_hInStorageWait != NULL)
  1164. {
  1165. CloseHandle(m_hInStorageWait);
  1166. m_hInStorageWait = NULL;
  1167. }
  1168. if(m_hJobInProcessing != NULL)
  1169. {
  1170. CloseHandle(m_hJobInProcessing);
  1171. m_hJobInProcessing = NULL;
  1172. }
  1173. return;
  1174. }
  1175. //----------------------------------------------------------------
  1176. CLASS_PRIVATE DWORD
  1177. CWorkManager::GetTimeToNextJob()
  1178. /*++
  1179. Abstract:
  1180. Class private, return time to next scheduled job.
  1181. Parameter:
  1182. None.
  1183. Return:
  1184. Time to next job in second.
  1185. --*/
  1186. {
  1187. DWORD dwNextJobTime = WORKMANAGER_WAIT_FOREVER;
  1188. DWORD dwNumPersistentJob = GetNumberJobInStorageQueue();
  1189. DWORD dwNumInMemoryJob = GetNumberJobInMemoryQueue();
  1190. DWORD dwCurrentTime = time(NULL);
  1191. if( dwNumPersistentJob == 0 && dwNumInMemoryJob == 0 )
  1192. {
  1193. // DO NOTHING
  1194. // dwTimeToNextJob = WORKMANAGER_WAIT_FOREVER;
  1195. }
  1196. else
  1197. {
  1198. UpdateTimeToNextInMemoryJob();
  1199. UpdateTimeToNextPersistentJob();
  1200. dwNextJobTime = min((DWORD)m_dwNextInMemoryJobTime, (DWORD)m_dwNextInStorageJobTime);
  1201. if((DWORD)dwNextJobTime < (DWORD)dwCurrentTime)
  1202. {
  1203. dwNextJobTime = 0;
  1204. }
  1205. else
  1206. {
  1207. dwNextJobTime -= dwCurrentTime;
  1208. }
  1209. }
  1210. return dwNextJobTime;
  1211. }
  1212. //----------------------------------------------------------------
  1213. //
  1214. CLASS_PRIVATE CWorkObject*
  1215. CWorkManager::GetNextJobInMemoryQueue(
  1216. PDWORD pdwTime
  1217. )
  1218. /*++
  1219. Abstract:
  1220. Class private, return pointer to next scheduled
  1221. in memory job.
  1222. Parameter:
  1223. pdwTime : Pointer to DWORD to receive time to the
  1224. scheduled job.
  1225. Returns:
  1226. Pointer to CWorkObject.
  1227. Note:
  1228. Remove the job from queue if job is <= time.
  1229. --*/
  1230. {
  1231. SCHEDULEJOBMAP::iterator it;
  1232. DWORD dwWantedJobTime;
  1233. CWorkObject* ptr = NULL;
  1234. SetLastError(ERROR_SUCCESS);
  1235. if(pdwTime != NULL)
  1236. {
  1237. dwWantedJobTime = *pdwTime;
  1238. m_JobLock.Acquire(READER_LOCK);
  1239. it = m_Jobs.begin();
  1240. if(it != m_Jobs.end())
  1241. {
  1242. *pdwTime = (*it).first;
  1243. if(dwWantedJobTime >= *pdwTime)
  1244. {
  1245. ptr = (*it).second;
  1246. // remove job from queue
  1247. m_Jobs.erase(it);
  1248. }
  1249. }
  1250. m_JobLock.Release(READER_LOCK);
  1251. }
  1252. else
  1253. {
  1254. SetLastError(ERROR_INVALID_PARAMETER);
  1255. }
  1256. return ptr;
  1257. }
  1258. //----------------------------------------------------------------
  1259. //
  1260. CLASS_PRIVATE void
  1261. CWorkManager::DeleteAllJobsInMemoryQueue()
  1262. /*++
  1263. Abstract:
  1264. Class private, unconditionally delete all in-memory job.
  1265. Parameter:
  1266. None.
  1267. Return:
  1268. None.
  1269. --*/
  1270. {
  1271. m_JobLock.Acquire(WRITER_LOCK);
  1272. SCHEDULEJOBMAP::iterator it;
  1273. for(it = m_Jobs.begin(); it != m_Jobs.end(); it++)
  1274. {
  1275. //
  1276. // let calling routine to delete it
  1277. //
  1278. (*it).second->EndJob();
  1279. if((*it).second->CanBeDelete() == TRUE)
  1280. {
  1281. (*it).second->SelfDestruct();
  1282. }
  1283. (*it).second = NULL;
  1284. }
  1285. m_Jobs.erase(m_Jobs.begin(), m_Jobs.end());
  1286. m_JobLock.Release(WRITER_LOCK);
  1287. return;
  1288. }
  1289. //----------------------------------------------------------------
  1290. //
  1291. CLASS_PRIVATE BOOL
  1292. CWorkManager::RemoveJobFromInMemoryQueue(
  1293. IN DWORD ulTime,
  1294. IN CWorkObject* ptr
  1295. )
  1296. /*++
  1297. Abstract:
  1298. Class private, remove a scheduled job.
  1299. Parameters:
  1300. ulTime : Job scheduled time.
  1301. ptr : Pointer to Job to be deleted.
  1302. Returns:
  1303. TRUE/FALSE.
  1304. Note:
  1305. A job might be scheduled multiple time so we
  1306. need to pass in the time.
  1307. --*/
  1308. {
  1309. BOOL bSuccess = FALSE;
  1310. m_JobLock.Acquire(WRITER_LOCK);
  1311. SCHEDULEJOBMAP::iterator low = m_Jobs.lower_bound(ulTime);
  1312. SCHEDULEJOBMAP::iterator high = m_Jobs.upper_bound(ulTime);
  1313. for(;low != m_Jobs.end() && low != high; low++)
  1314. {
  1315. if( (*low).second == ptr )
  1316. {
  1317. //
  1318. // let calling routine to delete it
  1319. //
  1320. (*low).second = NULL;
  1321. m_Jobs.erase(low);
  1322. bSuccess = TRUE;
  1323. break;
  1324. }
  1325. }
  1326. if(bSuccess == FALSE)
  1327. {
  1328. SetLastError(ERROR_INVALID_DATA);
  1329. TLSASSERT(FALSE);
  1330. }
  1331. m_JobLock.Release(WRITER_LOCK);
  1332. return bSuccess;
  1333. }
  1334. //----------------------------------------------------------------
  1335. //
  1336. CLASS_PRIVATE DWORD
  1337. CWorkManager::AddJobIntoMemoryQueue(
  1338. IN DWORD dwTime, // suggested scheduled time
  1339. IN CWorkObject* pJob // Job to be scheduled
  1340. )
  1341. /*++
  1342. Abstract:
  1343. Class private, add a job into in-memory list.
  1344. Parameters:
  1345. dwTime : suggested scheduled time.
  1346. pJob : Pointer to job to be added.
  1347. Returns:
  1348. ERROR_SUCCESS or error code.
  1349. --*/
  1350. {
  1351. DWORD dwStatus = ERROR_SUCCESS;
  1352. BOOL bSuccess = FALSE;
  1353. DWORD dwJobScheduleTime = time(NULL) + dwTime;
  1354. if(IsShuttingDown() == TRUE)
  1355. {
  1356. dwStatus = TLS_I_WORKMANAGER_SHUTDOWN;
  1357. return dwStatus;
  1358. }
  1359. m_JobLock.Acquire(WRITER_LOCK);
  1360. //
  1361. // insert a job into our queue
  1362. //
  1363. m_Jobs.insert( SCHEDULEJOBMAP::value_type( dwJobScheduleTime, pJob ) );
  1364. AddJobUpdateInMemoryJobWaitTimer(dwJobScheduleTime);
  1365. m_JobLock.Release(WRITER_LOCK);
  1366. return dwStatus;
  1367. }
  1368. //----------------------------------------------------------------
  1369. //
  1370. DWORD
  1371. CWorkManager::ScheduleJob(
  1372. IN DWORD ulTime, // suggested scheduled time
  1373. IN CWorkObject* pJob // Job to be scheduled
  1374. )
  1375. /*++
  1376. Abstract:
  1377. Schedule a job at time relative to current time
  1378. Parameters:
  1379. ulTime : suggested scheduled time.
  1380. pJob : Pointer to job to be scheduled
  1381. Returns:
  1382. ERROR_SUCCESS or error code.
  1383. --*/
  1384. {
  1385. BOOL bSuccess = TRUE;
  1386. DWORD dwStatus = ERROR_SUCCESS;
  1387. if(pJob == NULL)
  1388. {
  1389. SetLastError(dwStatus = ERROR_INVALID_PARAMETER);
  1390. goto cleanup;
  1391. }
  1392. if(IsShuttingDown() == TRUE)
  1393. {
  1394. SetLastError(dwStatus = TLS_I_WORKMANAGER_SHUTDOWN);
  1395. goto cleanup;
  1396. }
  1397. DBGPrintf(
  1398. DBG_INFORMATION,
  1399. DBG_FACILITY_WORKMGR,
  1400. DBGLEVEL_FUNCTION_TRACE,
  1401. _TEXT("CWorkManager::ScheduleJob() : schedule job <%s> to queue at time %d\n"),
  1402. pJob->GetJobDescription(),
  1403. ulTime
  1404. );
  1405. pJob->SetProcessingWorkManager(this);
  1406. if(ulTime == INFINITE && pJob->IsWorkPersistent() == FALSE)
  1407. {
  1408. //
  1409. // Only in-memory job can be executed at once.
  1410. //
  1411. dwStatus = RunJob(pJob, TRUE);
  1412. }
  1413. else
  1414. {
  1415. if(pJob->IsWorkPersistent() == TRUE)
  1416. {
  1417. if(m_pPersistentWorkStorage->AddJob(ulTime, pJob) == FALSE)
  1418. {
  1419. dwStatus = TLS_E_WORKMANAGER_PERSISTENJOB;
  1420. }
  1421. }
  1422. else
  1423. {
  1424. //
  1425. // insert a workobject into job queue, reason not to
  1426. // use RegisterWaitForSingleObject() or threadpool's timer
  1427. // is that we don't need to track handle nor wait for
  1428. // DeleteTimerXXX to finish
  1429. //
  1430. dwStatus = AddJobIntoMemoryQueue(
  1431. ulTime, // Memory queue is absolute time
  1432. pJob
  1433. );
  1434. }
  1435. if(dwStatus == ERROR_SUCCESS)
  1436. {
  1437. if(SignalJobArrive() == FALSE)
  1438. {
  1439. dwStatus = GetLastError();
  1440. TLSASSERT(FALSE);
  1441. }
  1442. }
  1443. }
  1444. cleanup:
  1445. return dwStatus;
  1446. }
  1447. ///////////////////////////////////////////////////////////////
  1448. //
  1449. // CWorkObject base class
  1450. //
  1451. CWorkObject::CWorkObject(
  1452. IN BOOL bDestructorDelete /* = FALSE */
  1453. ) :
  1454. m_dwLastRunStatus(ERROR_SUCCESS),
  1455. m_refCount(0),
  1456. m_pWkMgr(NULL),
  1457. m_bCanBeFree(bDestructorDelete)
  1458. {
  1459. }
  1460. //----------------------------------------------------------
  1461. DWORD
  1462. CWorkObject::Init(
  1463. IN BOOL bDestructorDelete /* = FALSE */
  1464. )
  1465. /*++
  1466. Abstract:
  1467. Initialize a work object.
  1468. Parameter:
  1469. bDestructorDelete : TRUE if destructor should delete the memory,
  1470. FALSE otherwise.
  1471. Returns:
  1472. ERROR_SUCCESS or error code.
  1473. Note:
  1474. if bDestructorDelete is FALSE, memory will not be free.
  1475. --*/
  1476. {
  1477. m_dwLastRunStatus = ERROR_SUCCESS;
  1478. m_refCount = 0;
  1479. m_bCanBeFree = bDestructorDelete;
  1480. return ERROR_SUCCESS;
  1481. }
  1482. //----------------------------------------------------------
  1483. CLASS_PRIVATE long
  1484. CWorkObject::GetReferenceCount()
  1485. /*++
  1486. Abstract:
  1487. Return reference count of work object.
  1488. Parameter:
  1489. None.
  1490. Return:
  1491. Reference count.
  1492. --*/
  1493. {
  1494. return m_refCount;
  1495. }
  1496. //----------------------------------------------------------
  1497. CLASS_PRIVATE void
  1498. CWorkObject::IncrementRefCount()
  1499. /*++
  1500. Abstract:
  1501. Increment object's reference counter.
  1502. Parameter:
  1503. None.
  1504. Return:
  1505. None.
  1506. --*/
  1507. {
  1508. InterlockedIncrement(&m_refCount);
  1509. }
  1510. //----------------------------------------------------------
  1511. CLASS_PRIVATE void
  1512. CWorkObject::DecrementRefCount()
  1513. /*++
  1514. Abstract:
  1515. Decrement object's reference counter.
  1516. Parameter:
  1517. None.
  1518. Return:
  1519. None.
  1520. --*/
  1521. {
  1522. InterlockedDecrement(&m_refCount);
  1523. }
  1524. //----------------------------------------------------------
  1525. CLASS_PRIVATE void
  1526. CWorkObject::ExecuteWorkObject()
  1527. /*++
  1528. Abstract:
  1529. Execute a work object. Work manager invoke work object's
  1530. ExecuteWorkObject so that base class can set its reference
  1531. counter.
  1532. Parameter:
  1533. None.
  1534. Return:
  1535. None.
  1536. --*/
  1537. {
  1538. if(IsValid() == TRUE)
  1539. {
  1540. IncrementRefCount();
  1541. m_dwLastRunStatus = Execute();
  1542. DecrementRefCount();
  1543. }
  1544. else
  1545. {
  1546. m_dwLastRunStatus = ERROR_INVALID_DATA;
  1547. TLSASSERT(FALSE);
  1548. }
  1549. }
  1550. //----------------------------------------------------------
  1551. CLASS_PRIVATE void
  1552. CWorkObject::EndExecuteWorkObject()
  1553. /*++
  1554. Abstract:
  1555. End a job, this does not terminate job currently in
  1556. processing, it remove the job from work manager's in-processing
  1557. list
  1558. Parameter:
  1559. None.
  1560. Return:
  1561. None.
  1562. --*/
  1563. {
  1564. TLSASSERT(IsValid() == TRUE);
  1565. m_pWkMgr->EndProcessingScheduledJob(this);
  1566. }