schedsvc: Add support for running tasks at specified time.

Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Dmitry Timoshkov 2018-06-06 14:34:32 +08:00 committed by Alexandre Julliard
parent a3566fd6e1
commit 70cedb3e5e
3 changed files with 296 additions and 4 deletions

View File

@ -20,6 +20,7 @@
#include <stdarg.h>
#define NONAMELESSUNION
#include "windef.h"
#include "atsvc.h"
#include "mstask.h"
@ -90,6 +91,235 @@ static CRITICAL_SECTION_DEBUG cs_debug =
};
static CRITICAL_SECTION at_job_list_section = { &cs_debug, -1, 0, 0, 0, 0 };
static void filetime_add_ms(FILETIME *ft, LONGLONG ms)
{
union u_ftll
{
FILETIME ft;
LONGLONG ll;
} *ftll = (union u_ftll *)ft;
ftll->ll += ms * (ULONGLONG)10000;
}
static void filetime_add_minutes(FILETIME *ft, LONG minutes)
{
filetime_add_ms(ft, (LONGLONG)minutes * 60 * 1000);
}
static void filetime_add_hours(FILETIME *ft, LONG hours)
{
filetime_add_minutes(ft, (LONGLONG)hours * 60);
}
static void filetime_add_days(FILETIME *ft, LONG days)
{
filetime_add_hours(ft, (LONGLONG)days * 24);
}
static void filetime_add_weeks(FILETIME *ft, ULONG weeks)
{
filetime_add_days(ft, (LONGLONG)weeks * 7);
}
static void get_begin_time(const TASK_TRIGGER *trigger, FILETIME *ft)
{
SYSTEMTIME st;
st.wYear = trigger->wBeginYear;
st.wMonth = trigger->wBeginMonth;
st.wDay = trigger->wBeginDay;
st.wDayOfWeek = 0;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, ft);
}
static void get_end_time(const TASK_TRIGGER *trigger, FILETIME *ft)
{
SYSTEMTIME st;
if (!(trigger->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE))
{
ft->dwHighDateTime = ~0u;
ft->dwLowDateTime = ~0u;
return;
}
st.wYear = trigger->wEndYear;
st.wMonth = trigger->wEndMonth;
st.wDay = trigger->wEndDay;
st.wDayOfWeek = 0;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, ft);
}
static BOOL trigger_get_next_runtime(const TASK_TRIGGER *trigger, const FILETIME *current_ft, FILETIME *rt)
{
SYSTEMTIME st, current_st;
FILETIME begin_ft, end_ft, trigger_ft;
if (trigger->rgFlags & TASK_TRIGGER_FLAG_DISABLED)
return FALSE;
FileTimeToSystemTime(current_ft, &current_st);
get_begin_time(trigger, &begin_ft);
get_end_time(trigger, &end_ft);
switch (trigger->TriggerType)
{
case TASK_EVENT_TRIGGER_ON_IDLE:
case TASK_EVENT_TRIGGER_AT_SYSTEMSTART:
case TASK_EVENT_TRIGGER_AT_LOGON:
return FALSE;
case TASK_TIME_TRIGGER_ONCE:
st = current_st;
st.wHour = trigger->wStartHour;
st.wMinute = trigger->wStartMinute;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, &trigger_ft);
if (CompareFileTime(&begin_ft, &trigger_ft) <= 0 && CompareFileTime(&trigger_ft, &end_ft) < 0)
{
*rt = trigger_ft;
return TRUE;
}
break;
case TASK_TIME_TRIGGER_DAILY:
st = current_st;
st.wHour = trigger->wStartHour;
st.wMinute = trigger->wStartMinute;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, &trigger_ft);
while (CompareFileTime(&trigger_ft, &end_ft) < 0)
{
if (CompareFileTime(&trigger_ft, &begin_ft) >= 0)
{
*rt = trigger_ft;
return TRUE;
}
filetime_add_days(&trigger_ft, trigger->Type.Daily.DaysInterval);
}
break;
case TASK_TIME_TRIGGER_WEEKLY:
if (!trigger->Type.Weekly.rgfDaysOfTheWeek)
break; /* avoid infinite loop */
st = current_st;
st.wHour = trigger->wStartHour;
st.wMinute = trigger->wStartMinute;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, &trigger_ft);
while (CompareFileTime(&trigger_ft, &end_ft) < 0)
{
FileTimeToSystemTime(&trigger_ft, &st);
if (CompareFileTime(&trigger_ft, &begin_ft) >= 0)
{
if (trigger->Type.Weekly.rgfDaysOfTheWeek & (1 << st.wDayOfWeek))
{
*rt = trigger_ft;
return TRUE;
}
}
if (st.wDayOfWeek == 0 && trigger->Type.Weekly.WeeksInterval > 1) /* Sunday, goto next week */
filetime_add_weeks(&trigger_ft, trigger->Type.Weekly.WeeksInterval - 1);
else /* check next weekday */
filetime_add_days(&trigger_ft, 1);
}
break;
default:
FIXME("trigger type %u is not handled\n", trigger->TriggerType);
break;
}
return FALSE;
}
static BOOL job_get_next_runtime(struct job_t *job, FILETIME *current_ft, FILETIME *next_rt)
{
FILETIME trigger_rt;
BOOL have_next_rt = FALSE;
USHORT i;
for (i = 0; i < job->trigger_count; i++)
{
if (trigger_get_next_runtime(&job->trigger[i], current_ft, &trigger_rt))
{
if (!have_next_rt || CompareFileTime(&trigger_rt, next_rt) < 0)
{
*next_rt = trigger_rt;
have_next_rt = TRUE;
}
}
}
return have_next_rt;
}
/* Returns next runtime in UTC */
BOOL get_next_runtime(LARGE_INTEGER *rt)
{
FILETIME current_ft, job_rt, next_job_rt;
BOOL have_next_rt = FALSE;
struct job_t *job;
GetSystemTimeAsFileTime(&current_ft);
FileTimeToLocalFileTime(&current_ft, &current_ft);
EnterCriticalSection(&at_job_list_section);
LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
{
if (job_get_next_runtime(job, &current_ft, &job_rt))
{
if (!have_next_rt || CompareFileTime(&job_rt, &next_job_rt) < 0)
{
next_job_rt = job_rt;
have_next_rt = TRUE;
}
}
}
LeaveCriticalSection(&at_job_list_section);
if (have_next_rt)
{
LocalFileTimeToFileTime(&next_job_rt, &next_job_rt);
rt->u.LowPart = next_job_rt.dwLowDateTime;
rt->u.HighPart = next_job_rt.dwHighDateTime;
}
return have_next_rt;
}
static BOOL job_runs_at(struct job_t *job, FILETIME *begin_ft, FILETIME *end_ft)
{
FILETIME job_ft;
if (job_get_next_runtime(job, begin_ft, &job_ft))
{
if (CompareFileTime(&job_ft, end_ft) < 0)
return TRUE;
}
return FALSE;
}
static DWORD load_unicode_strings(const char *data, DWORD limit, struct job_t *job)
{
DWORD i, data_size = 0;
@ -729,6 +959,39 @@ void check_task_state(void)
LeaveCriticalSection(&at_job_list_section);
}
static void run_job(struct job_t *job)
{
job->data.flags |= 0x04000000;
update_job_status(job);
}
void check_task_time(void)
{
FILETIME current_ft, begin_ft, end_ft;
struct job_t *job;
GetSystemTimeAsFileTime(&current_ft);
FileTimeToLocalFileTime(&current_ft, &current_ft);
/* Give -1/+1 minute margin */
begin_ft = current_ft;
filetime_add_minutes(&begin_ft, -1);
end_ft = current_ft;
filetime_add_minutes(&end_ft, 1);
EnterCriticalSection(&at_job_list_section);
LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
{
if (job_runs_at(job, &begin_ft, &end_ft))
{
run_job(job);
}
}
LeaveCriticalSection(&at_job_list_section);
}
void remove_job(const WCHAR *name)
{
struct job_t *job;

View File

@ -28,6 +28,8 @@ void remove_job(const WCHAR *name) DECLSPEC_HIDDEN;
void check_task_state(void) DECLSPEC_HIDDEN;
void add_process_to_queue(HANDLE hproc) DECLSPEC_HIDDEN;
void update_process_status(DWORD pid) DECLSPEC_HIDDEN;
BOOL get_next_runtime(LARGE_INTEGER *rt) DECLSPEC_HIDDEN;
void check_task_time(void) DECLSPEC_HIDDEN;
static inline WCHAR *heap_strdupW(const WCHAR *src)
{

View File

@ -46,12 +46,20 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
{
static const WCHAR tasksW[] = { '\\','T','a','s','k','s','\\',0 };
WCHAR path[MAX_PATH];
HANDLE htasks, hport;
HANDLE htasks, hport, htimer;
JOBOBJECT_ASSOCIATE_COMPLETION_PORT info;
OVERLAPPED ov;
LARGE_INTEGER period;
TRACE("Starting...\n");
htimer = CreateWaitableTimerW(NULL, FALSE, NULL);
if (htimer == NULL)
{
ERR("CreateWaitableTimer failed\n");
return -1;
}
GetWindowsDirectoryW(path, MAX_PATH);
lstrcatW(path, tasksW);
@ -98,7 +106,7 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
FILE_NOTIFY_INFORMATION data;
WCHAR name_buffer[MAX_PATH];
} info;
HANDLE events[3];
HANDLE events[4];
DWORD ret;
/* the buffer must be DWORD aligned */
@ -115,12 +123,22 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
FIXME("got multiple entries\n");
events[0] = done_event;
events[1] = ov.hEvent;
events[1] = htimer;
events[2] = hport;
events[3] = ov.hEvent;
ret = WaitForMultipleObjects(3, events, FALSE, INFINITE);
ret = WaitForMultipleObjects(4, events, FALSE, INFINITE);
/* Done event */
if (ret == WAIT_OBJECT_0) break;
/* Next runtime timer */
if (ret == WAIT_OBJECT_0 + 1)
{
check_task_time();
continue;
}
/* Job queue */
if (ret == WAIT_OBJECT_0 + 2)
{
DWORD msg;
@ -139,6 +157,7 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
continue;
}
/* Directory change notification */
info.data.FileName[info.data.FileNameLength/sizeof(WCHAR)] = 0;
switch (info.data.Action)
@ -176,8 +195,16 @@ static DWORD WINAPI tasks_monitor_thread(void *arg)
}
check_task_state();
if (get_next_runtime(&period))
{
if (!SetWaitableTimer(htimer, &period, 0, NULL, NULL, FALSE))
ERR("SetWaitableTimer failed\n");
}
}
CancelWaitableTimer(htimer);
CloseHandle(htimer);
CloseHandle(ov.hEvent);
CloseHandle(hport);
CloseHandle(hjob_queue);