[5/5] services: Use threadpool API instead of custom wait implementation.

Sebastian Lackner sebastian at fds-team.de
Thu Aug 18 02:38:24 CDT 2016


Signed-off-by: Sebastian Lackner <sebastian at fds-team.de>
---

I was planning to do this since a long time (to get rid of the MAXIMUM_WAIT_OBJECTS limitation),
but now I have a better reason. Following patches will spawn other asynchronous tasks and its
nice to use the threadpool API for that. Unfortunately we can't get rid of the critical section
because of M$s design flaws in the threadpool wait API. ;)

 include/winbase.h            |    6 +
 programs/services/rpc.c      |  177 ++++++++++++-------------------------------
 programs/services/services.c |    9 +-
 programs/services/services.h |    5 -
 4 files changed, 66 insertions(+), 131 deletions(-)

diff --git a/include/winbase.h b/include/winbase.h
index 220a057..99e3107 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -1710,6 +1710,9 @@ WINADVAPI  BOOL        WINAPI ClearEventLogW(HANDLE,LPCWSTR);
 WINADVAPI  BOOL        WINAPI CloseEventLog(HANDLE);
 WINBASEAPI BOOL        WINAPI CloseHandle(HANDLE);
 WINBASEAPI VOID        WINAPI CloseThreadpool(PTP_POOL);
+WINBASEAPI VOID        WINAPI CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP);
+WINBASEAPI VOID        WINAPI CloseThreadpoolCleanupGroupMembers(PTP_CLEANUP_GROUP,BOOL,PVOID);
+WINBASEAPI VOID        WINAPI CloseThreadpoolWait(PTP_WAIT);
 WINBASEAPI VOID        WINAPI CloseThreadpoolWork(PTP_WORK);
 WINBASEAPI BOOL        WINAPI CommConfigDialogA(LPCSTR,HWND,LPCOMMCONFIG);
 WINBASEAPI BOOL        WINAPI CommConfigDialogW(LPCWSTR,HWND,LPCOMMCONFIG);
@@ -1775,6 +1778,8 @@ WINADVAPI  BOOL        WINAPI CreatePrivateObjectSecurity(PSECURITY_DESCRIPTOR,P
 WINADVAPI  BOOL        WINAPI CreatePrivateObjectSecurityEx(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR*,GUID*,BOOL,ULONG,HANDLE,PGENERIC_MAPPING);
 WINADVAPI  BOOL        WINAPI CreatePrivateObjectSecurityWithMultipleInheritance(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR*,GUID**,ULONG,BOOL,ULONG,HANDLE,PGENERIC_MAPPING);
 WINBASEAPI PTP_POOL    WINAPI CreateThreadpool(PVOID);
+WINBASEAPI PTP_CLEANUP_GROUP WINAPI CreateThreadpoolCleanupGroup(void);
+WINBASEAPI PTP_WAIT    WINAPI CreateThreadpoolWait(PTP_WAIT_CALLBACK,PVOID,PTP_CALLBACK_ENVIRON);
 WINBASEAPI PTP_WORK    WINAPI CreateThreadpoolWork(PTP_WORK_CALLBACK,PVOID,PTP_CALLBACK_ENVIRON);
 WINBASEAPI BOOL        WINAPI CreateProcessA(LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION);
 WINBASEAPI BOOL        WINAPI CreateProcessW(LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
@@ -2510,6 +2515,7 @@ WINBASEAPI DWORD       WINAPI SetThreadIdealProcessor(HANDLE,DWORD);
 WINBASEAPI BOOL        WINAPI SetThreadPriority(HANDLE,INT);
 WINBASEAPI BOOL        WINAPI SetThreadPriorityBoost(HANDLE,BOOL);
 WINADVAPI  BOOL        WINAPI SetThreadToken(PHANDLE,HANDLE);
+WINBASEAPI VOID        WINAPI SetThreadpoolWait(PTP_WAIT,HANDLE,FILETIME *);
 WINBASEAPI HANDLE      WINAPI SetTimerQueueTimer(HANDLE,WAITORTIMERCALLBACK,PVOID,DWORD,DWORD,BOOL);
 WINBASEAPI BOOL        WINAPI SetTimeZoneInformation(const TIME_ZONE_INFORMATION *);
 WINADVAPI  BOOL        WINAPI SetTokenInformation(HANDLE,TOKEN_INFORMATION_CLASS,LPVOID,DWORD);
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 4cab79e..4c6301c 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -85,46 +85,51 @@ struct sc_lock
     struct scmdatabase *db;
 };
 
-static HANDLE timeout_queue_event;
-static CRITICAL_SECTION timeout_queue_cs;
-static CRITICAL_SECTION_DEBUG timeout_queue_cs_debug =
+static CRITICAL_SECTION shutdown_cs;
+static CRITICAL_SECTION_DEBUG critsect_debug =
 {
-    0, 0, &timeout_queue_cs,
-    { &timeout_queue_cs_debug.ProcessLocksList, &timeout_queue_cs_debug.ProcessLocksList },
-    0, 0, { (DWORD_PTR)(__FILE__ ": timeout_queue_cs") }
+    0, 0, &shutdown_cs,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": shutdown_cs") }
 };
