Reactos

[SCHEDSVC] Use a timer to start jobs

This enables the service to start multiple jobs at the same time.

+110 -152
+86 -134
base/services/schedsvc/job.c
··· 31 31 32 32 LIST_ENTRY StartListHead; 33 33 RTL_RESOURCE StartListLock; 34 + FILETIME NextJobStartTime; 35 + BOOL bValidNextJobStartTime = FALSE; 36 + 34 37 35 38 static WORD wDaysArray[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 36 39 37 40 38 41 /* FUNCTIONS *****************************************************************/ 39 42 40 - DWORD 41 - GetNextJobTimeout(VOID) 43 + VOID 44 + GetNextJobTimeout(HANDLE hTimer) 42 45 { 43 - FILETIME FileTime; 44 - SYSTEMTIME SystemTime; 45 - ULARGE_INTEGER CurrentTime, Timeout; 46 - PJOB pNextJob; 46 + PLIST_ENTRY CurrentEntry; 47 + FILETIME DueTime; 48 + PJOB CurrentJob; 47 49 48 - if (IsListEmpty(&StartListHead)) 50 + bValidNextJobStartTime = FALSE; 51 + CurrentEntry = JobListHead.Flink; 52 + while (CurrentEntry != &JobListHead) 49 53 { 50 - TRACE("No job in list! Wait until next update.\n"); 51 - return INFINITE; 52 - } 54 + CurrentJob = CONTAINING_RECORD(CurrentEntry, JOB, JobEntry); 53 55 54 - pNextJob = CONTAINING_RECORD((&StartListHead)->Flink, JOB, StartEntry); 55 - 56 - FileTime.dwLowDateTime = pNextJob->StartTime.u.LowPart; 57 - FileTime.dwHighDateTime = pNextJob->StartTime.u.HighPart; 58 - FileTimeToSystemTime(&FileTime, &SystemTime); 59 - 60 - TRACE("Start next job (%lu) at %02hu:%02hu %02hu.%02hu.%hu\n", 61 - pNextJob->JobId, SystemTime.wHour, SystemTime.wMinute, 62 - SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear); 56 + if (bValidNextJobStartTime == FALSE) 57 + { 58 + CopyMemory(&NextJobStartTime, &CurrentJob->StartTime, sizeof(FILETIME)); 59 + bValidNextJobStartTime = TRUE; 60 + } 61 + else 62 + { 63 + if (CompareFileTime(&NextJobStartTime, &CurrentJob->StartTime) > 0) 64 + CopyMemory(&NextJobStartTime, &CurrentJob->StartTime, sizeof(FILETIME)); 65 + } 63 66 64 - GetLocalTime(&SystemTime); 65 - SystemTimeToFileTime(&SystemTime, &FileTime); 66 - 67 - CurrentTime.u.LowPart = FileTime.dwLowDateTime; 68 - CurrentTime.u.HighPart = FileTime.dwHighDateTime; 69 - 70 - if (CurrentTime.QuadPart >= pNextJob->StartTime.QuadPart) 71 - { 72 - TRACE("Next event has already gone by!\n"); 73 - return 0; 67 + CurrentEntry = CurrentEntry->Flink; 74 68 } 75 69 76 - Timeout.QuadPart = (pNextJob->StartTime.QuadPart - CurrentTime.QuadPart) / 10000; 77 - if (Timeout.u.HighPart != 0) 70 + if (bValidNextJobStartTime == FALSE) 78 71 { 79 - TRACE("Event happens too far in the future!\n"); 80 - return INFINITE; 72 + TRACE("No valid job!\n"); 73 + return; 81 74 } 82 75 83 - TRACE("Timeout: %lu\n", Timeout.u.LowPart); 84 - return Timeout.u.LowPart; 85 - } 76 + LocalFileTimeToFileTime(&DueTime, &NextJobStartTime); 86 77 78 + SetWaitableTimer(hTimer, 79 + (PLARGE_INTEGER)&DueTime, 80 + 0, 81 + NULL, 82 + NULL, 83 + TRUE); 84 + } 87 85 86 + #if 0 88 87 static 89 88 VOID 90 89 ReScheduleJob( ··· 117 116 DumpStartList(&StartListHead); 118 117 #endif 119 118 } 120 - 119 + #endif 121 120 122 121 VOID 123 - RunNextJob(VOID) 122 + RunCurrentJobs(VOID) 124 123 { 125 124 PROCESS_INFORMATION ProcessInformation; 126 125 STARTUPINFOW StartupInfo; 126 + PLIST_ENTRY CurrentEntry; 127 + PJOB CurrentJob; 127 128 BOOL bRet; 128 - PJOB pNextJob; 129 129 130 - if (IsListEmpty(&StartListHead)) 130 + CurrentEntry = JobListHead.Flink; 131 + while (CurrentEntry != &JobListHead) 131 132 { 132 - ERR("No job in list!\n"); 133 - return; 134 - } 133 + CurrentJob = CONTAINING_RECORD(CurrentEntry, JOB, JobEntry); 135 134 136 - pNextJob = CONTAINING_RECORD((&StartListHead)->Flink, JOB, StartEntry); 135 + if (CompareFileTime(&NextJobStartTime, &CurrentJob->StartTime) == 0) 136 + { 137 + TRACE("Run job %ld: %S\n", CurrentJob->JobId, CurrentJob->Command); 137 138 138 - TRACE("Run job %ld: %S\n", pNextJob->JobId, pNextJob->Command); 139 + ZeroMemory(&StartupInfo, sizeof(StartupInfo)); 140 + StartupInfo.cb = sizeof(StartupInfo); 141 + StartupInfo.lpTitle = CurrentJob->Command; 142 + StartupInfo.dwFlags = STARTF_USESHOWWINDOW; 143 + StartupInfo.wShowWindow = SW_SHOWDEFAULT; 139 144 140 - ZeroMemory(&StartupInfo, sizeof(StartupInfo)); 141 - StartupInfo.cb = sizeof(StartupInfo); 142 - StartupInfo.lpTitle = pNextJob->Command; 143 - StartupInfo.dwFlags = STARTF_USESHOWWINDOW; 144 - StartupInfo.wShowWindow = SW_SHOWDEFAULT; 145 + if ((CurrentJob->Flags & JOB_NONINTERACTIVE) == 0) 146 + { 147 + StartupInfo.dwFlags |= STARTF_INHERITDESKTOP; 148 + StartupInfo.lpDesktop = L"WinSta0\\Default"; 149 + } 145 150 146 - if ((pNextJob->Flags & JOB_NONINTERACTIVE) == 0) 147 - { 148 - StartupInfo.dwFlags |= STARTF_INHERITDESKTOP; 149 - StartupInfo.lpDesktop = L"WinSta0\\Default"; 150 - } 151 + bRet = CreateProcessW(NULL, 152 + CurrentJob->Command, 153 + NULL, 154 + NULL, 155 + FALSE, 156 + CREATE_NEW_CONSOLE, 157 + NULL, 158 + NULL, 159 + &StartupInfo, 160 + &ProcessInformation); 161 + if (bRet == FALSE) 162 + { 163 + ERR("CreateProcessW() failed (Error %lu)\n", GetLastError()); 164 + } 165 + else 166 + { 167 + CloseHandle(ProcessInformation.hThread); 168 + CloseHandle(ProcessInformation.hProcess); 169 + } 170 + } 151 171 152 - bRet = CreateProcessW(NULL, 153 - pNextJob->Command, 154 - NULL, 155 - NULL, 156 - FALSE, 157 - CREATE_NEW_CONSOLE, 158 - NULL, 159 - NULL, 160 - &StartupInfo, 161 - &ProcessInformation); 162 - if (bRet == FALSE) 163 - { 164 - ERR("CreateProcessW() failed (Error %lu)\n", GetLastError()); 172 + CurrentEntry = CurrentEntry->Flink; 165 173 } 166 - else 167 - { 168 - CloseHandle(ProcessInformation.hThread); 169 - CloseHandle(ProcessInformation.hProcess); 170 - } 171 - 172 - ReScheduleJob(pNextJob); 173 174 } 174 175 175 176 ··· 420 421 /* Calculate the next start time */ 421 422 CalculateNextStartTime(pJob); 422 423 423 - /* Insert the job into the start list */ 424 - InsertJobIntoStartList(&StartListHead, pJob); 425 424 #if 0 426 425 DumpStartList(&StartListHead); 427 426 #endif ··· 473 472 WORD wDaysOffset, wTempOffset, i, wJobDayOfWeek, wJobDayOfMonth; 474 473 DWORD_PTR CurrentTimeMs; 475 474 BOOL bDaysOffsetValid; 475 + ULARGE_INTEGER LocalStartTime; 476 476 477 477 TRACE("CalculateNextStartTime(%p)\n", pJob); 478 478 TRACE("JobTime: %lu\n", pJob->JobTime); ··· 590 590 591 591 SystemTimeToFileTime(&StartSystemTime, &StartFileTime); 592 592 593 - pJob->StartTime.u.LowPart = StartFileTime.dwLowDateTime; 594 - pJob->StartTime.u.HighPart = StartFileTime.dwHighDateTime; 593 + LocalStartTime.u.LowPart = StartFileTime.dwLowDateTime; 594 + LocalStartTime.u.HighPart = StartFileTime.dwHighDateTime; 595 595 if (bDaysOffsetValid && wDaysOffset != 0) 596 596 { 597 - pJob->StartTime.QuadPart += ((ULONGLONG)wDaysOffset * 24 * 60 * 60 * 10000); 598 - } 599 - } 600 - 601 - 602 - VOID 603 - InsertJobIntoStartList( 604 - _In_ PLIST_ENTRY StartListHead, 605 - _In_ PJOB pJob) 606 - { 607 - PLIST_ENTRY CurrentEntry, PreviousEntry; 608 - PJOB CurrentJob; 609 - 610 - if (IsListEmpty(StartListHead)) 611 - { 612 - InsertHeadList(StartListHead, &pJob->StartEntry); 613 - return; 597 + LocalStartTime.QuadPart += ((ULONGLONG)wDaysOffset * 24 * 60 * 60 * 10000); 614 598 } 615 599 616 - CurrentEntry = StartListHead->Flink; 617 - while (CurrentEntry != StartListHead) 618 - { 619 - CurrentJob = CONTAINING_RECORD(CurrentEntry, JOB, StartEntry); 620 - 621 - if ((CurrentEntry == StartListHead->Flink) && 622 - (pJob->StartTime.QuadPart < CurrentJob->StartTime.QuadPart)) 623 - { 624 - /* Insert before the first entry */ 625 - InsertHeadList(StartListHead, &pJob->StartEntry); 626 - return; 627 - } 628 - 629 - if (pJob->StartTime.QuadPart < CurrentJob->StartTime.QuadPart) 630 - { 631 - /* Insert between the previous and the current entry */ 632 - PreviousEntry = CurrentEntry->Blink; 633 - pJob->StartEntry.Blink = PreviousEntry; 634 - pJob->StartEntry.Flink = CurrentEntry; 635 - PreviousEntry->Flink = &pJob->StartEntry; 636 - CurrentEntry->Blink = &pJob->StartEntry; 637 - return; 638 - } 639 - 640 - if ((CurrentEntry->Flink == StartListHead) && 641 - (pJob->StartTime.QuadPart >= CurrentJob->StartTime.QuadPart)) 642 - { 643 - /* Insert after the last entry */ 644 - InsertTailList(StartListHead, &pJob->StartEntry); 645 - return; 646 - } 647 - 648 - CurrentEntry = CurrentEntry->Flink; 649 - } 600 + pJob->StartTime.dwLowDateTime = LocalStartTime.u.LowPart; 601 + pJob->StartTime.dwHighDateTime = LocalStartTime.u.HighPart; 650 602 } 651 603 652 - 604 + #if 0 653 605 VOID 654 606 DumpStartList( 655 607 _In_ PLIST_ENTRY StartListHead) ··· 657 609 PLIST_ENTRY CurrentEntry; 658 610 PJOB CurrentJob; 659 611 660 - CurrentEntry = StartListHead->Flink; 661 - while (CurrentEntry != StartListHead) 612 + CurrentEntry = JobListHead->Flink; 613 + while (CurrentEntry != &JobListHead) 662 614 { 663 615 CurrentJob = CONTAINING_RECORD(CurrentEntry, JOB, StartEntry); 664 616 ··· 667 619 CurrentEntry = CurrentEntry->Flink; 668 620 } 669 621 } 670 - 622 + #endif 671 623 /* EOF */
+6 -6
base/services/schedsvc/precomp.h
··· 32 32 { 33 33 LIST_ENTRY JobEntry; 34 34 35 - LIST_ENTRY StartEntry; 36 - ULARGE_INTEGER StartTime; 35 + FILETIME StartTime; 37 36 WCHAR Name[JOB_NAME_LENGTH]; 38 37 39 38 DWORD JobId; ··· 54 53 extern LIST_ENTRY StartListHead; 55 54 extern RTL_RESOURCE StartListLock; 56 55 57 - extern HANDLE Events[2]; 56 + extern HANDLE Events[3]; 58 57 59 58 60 59 /* job.c */ 61 60 62 - DWORD 63 - GetNextJobTimeout(VOID); 61 + VOID 62 + GetNextJobTimeout( 63 + HANDLE hTimer); 64 64 65 65 VOID 66 - RunNextJob(VOID); 66 + RunCurrentJobs(VOID); 67 67 68 68 LONG 69 69 SaveJob(
-4
base/services/schedsvc/rpcserver.c
··· 121 121 /* Calculate the next start time */ 122 122 CalculateNextStartTime(pJob); 123 123 124 - /* Insert the job into the start list */ 125 - InsertJobIntoStartList(&StartListHead, pJob); 126 124 #if 0 127 125 DumpStartList(&StartListHead); 128 126 #endif ··· 169 167 170 168 if ((CurrentJob->JobId >= MinJobId) && (CurrentJob->JobId <= MaxJobId)) 171 169 { 172 - /* Remove the job from the start list */ 173 - RemoveEntryList(&CurrentJob->StartEntry); 174 170 #if 0 175 171 DumpStartList(&StartListHead); 176 172 #endif
+18 -8
base/services/schedsvc/schedsvc.c
··· 37 37 static SERVICE_STATUS_HANDLE ServiceStatusHandle; 38 38 static SERVICE_STATUS ServiceStatus; 39 39 40 - HANDLE Events[2] = {NULL, NULL}; // StopEvent, UpdateEvent 40 + HANDLE Events[3] = {NULL, NULL, NULL}; // StopEvent, UpdateEvent, Timer 41 41 42 42 43 43 /* FUNCTIONS *****************************************************************/ ··· 181 181 return GetLastError(); 182 182 } 183 183 184 + Events[2] = CreateWaitableTimerW(NULL, FALSE, NULL); 185 + if (Events[2] == NULL) 186 + { 187 + ERR("Could not create the timer\n"); 188 + CloseHandle(Events[1]); 189 + CloseHandle(Events[0]); 190 + return GetLastError(); 191 + } 192 + 184 193 return ERROR_SUCCESS; 185 194 } 186 195 ··· 188 197 VOID WINAPI 189 198 SchedServiceMain(DWORD argc, LPTSTR *argv) 190 199 { 191 - DWORD dwWait, dwTimeout, dwError; 200 + DWORD dwWait, dwError; 192 201 193 202 UNREFERENCED_PARAMETER(argc); 194 203 UNREFERENCED_PARAMETER(argv); ··· 216 225 217 226 UpdateServiceStatus(SERVICE_RUNNING); 218 227 219 - dwTimeout = GetNextJobTimeout(); 228 + GetNextJobTimeout(Events[2]); 220 229 221 230 for (;;) 222 231 { 223 232 /* Wait for the next event */ 224 233 TRACE("Wait for next event!\n"); 225 - dwWait = WaitForMultipleObjects(2, Events, FALSE, dwTimeout); 234 + dwWait = WaitForMultipleObjects(3, Events, FALSE, INFINITE); 226 235 if (dwWait == WAIT_OBJECT_0) 227 236 { 228 237 TRACE("Stop event signaled!\n"); ··· 233 242 TRACE("Update event signaled!\n"); 234 243 235 244 RtlAcquireResourceShared(&JobListLock, TRUE); 236 - dwTimeout = GetNextJobTimeout(); 245 + GetNextJobTimeout(Events[2]); 237 246 RtlReleaseResource(&JobListLock); 238 247 } 239 - else if (dwWait == WAIT_TIMEOUT) 248 + else if (dwWait == WAIT_OBJECT_0 + 2) 240 249 { 241 250 TRACE("Timeout: Start the next job!\n"); 242 251 243 252 RtlAcquireResourceExclusive(&JobListLock, TRUE); 244 - RunNextJob(); 245 - dwTimeout = GetNextJobTimeout(); 253 + RunCurrentJobs(); 254 + GetNextJobTimeout(Events[2]); 246 255 RtlReleaseResource(&JobListLock); 247 256 } 248 257 } ··· 250 259 /* Close the start and update event handles */ 251 260 CloseHandle(Events[0]); 252 261 CloseHandle(Events[1]); 262 + CloseHandle(Events[2]); 253 263 254 264 /* Stop the service */ 255 265 UpdateServiceStatus(SERVICE_STOPPED);