Spawn a separate thread for stop_service().

We must acknowledge a STOP or SHUTDOWN control promptly but the
termination of the application may take a significant amount of time if
one of the AppStopMethod* registry values is set.

We now spawn a separate thread to try to stop the process and to call
await_shutdown() while the main thread immediately acknowledges receipt
of the STOP request.  Once the worker thread has updated the service
status to say that application is really stopped we will be
automatically cleaned up by the system.

If for some reason we can't spawn a new thread we log an error and
ignore user-supplied timeouts so as to ensure we tidy up promptly.
master
Iain Patterson 11 years ago
parent 99c5c2868f
commit a3ad2151ad

@ -1360,3 +1360,19 @@ Language = Italian
%1 has waited %3 of %5 milliseconds for the %2 service to exit.
Next update in %4 milliseconds.
.
MessageId = +1
SymbolicName = NSSM_EVENT_CREATETHREAD_FAILED
Severity = Error
Language = English
CreateThread() failed:
%1
.
Language = French
CreateThread() a échoué:
%1
.
Language = Italian
Chiamata a CreateThread() fallita:
%1
.

@ -37,6 +37,14 @@ static inline int throttle_milliseconds() {
return ret * 1000;
}
/*
Wrapper to be called in a new thread so that we can acknowledge a STOP
control immediately.
*/
static unsigned long WINAPI shutdown_service(void *arg) {
return stop_service(0, true, true);
}
/* Connect to the service manager */
SC_HANDLE open_service_manager() {
SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
@ -343,7 +351,24 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
log_service_control(service_name, control, true);
stop_service(0, true, true);
/*
We MUST acknowledge the stop request promptly but we're committed to
waiting for the application to exit. Spawn a new thread to wait
while we acknowledge the request.
*/
if (! CreateThread(NULL, 0, shutdown_service, (void *) service_name, 0, NULL)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
/*
We couldn't create a thread to tidy up so we'll have to force the tidyup
to complete in time in this thread.
*/
kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
stop_service(0, true, true);
}
return NO_ERROR;
case SERVICE_CONTROL_CONTINUE:
@ -449,9 +474,6 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
if (graceful) {
service_status.dwCurrentState = SERVICE_STOP_PENDING;
service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
if (stop_method & NSSM_STOP_METHOD_CONSOLE && imports.AttachConsole) service_status.dwWaitHint += kill_console_delay;
if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += kill_window_delay;
if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += kill_threads_delay;
SetServiceStatus(service_handle, &service_status);
}

Loading…
Cancel
Save