-static CRITICAL_SECTION timeout_queue_cs = { &timeout_queue_cs_debug, -1, 0, 0, 0, 0 };
-static struct list timeout_queue = LIST_INIT(timeout_queue);
-struct timeout_queue_elem
-{
-    struct list entry;
+static CRITICAL_SECTION shutdown_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
+static BOOL service_shutdown;
 
-    FILETIME time;
-    struct process_entry *process;
-};
+static PTP_CLEANUP_GROUP cleanup_group;
+HANDLE exit_event;
 
-static void terminate_after_timeout(struct process_entry *process, DWORD timeout)
+static void CALLBACK terminate_callback(TP_CALLBACK_INSTANCE *instance, void *context,
+                                        TP_WAIT *wait, TP_WAIT_RESULT result)
 {
-    struct timeout_queue_elem *elem;
-    ULARGE_INTEGER time;
+    struct process_entry *process = context;
+    if (result == WAIT_TIMEOUT) process_terminate(process);
+    release_process(process);
 
-    if (!(elem = HeapAlloc(GetProcessHeap(), 0, sizeof(*elem))))
-        return;
+    /* synchronize with CloseThreadpoolCleanupGroupMembers */
+    EnterCriticalSection(&shutdown_cs);
+    if (!service_shutdown) CloseThreadpoolWait(wait);
+    LeaveCriticalSection(&shutdown_cs);
+}
 
-    elem->process = grab_process(process);
+static void terminate_after_timeout(struct process_entry *process, DWORD timeout)
+{
+    TP_CALLBACK_ENVIRON environment;
+    LARGE_INTEGER timestamp;
+    TP_WAIT *wait;
+    FILETIME ft;
 
-    GetSystemTimeAsFileTime(&elem->time);
-    time.u.LowPart = elem->time.dwLowDateTime;
-    time.u.HighPart = elem->time.dwHighDateTime;
-    time.QuadPart += (ULONGLONG)timeout * 10000;
-    elem->time.dwLowDateTime = time.u.LowPart;
-    elem->time.dwHighDateTime = time.u.HighPart;
+    memset(&environment, 0, sizeof(environment));
+    environment.Version = 1;
+    environment.CleanupGroup = cleanup_group;
 
-    EnterCriticalSection(&timeout_queue_cs);
-    list_add_head(&timeout_queue, &elem->entry);
-    LeaveCriticalSection(&timeout_queue_cs);
+    timestamp.QuadPart = (ULONGLONG)timeout * -10000;
+    ft.dwLowDateTime   = timestamp.u.LowPart;
+    ft.dwHighDateTime  = timestamp.u.HighPart;
 
-    SetEvent(timeout_queue_event);
+    if ((wait = CreateThreadpoolWait(terminate_callback, grab_process(process), &environment)))
+        SetThreadpoolWait(wait, process->process, &ft);
+    else
+        release_process(process);
 }
 
 static void free_service_strings(struct service_entry *old, struct service_entry *new)
