+\r
+void throttle_restart(nssm_service_t *service) {\r
+ /* This can't be a restart if the service is already running. */\r
+ if (! service->throttle++) return;\r
+\r
+ int ms = throttle_milliseconds(service->throttle);\r
+\r
+ if (service->throttle > 7) service->throttle = 8;\r
+\r
+ TCHAR threshold[8], milliseconds[8];\r
+ _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);\r
+ _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
+\r
+ if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
+ else if (service->throttle_timer) {\r
+ ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
+ service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
+ SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
+ }\r
+\r
+ service->status.dwCurrentState = SERVICE_PAUSED;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
+\r
+ if (use_critical_section) {\r
+ imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
+ LeaveCriticalSection(&service->throttle_section);\r
+ }\r
+ else {\r
+ if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
+ else Sleep(ms);\r
+ }\r
+}\r
+\r
+/*\r
+ When responding to a stop (or any other) request we need to set dwWaitHint to\r
+ the number of milliseconds we expect the operation to take, and optionally\r
+ increase dwCheckPoint. If dwWaitHint milliseconds elapses without the\r
+ operation completing or dwCheckPoint increasing, the system will consider the\r
+ service to be hung.\r
+\r
+ However the system will consider the service to be hung after 30000\r
+ milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not\r
+ changed. Therefore if we want to wait longer than that we must periodically\r
+ increase dwCheckPoint.\r
+\r
+ Furthermore, it will consider the service to be hung after 60000 milliseconds\r
+ regardless of the value of dwCheckPoint unless dwWaitHint is increased every\r
+ time dwCheckPoint is also increased.\r
+\r
+ Our strategy then is to retrieve the initial dwWaitHint and wait for\r
+ NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running\r
+ and we haven't finished waiting we increment dwCheckPoint and add whichever is\r
+ smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to\r
+ dwWaitHint.\r
+\r
+ Only doing both these things will prevent the system from killing the service.\r
+\r
+ Returns: 1 if the wait timed out.\r
+ 0 if the wait completed.\r
+ -1 on error.\r
+*/\r
+int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
+ unsigned long interval;\r
+ unsigned long waithint;\r
+ unsigned long ret;\r
+ unsigned long waited;\r
+ TCHAR interval_milliseconds[16];\r
+ TCHAR timeout_milliseconds[16];\r
+ TCHAR waited_milliseconds[16];\r
+ TCHAR *function = function_name;\r
+\r
+ /* Add brackets to function name. */\r
+ size_t funclen = _tcslen(function_name) + 3;\r
+ TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));\r
+ if (func) {\r
+ if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;\r
+ }\r
+\r
+ _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);\r
+\r
+ waithint = service->status.dwWaitHint;\r
+ waited = 0;\r
+ while (waited < timeout) {\r
+ interval = timeout - waited;\r
+ if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
+\r
+ service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
+ service->status.dwWaitHint += interval;\r
+ service->status.dwCheckPoint++;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
+\r
+ if (waited) {\r
+ _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
+ _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
+ }\r
+\r
+ switch (WaitForSingleObject(service->process_handle, interval)) {\r
+ case WAIT_OBJECT_0:\r
+ ret = 0;\r
+ goto awaited;\r
+\r
+ case WAIT_TIMEOUT:\r
+ ret = 1;\r
+ break;\r
+\r
+ default:\r
+ ret = -1;\r
+ goto awaited;\r
+ }\r
+\r
+ waited += interval;\r
+ }\r
+\r
+awaited:\r
+ if (func) HeapFree(GetProcessHeap(), 0, func);\r
+\r
+ return ret;\r
+}\r