From: Sebastian Lackner Subject: [5/5] services: Use threadpool API instead of custom wait implementation. Message-Id: Date: Thu, 18 Aug 2016 09:38:24 +0200 Signed-off-by: Sebastian Lackner --- 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