@@ -1876,6 +1881,12 @@ DWORD RPC_Init(void)
     WCHAR endpoint[] = SVCCTL_ENDPOINT;
     DWORD err;
 
+    if (!(cleanup_group = CreateThreadpoolCleanupGroup()))
+    {
+        WINE_ERR("CreateThreadpoolCleanupGroup failed with error %u\n", GetLastError());
+        return GetLastError();
+    }
+
     if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
     {
         WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
@@ -1893,106 +1904,24 @@ DWORD RPC_Init(void)
         WINE_ERR("RpcServerListen failed with error %u\n", err);
         return err;
     }
+
+    exit_event = __wine_make_process_system();
     return ERROR_SUCCESS;
 }
 
-DWORD events_loop(void)
+void RPC_Stop(void)
 {
-    struct timeout_queue_elem *iter, *iter_safe;
-    DWORD err;
-    HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
-    DWORD timeout = INFINITE;
-
-    wait_handles[0] = __wine_make_process_system();
-    wait_handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL);
-    timeout_queue_event = wait_handles[1];
-
-    SetEvent(g_hStartedEvent);
-
-    WINE_TRACE("Entered main loop\n");
-
-    do
-    {
-        DWORD num_handles = 2;
-
-        /* monitor tracked process handles for process end */
-        EnterCriticalSection(&timeout_queue_cs);
-        LIST_FOR_EACH_ENTRY(iter, &timeout_queue, struct timeout_queue_elem, entry)
-        {
-            if(num_handles == MAXIMUM_WAIT_OBJECTS){
-                WINE_TRACE("Exceeded maximum wait object count\n");
-                break;
-            }
-            wait_handles[num_handles] = iter->process->process;
-            num_handles++;
-        }
-        LeaveCriticalSection(&timeout_queue_cs);
-
-        err = WaitForMultipleObjects(num_handles, wait_handles, FALSE, timeout);
-        WINE_TRACE("Wait returned %d\n", err);
-
-        if(err > WAIT_OBJECT_0 || err == WAIT_TIMEOUT)
-        {
-            FILETIME cur_time;
-            ULARGE_INTEGER time;
-            DWORD idx = 0;
-
-            GetSystemTimeAsFileTime(&cur_time);
-            time.u.LowPart = cur_time.dwLowDateTime;
-            time.u.HighPart = cur_time.dwHighDateTime;
-
-            EnterCriticalSection(&timeout_queue_cs);
-            timeout = INFINITE;
-            LIST_FOR_EACH_ENTRY_SAFE(iter, iter_safe, &timeout_queue, struct timeout_queue_elem, entry)
-            {
-                if(CompareFileTime(&cur_time, &iter->time) >= 0 ||
-                        (err > WAIT_OBJECT_0 + 1 && idx == err - WAIT_OBJECT_0 - 2))
-                {
-                    LeaveCriticalSection(&timeout_queue_cs);
-                    process_terminate(iter->process);
-                    EnterCriticalSection(&timeout_queue_cs);
-
-                    release_process(iter->process);
-                    list_remove(&iter->entry);
-                    HeapFree(GetProcessHeap(), 0, iter);
-                }
-                else
-                {
-                    ULARGE_INTEGER time_diff;
-
-                    time_diff.u.LowPart = iter->time.dwLowDateTime;
-                    time_diff.u.HighPart = iter->time.dwHighDateTime;
-                    time_diff.QuadPart = (time_diff.QuadPart-time.QuadPart)/10000;
-
-                    if(time_diff.QuadPart < timeout)
-                        timeout = time_diff.QuadPart;
-                }
-                idx++;
-            }
-            LeaveCriticalSection(&timeout_queue_cs);
+    RpcMgmtStopServerListening(NULL);
+    RpcServerUnregisterIf(svcctl_v2_0_s_ifspec, NULL, TRUE);
 
-            if(timeout != INFINITE)
-                timeout += 1000;
-        }
-    } while (err != WAIT_OBJECT_0);
+    /* synchronize with CloseThreadpoolWait */
+    EnterCriticalSection(&shutdown_cs);
+    service_shutdown = TRUE;
+    LeaveCriticalSection(&shutdown_cs);
 
