[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 *****************************************************************/ /* 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 static
VOID VOID
GetJobName( GetJobName(
@ -276,8 +324,6 @@ LoadJobs(VOID)
pJob->JobId = dwNextJobId++; pJob->JobId = dwNextJobId++;
dwJobCount++; dwJobCount++;
// Cancel the start timer
/* Append the new job to the job list */ /* Append the new job to the job list */
InsertTailList(&JobListHead, &pJob->JobEntry); InsertTailList(&JobListHead, &pJob->JobEntry);
@ -290,8 +336,6 @@ LoadJobs(VOID)
DumpStartList(&StartListHead); DumpStartList(&StartListHead);
#endif #endif
// Update the start timer
/* Release the job list lock */ /* Release the job list lock */
RtlReleaseResource(&JobListLock); RtlReleaseResource(&JobListLock);
@ -333,7 +377,8 @@ DaysOfMonth(
VOID VOID
CalculateNextStartTime(PJOB pJob) CalculateNextStartTime(
_In_ PJOB pJob)
{ {
SYSTEMTIME StartTime; SYSTEMTIME StartTime;
FILETIME FileTime; FILETIME FileTime;

View file

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

View file

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

View file

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