diff --git a/programs/services/rpc.c b/programs/services/rpc.c index bf8ce801efc..9fe81d1a5d1 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -84,6 +84,53 @@ 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 = +{ + 0, 0, &timeout_queue_cs, + { &timeout_queue_cs_debug.ProcessLocksList, &timeout_queue_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": timeout_queue_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; + + FILETIME time; + void (*func)(struct service_entry*); + struct service_entry *service_entry; +}; + +static void run_after_timeout(void (*func)(struct service_entry*), struct service_entry *service, DWORD timeout) +{ + struct timeout_queue_elem *elem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct timeout_queue_elem)); + ULARGE_INTEGER time; + + if(!elem) { + func(service); + return; + } + + service->ref_count++; + elem->func = func; + elem->service_entry = service; + + GetSystemTimeAsFileTime(&elem->time); + time.LowPart = elem->time.dwLowDateTime; + time.HighPart = elem->time.dwHighDateTime; + time.QuadPart += timeout*10000000; + elem->time.dwLowDateTime = time.LowPart; + elem->time.dwHighDateTime = time.HighPart; + + EnterCriticalSection(&timeout_queue_cs); + list_add_head(&timeout_queue, &elem->entry); + LeaveCriticalSection(&timeout_queue_cs); + + SetEvent(timeout_queue_event); +} + static void free_service_strings(struct service_entry *old, struct service_entry *new) { QUERY_SERVICE_CONFIGW *old_cfg = &old->config; @@ -700,7 +747,7 @@ DWORD __cdecl svcctl_SetServiceStatus( service_unlock(service->service_entry); if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED) - service_terminate(service->service_entry); + run_after_timeout(service_terminate, service->service_entry, service_kill_timeout); else if (service->service_entry->status_changed_event) SetEvent(service->service_entry->status_changed_event); @@ -1567,10 +1614,16 @@ DWORD RPC_Init(void) return ERROR_SUCCESS; } -DWORD RPC_MainLoop(void) +DWORD events_loop(void) { + struct timeout_queue_elem *iter, *iter_safe; DWORD err; - HANDLE hExitEvent = __wine_make_process_system(); + HANDLE wait_handles[2]; + 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); @@ -1578,12 +1631,67 @@ DWORD RPC_MainLoop(void) do { - err = WaitForSingleObjectEx(hExitEvent, INFINITE, TRUE); + err = WaitForMultipleObjects(2, wait_handles, FALSE, timeout); WINE_TRACE("Wait returned %d\n", err); + + if(err==WAIT_OBJECT_0+1 || err==WAIT_TIMEOUT) + { + FILETIME cur_time; + ULARGE_INTEGER time; + + GetSystemTimeAsFileTime(&cur_time); + time.LowPart = cur_time.dwLowDateTime; + time.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) + { + LeaveCriticalSection(&timeout_queue_cs); + iter->func(iter->service_entry); + EnterCriticalSection(&timeout_queue_cs); + + release_service(iter->service_entry); + list_remove(&iter->entry); + HeapFree(GetProcessHeap(), 0, iter); + } + else + { + ULARGE_INTEGER time_diff; + + time_diff.LowPart = iter->time.dwLowDateTime; + time_diff.HighPart = iter->time.dwHighDateTime; + time_diff.QuadPart = (time_diff.QuadPart-time.QuadPart)/10000; + + if(time_diff.QuadPart < timeout) + timeout = time_diff.QuadPart; + } + } + LeaveCriticalSection(&timeout_queue_cs); + + if(timeout != INFINITE) + timeout += 1000; + } } while (err != WAIT_OBJECT_0); WINE_TRACE("Object signaled - wine shutdown\n"); - CloseHandle(hExitEvent); + EnterCriticalSection(&timeout_queue_cs); + LIST_FOR_EACH_ENTRY_SAFE(iter, iter_safe, &timeout_queue, struct timeout_queue_elem, entry) + { + LeaveCriticalSection(&timeout_queue_cs); + iter->func(iter->service_entry); + EnterCriticalSection(&timeout_queue_cs); + + release_service(iter->service_entry); + list_remove(&iter->entry); + HeapFree(GetProcessHeap(), 0, iter); + } + LeaveCriticalSection(&timeout_queue_cs); + + CloseHandle(wait_handles[0]); + CloseHandle(wait_handles[1]); return ERROR_SUCCESS; } diff --git a/programs/services/services.c b/programs/services/services.c index d28e47e33f9..75abf065d4c 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -40,7 +40,7 @@ HANDLE g_hStartedEvent; struct scmdatabase *active_database; DWORD service_pipe_timeout = 10000; -DWORD service_kill_timeout = 20000; +DWORD service_kill_timeout = 60000; static DWORD default_preshutdown_timeout = 180000; static void *env = NULL; @@ -914,7 +914,7 @@ int main(int argc, char *argv[]) if ((err = RPC_Init()) == ERROR_SUCCESS) { scmdatabase_autostart_services(active_database); - RPC_MainLoop(); + events_loop(); scmdatabase_wait_terminate(active_database); } scmdatabase_destroy(active_database); diff --git a/programs/services/services.h b/programs/services/services.h index b4b895eaf38..3e36141a9e3 100644 --- a/programs/services/services.h +++ b/programs/services/services.h @@ -88,7 +88,7 @@ extern DWORD service_pipe_timeout; extern DWORD service_kill_timeout; DWORD RPC_Init(void); -DWORD RPC_MainLoop(void); +DWORD events_loop(void); /* from utils.c */ LPWSTR strdupW(LPCWSTR str);