[PATCH 2/2] schedsvc: Add support for running tasks at specified time.
Dmitry Timoshkov
dmitry at baikal.ru
Wed Jun 6 01:34:32 CDT 2018
Signed-off-by: Dmitry Timoshkov <dmitry at baikal.ru>
---
dlls/schedsvc/atsvc.c | 263 +++++++++++++++++++++++++++++++++++++++
dlls/schedsvc/schedsvc_private.h | 2 +
dlls/schedsvc/svc_main.c | 35 +++++-
3 files changed, 296 insertions(+), 4 deletions(-)
diff --git a/dlls/schedsvc/atsvc.c b/dlls/schedsvc/atsvc.c
index 8f6cf03936..91ef3edca0 100644
--- a/dlls/schedsvc/atsvc.c
+++ b/dlls/schedsvc/atsvc.c
@@ -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, ¤t_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(¤t_ft);
+ FileTimeToLocalFileTime(¤t_ft, ¤t_ft);
+
+ EnterCriticalSection(&at_job_list_section);
+
+ LIST_FOR_EACH_ENTRY(job, &at_job_list, struct job_t, entry)
+ {
+ if (job_get_next_runtime(job, ¤t_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(¤t_ft);
+ FileTimeToLocalFileTime(¤t_ft, ¤t_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;
diff --git a/dlls/schedsvc/schedsvc_private.h b/dlls/schedsvc/schedsvc_private.h
index f5cec2a7cb..d601030e78 100644
--- a/dlls/schedsvc/schedsvc_private.h
+++ b/dlls/schedsvc/schedsvc_private.h
@@ -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)
{
diff --git a/dlls/schedsvc/svc_main.c b/dlls/schedsvc/svc_main.c
index 2298926193..0dfac818fd 100644
--- a/dlls/schedsvc/svc_main.c
+++ b/dlls/schedsvc/svc_main.c
@@ -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);
--
2.16.3
More information about the wine-devel
mailing list