[SCHEDSVC] Improvements to the scheduler service

Use WaitForMultipleObjects in the mail scheduler loop:
- Use events to signal service stop and job update events to the main loop.
- Use the timeout timer to start the next job.
This commit is contained in:
Eric Kohl 2018-10-28 00:02:18 +02:00
parent 03294dd097
commit e4d79e514a
4 changed files with 108 additions and 32 deletions

View file

@ -35,6 +35,54 @@ RTL_RESOURCE StartListLock;
/* FUNCTIONS *****************************************************************/
DWORD
GetNextJobTimeout(VOID)
{
FILETIME FileTime;
SYSTEMTIME SystemTime;
ULARGE_INTEGER CurrentTime, Timeout;
PJOB pNextJob;
if (IsListEmpty(&StartListHead))
{
TRACE("No job in list! Wait until next update.\n");
return INFINITE;
}
pNextJob = CONTAINING_RECORD((&StartListHead)->Flink, JOB, StartEntry);
FileTime.dwLowDateTime = pNextJob->StartTime.u.LowPart;
FileTime.dwHighDateTime = pNextJob->StartTime.u.HighPart;
FileTimeToSystemTime(&FileTime, &SystemTime);
TRACE("Start next job (%lu) at %02hu:%02hu %02hu.%02hu.%hu\n",
pNextJob->JobId, SystemTime.wHour, SystemTime.wMinute,
SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear);
GetLocalTime(&SystemTime);
SystemTimeToFileTime(&SystemTime, &FileTime);
CurrentTime.u.LowPart = FileTime.dwLowDateTime;
CurrentTime.u.HighPart = FileTime.dwHighDateTime;
if (CurrentTime.QuadPart >= pNextJob->StartTime.QuadPart)
{
TRACE("Next event has already gone by!\n");
return 0;
}
Timeout.QuadPart = (pNextJob->StartTime.QuadPart - CurrentTime.QuadPart) / 10000;
if (Timeout.u.HighPart != 0)
{
TRACE("Event happens too far in the future!\n");
return INFINITE;
}
TRACE("Timeout: %lu\n", Timeout.u.LowPart);
return Timeout.u.LowPart;
}
static
VOID
GetJobName(
@ -276,8 +324,6 @@ LoadJobs(VOID)
pJob->JobId = dwNextJobId++;
dwJobCount++;
// Cancel the start timer
/* Append the new job to the job list */
InsertTailList(&JobListHead, &pJob->JobEntry);
@ -290,8 +336,6 @@ LoadJobs(VOID)
DumpStartList(&StartListHead);
#endif
// Update the start timer
/* Release the job list lock */
RtlReleaseResource(&JobListLock);
@ -333,7 +377,8 @@ DaysOfMonth(
VOID
CalculateNextStartTime(PJOB pJob)
CalculateNextStartTime(
_In_ PJOB pJob)
{
SYSTEMTIME StartTime;
FILETIME FileTime;

View file

@ -52,9 +52,14 @@ extern RTL_RESOURCE JobListLock;
extern LIST_ENTRY StartListHead;
extern RTL_RESOURCE StartListLock;
extern HANDLE Events[2];
/* job.c */
DWORD
GetNextJobTimeout(VOID);
LONG
SaveJob(
PJOB pJob);

View file

@ -112,8 +112,6 @@ NetrJobAdd(
pJob->JobId = dwNextJobId++;
dwJobCount++;
// Cancel the start timer
/* Append the new job to the job list */
InsertTailList(&JobListHead, &pJob->JobEntry);
@ -129,11 +127,13 @@ NetrJobAdd(
DumpStartList(&StartListHead);
#endif
// Update the start timer
/* Release the job list lock */
RtlReleaseResource(&JobListLock);
/* Set the update event */
if (Events[1] != NULL)
SetEvent(Events[1]);
/* Return the new job ID */
*pJobId = pJob->JobId;
@ -162,8 +162,6 @@ NetrJobDel(
/* Acquire the job list lock exclusively */
RtlAcquireResourceExclusive(&JobListLock, TRUE);
// Cancel the start timer
JobEntry = JobListHead.Flink;
while (JobEntry != &JobListHead)
{
@ -193,11 +191,13 @@ NetrJobDel(
JobEntry = JobEntry->Flink;
}
// Update the start timer
/* Release the job list lock */
RtlReleaseResource(&JobListLock);
/* Set the update event */
if (Events[1] != NULL)
SetEvent(Events[1]);
return ERROR_SUCCESS;
}

View file

@ -37,7 +37,8 @@ static WCHAR ServiceName[] = L"Schedule";
static SERVICE_STATUS_HANDLE ServiceStatusHandle;
static SERVICE_STATUS ServiceStatus;
static BOOL bStopService = FALSE;
HANDLE Events[2] = {NULL, NULL}; // StopEvent, UpdateEvent
/* FUNCTIONS *****************************************************************/
@ -77,7 +78,7 @@ ServiceControlHandler(DWORD dwControl,
LPVOID lpEventData,
LPVOID lpContext)
{
TRACE("ServiceControlHandler() called\n");
TRACE("ServiceControlHandler()\n");
switch (dwControl)
{
@ -87,7 +88,8 @@ ServiceControlHandler(DWORD dwControl,
UpdateServiceStatus(SERVICE_STOP_PENDING);
/* Stop listening to incoming RPC messages */
RpcMgmtStopServerListening(NULL);
bStopService = TRUE;
if (Events[0] != NULL)
SetEvent(Events[0]);
return ERROR_SUCCESS;
case SERVICE_CONTROL_PAUSE:
@ -106,6 +108,7 @@ ServiceControlHandler(DWORD dwControl,
&ServiceStatus);
return ERROR_SUCCESS;
#if 0
case 128:
TRACE(" Start Shell control received\n");
return ERROR_SUCCESS;
@ -113,6 +116,7 @@ ServiceControlHandler(DWORD dwControl,
case 129:
TRACE(" Logoff control received\n");
return ERROR_SUCCESS;
#endif
default:
TRACE(" Control %lu received\n", dwControl);
@ -123,7 +127,7 @@ ServiceControlHandler(DWORD dwControl,
static
DWORD
ServiceInit(PHANDLE phEvent)
ServiceInit(VOID)
{
HANDLE hThread;
DWORD dwError;
@ -160,11 +164,20 @@ ServiceInit(PHANDLE phEvent)
CloseHandle(hThread);
/* Create the scheduler event */
*phEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (*phEvent == NULL)
/* Create the stop event */
Events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (Events[0] == NULL)
{
ERR("Could not create the scheduler event\n");
ERR("Could not create the stop event\n");
return GetLastError();
}
/* Create the update event */
Events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (Events[1] == NULL)
{
ERR("Could not create the update event\n");
CloseHandle(Events[0]);
return GetLastError();
}
@ -175,13 +188,12 @@ ServiceInit(PHANDLE phEvent)
VOID WINAPI
SchedServiceMain(DWORD argc, LPTSTR *argv)
{
HANDLE hEvent = NULL;
DWORD dwError;
DWORD dwWait, dwTimeout, dwError;
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
TRACE("SchedServiceMain() called\n");
TRACE("SchedServiceMain()\n");
ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
ServiceControlHandler,
@ -194,7 +206,7 @@ SchedServiceMain(DWORD argc, LPTSTR *argv)
UpdateServiceStatus(SERVICE_START_PENDING);
dwError = ServiceInit(&hEvent);
dwError = ServiceInit();
if (dwError != ERROR_SUCCESS)
{
ERR("Service stopped (dwError: %lu\n", dwError);
@ -204,19 +216,33 @@ SchedServiceMain(DWORD argc, LPTSTR *argv)
UpdateServiceStatus(SERVICE_RUNNING);
dwTimeout = GetNextJobTimeout();
for (;;)
{
/* Leave the loop, if the service has to be stopped */
if (bStopService)
/* Wait for the next event */
TRACE("Wait for next event!\n");
dwWait = WaitForMultipleObjects(2, Events, FALSE, dwTimeout);
if (dwWait == WAIT_OBJECT_0)
{
TRACE("Stop event signaled!\n");
break;
}
else if (dwWait == WAIT_OBJECT_0 + 1)
{
TRACE("Update event signaled!\n");
dwTimeout = GetNextJobTimeout();
}
else if (dwWait == WAIT_TIMEOUT)
{
TRACE("Timeout: Start the next job!\n");
/* Wait for the next timeout */
WaitForSingleObject(hEvent, 5000);
TRACE("Service running!\n");
}
}
/* Close the scheduler event handle */
CloseHandle(hEvent);
/* Close the start and update event handles */
CloseHandle(Events[0]);
CloseHandle(Events[1]);
/* Stop the service */
UpdateServiceStatus(SERVICE_STOPPED);