From 98a2516c22eefd912301ff3c39736da8970dcdff Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Sun, 17 Nov 2013 18:56:32 +0000 Subject: [PATCH] Enforce maximum report delay when starting service. We were not changing the service's state to running until the restart throttling threshold had passed. However best practice is to change the status to running or stopped as quickly as possible. Therefore we we now report the status after no more than 20000 milliseconds regardless of the throttle setting. --- messages.mc | 13 +++++++++++++ nssm.h | 2 +- service.cpp | 34 ++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/messages.mc b/messages.mc index 2ab7dc1..930e9e5 100644 --- a/messages.mc +++ b/messages.mc @@ -1376,3 +1376,16 @@ Language = Italian Chiamata a CreateThread() fallita: %1 . + +MessageId = +1 +SymbolicName = NSSM_EVENT_STARTUP_DELAY_TOO_LONG +Severity = Informational +Language = English +The minimum number of milliseconds which must pass before service %1 is considered to have been started successfully is set to %2. Access to the Windows service control manager is blocked until the service updates its status, therefore %3 will wait a maximum of %4 milliseconds before reporting the service's state as running. Service restart throttling will be enforced if the service runs for less than the full %2 milliseconds. +. +Language = French +The minimum number of milliseconds which must pass before service %1 is considered to have been started successfully is set to %2. Access to the Windows service control manager is blocked until the service updates its status, therefore %3 will wait a maximum of %4 milliseconds before reporting the service's state as running. Service restart throttling will be enforced if the service runs for less than the full %2 milliseconds. +. +Language = Italian +The minimum number of milliseconds which must pass before service %1 is considered to have been started successfully is set to %2. Access to the Windows service control manager is blocked until the service updates its status, therefore %3 will wait a maximum of %4 milliseconds before reporting the service's state as running. Service restart throttling will be enforced if the service runs for less than the full %2 milliseconds. +. diff --git a/nssm.h b/nssm.h index 1c1937c..1479f10 100644 --- a/nssm.h +++ b/nssm.h @@ -67,6 +67,6 @@ int str_equiv(const char *, const char *); #define NSSM_STOP_METHOD_TERMINATE (1 << 3) /* How many milliseconds to wait before updating service status. */ -#define NSSM_SHUTDOWN_CHECKPOINT 20000 +#define NSSM_SERVICE_STATUS_DEADLINE 20000 #endif diff --git a/service.cpp b/service.cpp index c24263f..be70501 100644 --- a/service.cpp +++ b/service.cpp @@ -450,13 +450,34 @@ int start_service() { close_output_handles(&si); - /* Wait for a clean startup. */ - if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0; + /* + Wait for a clean startup before changing the service status to RUNNING + but be mindful of the fact that we are blocking the service control manager + so abandon the wait before too much time has elapsed. + */ + unsigned long delay = throttle_delay; + if (delay > NSSM_SERVICE_STATUS_DEADLINE) { + char delay_milliseconds[16]; + _snprintf_s(delay_milliseconds, sizeof(delay_milliseconds), _TRUNCATE, "%lu", delay); + char deadline_milliseconds[16]; + _snprintf_s(deadline_milliseconds, sizeof(deadline_milliseconds), _TRUNCATE, "%lu", NSSM_SERVICE_STATUS_DEADLINE); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service_name, delay_milliseconds, NSSM, deadline_milliseconds, 0); + delay = NSSM_SERVICE_STATUS_DEADLINE; + } + unsigned long deadline = WaitForSingleObject(process_handle, delay); /* Signal successful start */ service_status.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(service_handle, &service_status); + /* Continue waiting for a clean startup. */ + if (deadline == WAIT_TIMEOUT) { + if (throttle_delay > delay) { + if (WaitForSingleObject(process_handle, throttle_delay - delay) == WAIT_TIMEOUT) throttle = 0; + } + else throttle = 0; + } + return 0; } @@ -639,9 +660,10 @@ void throttle_restart() { time dwCheckPoint is also increased. Our strategy then is to retrieve the initial dwWaitHint and wait for - NSSM_SHUTDOWN_CHECKPOINT milliseconds. If the process is still running and - we haven't finished waiting we increment dwCheckPoint and add whichever is - smaller of NSSM_SHUTDOWN_CHECKPOINT or the remaining timeout to dwWaitHint. + NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running + and we haven't finished waiting we increment dwCheckPoint and add whichever is + smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to + dwWaitHint. Only doing both these things will prevent the system from killing the service. @@ -672,7 +694,7 @@ int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDL waited = 0; while (waited < timeout) { interval = timeout - waited; - if (interval > NSSM_SHUTDOWN_CHECKPOINT) interval = NSSM_SHUTDOWN_CHECKPOINT; + if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE; service_status->dwCurrentState = SERVICE_STOP_PENDING; service_status->dwWaitHint += interval; -- 2.7.4