-    WINE_TRACE("Object signaled - wine shutdown\n");
-    EnterCriticalSection(&timeout_queue_cs);
-    LIST_FOR_EACH_ENTRY_SAFE(iter, iter_safe, &timeout_queue, struct timeout_queue_elem, entry)
-    {
-        LeaveCriticalSection(&timeout_queue_cs);
-        process_terminate(iter->process);
-        EnterCriticalSection(&timeout_queue_cs);
-
-        release_process(iter->process);
-        list_remove(&iter->entry);
-        HeapFree(GetProcessHeap(), 0, iter);
-    }
-    LeaveCriticalSection(&timeout_queue_cs);
-
-    CloseHandle(wait_handles[0]);
-    CloseHandle(wait_handles[1]);
-    return ERROR_SUCCESS;
+    CloseThreadpoolCleanupGroupMembers(cleanup_group, TRUE, NULL);
+    CloseThreadpoolCleanupGroup(cleanup_group);
+    CloseHandle(exit_event);
 }
 
 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
diff --git a/programs/services/services.c b/programs/services/services.c
index 1092a25..36ceae8 100644
--- a/programs/services/services.c
+++ b/programs/services/services.c
@@ -36,7 +36,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(service);
 
-HANDLE g_hStartedEvent;
 struct scmdatabase *active_database;
 
 DWORD service_pipe_timeout = 10000;
@@ -990,10 +989,10 @@ int main(int argc, char *argv[])
         'C','o','n','t','r','o','l','\\',
         'S','e','r','v','i','c','e','C','u','r','r','e','n','t',0};
     static const WCHAR svcctl_started_event[] = SVCCTL_STARTED_EVENT;
-    HANDLE htok;
+    HANDLE started_event, htok;
     DWORD err;
 
-    g_hStartedEvent = CreateEventW(NULL, TRUE, FALSE, svcctl_started_event);
+    started_event = CreateEventW(NULL, TRUE, FALSE, svcctl_started_event);
 
     if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|TOKEN_DUPLICATE, &htok))
     {
@@ -1019,8 +1018,10 @@ int main(int argc, char *argv[])
     if ((err = RPC_Init()) == ERROR_SUCCESS)
     {
         scmdatabase_autostart_services(active_database);
-        events_loop();
+        SetEvent(started_event);
+        WaitForSingleObject(exit_event, INFINITE);
         scmdatabase_wait_terminate(active_database);
+        RPC_Stop();
     }
     scmdatabase_destroy(active_database);
     if (env)
diff --git a/programs/services/services.h b/programs/services/services.h
index fb1374e..417efcd 100644
--- a/programs/services/services.h
+++ b/programs/services/services.h
@@ -98,13 +98,12 @@ void release_process(struct process_entry *process);
 BOOL process_send_command(struct process_entry *process, const void *data, DWORD size, DWORD *result);
 void process_terminate(struct process_entry *process);
 
-extern HANDLE g_hStartedEvent;
-
 extern DWORD service_pipe_timeout;
 extern DWORD service_kill_timeout;
+extern HANDLE exit_event;
 
 DWORD RPC_Init(void);
-DWORD events_loop(void);
+void RPC_Stop(void);
 
 /* from utils.c */
 LPWSTR strdupW(LPCWSTR str);
-- 
2.9.0



More information about the wine-patches mailing list