[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, &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;
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