From 4a41a838e9ff330fe999a61b2b8d2e8da9289042 Mon Sep 17 00:00:00 2001 From: Mikolaj Zalewski Date: Wed, 26 Sep 2007 14:58:54 -0700 Subject: [PATCH] advapi32: exit the service process when the last service thread stops --- dlls/advapi32/service.c | 156 +++++++++++++++++++++++++++++++++++------------ 1 files changed, 116 insertions(+), 40 deletions(-) diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index 666be6e..d0f3e68 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -60,6 +60,9 @@ static const GENERIC_MAPPING svc_generic SERVICE_ALL_ACCESS }; +static ULONG g_num_services = 0; +static BOOL g_service_process_shutdown = FALSE; + typedef struct service_start_info_t { DWORD cmd; @@ -235,6 +238,34 @@ static inline LPWSTR SERV_dupmulti(LPCST } /****************************************************************************** + * registering functions that will be executed in the main thread if the given + * handle will become signaled + */ +struct handle_wait +{ + struct list entry; + HANDLE handle; + PAPCFUNC apc; + ULONG_PTR dwParam; +}; + +static struct list g_wait_handles; +static HANDLE g_handle_list_changed; + +void register_wait_handle(HANDLE handle, PAPCFUNC apc, ULONG_PTR dwParam) +{ + struct handle_wait *wait = HeapAlloc(GetProcessHeap(), 0, sizeof(struct handle_wait)); + wait->handle = handle; + wait->apc = apc; + wait->dwParam = dwParam; + + EnterCriticalSection(&service_cs); + list_add_tail(&g_wait_handles, &wait->entry); + LeaveCriticalSection(&service_cs); + SetEvent(g_handle_list_changed); +} + +/****************************************************************************** * registry access functions and data */ static const WCHAR szDisplayName[] = { @@ -369,6 +400,27 @@ static HANDLE service_get_event_handle(L } /****************************************************************************** + * service_reap_thread + */ +static void CALLBACK service_reap_thread(ULONG_PTR dwParam) +{ + service_data *service = (service_data *)dwParam; + + TRACE("Reaping service %s thread\n", wine_dbgstr_w(service->name)); + + EnterCriticalSection(&service_cs); + CloseHandle(service->thread); + service->thread = 0; + g_num_services--; + if (g_num_services == 0) + { + TRACE("Last service thread finished - process shutdown\n"); + g_service_process_shutdown = TRUE; + } + LeaveCriticalSection(&service_cs); +} + +/****************************************************************************** * service_thread * * Call into the main service routine provided by StartServiceCtrlDispatcher. @@ -459,9 +511,24 @@ static BOOL service_handle_start(HANDLE HeapFree(GetProcessHeap(), 0, service->args); service->args = args; args = NULL; + + EnterCriticalSection(&service_cs); + if (g_service_process_shutdown) + { + /* possible race condition - request while process is shutting down */ + LeaveCriticalSection(&service_cs); + result = ERROR_SERVICE_CANNOT_ACCEPT_CTRL; + goto end; + } + else + g_num_services++; + LeaveCriticalSection(&service_cs); + service->thread = CreateThread( NULL, 0, service_thread, service, 0, NULL ); + register_wait_handle(service->thread, service_reap_thread, (ULONG_PTR)service); + end: HeapFree(GetProcessHeap(), 0, args); WriteFile( pipe, &result, sizeof result, &read, NULL ); @@ -654,24 +721,6 @@ static BOOL service_handle_control(HANDL } /****************************************************************************** - * service_reap_thread - */ -static DWORD service_reap_thread(service_data *service) -{ - DWORD exitcode = 0; - - if (!service->thread) - return 0; - GetExitCodeThread(service->thread, &exitcode); - if (exitcode!=STILL_ACTIVE) - { - CloseHandle(service->thread); - service->thread = 0; - } - return exitcode; -} - -/****************************************************************************** * service_control_dispatcher */ static DWORD WINAPI service_control_dispatcher(LPVOID arg) @@ -720,8 +769,6 @@ static DWORD WINAPI service_control_disp break; } - service_reap_thread(service); - /* handle the request */ switch (req[0]) { @@ -751,50 +798,79 @@ static DWORD WINAPI service_control_disp */ static BOOL service_run_threads(void) { + const int builtins_count = 2; service_data *service; - DWORD count, n = 0; HANDLE *handles; + DWORD max_count; - EnterCriticalSection( &service_cs ); - - count = list_count( &service_list ); - - TRACE("Starting %d pipe listener threads. Services running as process %d\n", count, GetCurrentProcessId()); + list_init(&g_wait_handles); + g_handle_list_changed = CreateEventW(NULL, FALSE, FALSE, NULL); - handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE) * (count + 1)); + EnterCriticalSection( &service_cs ); - handles[n++] = __wine_make_process_system(); + TRACE("Starting %d pipe listener threads. Services running as process %d\n", list_count( &service_list ), GetCurrentProcessId()); LIST_FOR_EACH_ENTRY( service, &service_list, service_data, entry ) { + HANDLE listener; service->status.dwProcessId = GetCurrentProcessId(); - handles[n++] = CreateThread( NULL, 0, service_control_dispatcher, + listener = CreateThread( NULL, 0, service_control_dispatcher, service, 0, NULL ); + CloseHandle(listener); } - assert(n == count + 1); LeaveCriticalSection( &service_cs ); + max_count = 64; /* initial value */ + handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE) * max_count); + handles[0] = __wine_make_process_system(); + handles[1] = g_handle_list_changed; + /* wait for all the threads to pack up and exit */ - while (n > 1) + while (!g_service_process_shutdown) { - DWORD ret = WaitForMultipleObjects( min(n,MAXIMUM_WAIT_OBJECTS), handles, FALSE, INFINITE ); - if (!ret) /* system process event */ + struct handle_wait *wait; + DWORD ret; + int count, n = 0; + + EnterCriticalSection(&service_cs); + count = list_count(&g_wait_handles); + if (count + builtins_count > max_count) + { + max_count = count + builtins_count; + handles = HeapReAlloc(GetProcessHeap(), 0, handles, sizeof(HANDLE) * max_count); + } + + LIST_FOR_EACH_ENTRY(wait, &g_wait_handles, struct handle_wait, entry) + { + handles[builtins_count + n] = wait->handle; + n++; + } + LeaveCriticalSection(&service_cs); + + ret = WaitForMultipleObjectsEx(count + builtins_count, handles, FALSE, INFINITE, TRUE); + + if (ret == WAIT_OBJECT_0) /* system shutdown - closing program */ { TRACE( "last user process exited, shutting down\n" ); /* FIXME: we should maybe send a shutdown control to running services */ ExitProcess(0); - } - if (ret < MAXIMUM_WAIT_OBJECTS) + } else if (ret == WAIT_OBJECT_0 + 1) + { + /* we were woken up only to rebuild the list */ + } else if (ret < MAXIMUM_WAIT_OBJECTS) { - CloseHandle( handles[ret] ); - memmove( &handles[ret], &handles[ret+1], (n - ret - 1) * sizeof(HANDLE) ); - n--; + int i; + + wait = (struct handle_wait *)list_head(&g_wait_handles); + for (i = WAIT_OBJECT_0 + builtins_count; i < ret; i++) + wait = (struct handle_wait *)list_next(&g_wait_handles, &wait->entry); + list_remove(&wait->entry); + wait->apc(wait->dwParam); + HeapFree(GetProcessHeap(), 0, wait); } - else break; } - while (n) CloseHandle( handles[--n] ); HeapFree(GetProcessHeap(), 0, handles); return TRUE; -- 1.4.1