Правильная остановка долгозавершающейся службы
От: nekoriu Россия  
Дата: 30.03.21 13:33
Оценка:
Добрый день.

Есть задача правильно завершить долгозавершающуюся рабочую службу. Служба моя.

Написал тестовую службу для отработки завершения, которая завершается 5 минут. Через 2 минуты после отправки команды stop приложение Cлужбы выдает ошибку, Служба не ответила на запрос своевременно.

Тестовая служба отправляет сообщение SERVICE_STOP_PENDING раз в секунду при завершении. Используется dwCheckPoint, dwWaitHint.

Windows 10.

Возможно ли без ошибок завершить тестовую службу в приложении Службы?

Часть ServiceMain.
while (1)
{
    WaitForSingleObject(ghSvcStopEvent, INFINITE);

    for (int i = 0; i < 300; i++)
    {
        ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 2000);
        Sleep(1000);
    }

    ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);

    return;
}

Обработка сообщений от SCM.
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
{
   switch(dwCtrl) 
   {  
      case SERVICE_CONTROL_STOP:
         ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

         // Signal the service to stop.
         SetEvent(ghSvcStopEvent);
         
         return;
 
      case SERVICE_CONTROL_INTERROGATE: 
     ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
         break; 
 
      default: 
         break;
   } 
}

Сообщение о статусе SCMу.
VOID ReportSvcStatus( DWORD dwCurrentState,
                      DWORD dwWin32ExitCode,
                      DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;

    gSvcStatus.dwCurrentState = dwCurrentState;
    gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
    gSvcStatus.dwWaitHint = dwWaitHint;

    if (dwCurrentState == SERVICE_START_PENDING)
        gSvcStatus.dwControlsAccepted = 0;
    else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

    if ( (dwCurrentState == SERVICE_RUNNING) ||
           (dwCurrentState == SERVICE_STOPPED) )
        gSvcStatus.dwCheckPoint = 0;
    else gSvcStatus.dwCheckPoint = dwCheckPoint++;

    // Report the status of the service to the SCM.
    SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
}
Отредактировано 01.04.2021 7:15 nekoriu . Предыдущая версия . Еще …
Отредактировано 30.03.2021 14:52 nekoriu . Предыдущая версия .
Отредактировано 30.03.2021 13:43 nekoriu . Предыдущая версия .
Re: Правильная остановка долгозавершающейся службы
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 30.03.21 14:42
Оценка: 2 (2) +1
Здравствуйте, nekoriu, Вы писали:

N>Есть задача правильно завершить долгозавершающуюся службу.


Если эта служба Ваша, то единственно правильным будет сделать ее так, чтобы она завершалась максимум за 10-20 секунд.

Если нет, то после истечения таймаута прибивать через TerminateProcess.
Re[2]: Правильная остановка долгозавершающейся службы
От: nekoriu Россия  
Дата: 30.03.21 14:50
Оценка:
ЕМ>Если эта служба Ваша, то единственно правильным будет сделать ее так, чтобы она завершалась максимум за 10-20 секунд.

ЕМ>Если нет, то после истечения таймаута прибивать через TerminateProcess.


Рабочая служба моя. Она может завершиться самостоятельно, просто делает это за 40 секунд. Наверно, может и дольше завершаться.
Re[3]: Правильная остановка долгозавершающейся службы
От: Carc Россия https://vk.com/gosha_mazov
Дата: 30.03.21 15:11
Оценка:
Здравствуйте, nekoriu, Вы писали:

ЕМ>>Если эта служба Ваша, то единственно правильным будет сделать ее так, чтобы она завершалась максимум за 10-20 секунд.


ЕМ>>Если нет, то после истечения таймаута прибивать через TerminateProcess.


N>Рабочая служба моя. Она может завершиться самостоятельно, просто делает это за 40 секунд. Наверно, может и дольше завершаться.

А если не секрет, в чем причина такой могучей задержки? Что она — служба — делает-то в это время да так долго?
Aml Pages Home
Re[4]: Правильная остановка долгозавершающейся службы
От: nekoriu Россия  
Дата: 31.03.21 06:39
Оценка:
C>А если не секрет, в чем причина такой могучей задержки? Что она — служба — делает-то в это время да так долго?
Служба моя в том смысле, что у меня есть ее код. Писал ее не я. Мне нужно подправить ее завершение. Служба работает с несколькими БД, имеет несколько плагинов, прослушивает сокет и выполняет команды. Завершение 40 секунд у Debug конфигурации. Надо будет попробовать с конфигурацией Release.
Re[5]: Правильная остановка долгозавершающейся службы
От: Carc Россия https://vk.com/gosha_mazov
Дата: 31.03.21 07:25
Оценка:
Здравствуйте, nekoriu, Вы писали:

C>>А если не секрет, в чем причина такой могучей задержки? Что она — служба — делает-то в это время да так долго?

N>Служба моя в том смысле, что у меня есть ее код. Писал ее не я. Мне нужно подправить ее завершение. Служба работает с несколькими БД, имеет несколько плагинов, прослушивает сокет и выполняет команды. Завершение 40 секунд у Debug конфигурации. Надо будет попробовать с конфигурацией Release.
По моему тут ошибка в архитектуре, в общей схеме организации работы службы, а не техническая проблема…

А работать с БД, и слушать сокет нельзя что ли в соседнем потоке? Когда основной поток службы получит сигнал о завершении, то пусть взведет какой-нить примитив синхронизации (mutex, event и.т.п). Ну, а соответственно фоновые потоки (работа с БД, сокет) пусть периодически проверяют этот примитив синхронизации. И коль примитив взведен, то сразу на выход…

Не? Так не подойдет разве?
Aml Pages Home
Отредактировано 31.03.2021 7:26 Carc . Предыдущая версия .
Re[6]: Правильная остановка долгозавершающейся службы
От: nekoriu Россия  
Дата: 31.03.21 08:58
Оценка:
C>По моему тут ошибка в архитектуре, в общей схеме организации работы службы, а не техническая проблема…

C>А работать с БД, и слушать сокет нельзя что ли в соседнем потоке? Когда основной поток службы получит сигнал о завершении, то пусть взведет какой-нить примитив синхронизации (mutex, event и.т.п). Ну, а соответственно фоновые потоки (работа с БД, сокет) пусть периодически проверяют этот примитив синхронизации. И коль примитив взведен, то сразу на выход…


C>Не? Так не подойдет разве?

Архитектуру менять — большие затраты. Соседние потоки есть и они выполняют свои функции. Архитектура такая, как вы описали. Нужно ждать завершения фоновых потоков, сразу на выход они не могут.
Re[7]: Правильная остановка долгозавершающейся службы
От: Carc Россия https://vk.com/gosha_mazov
Дата: 31.03.21 09:57
Оценка:
Здравствуйте, nekoriu, Вы писали:

C>>По моему тут ошибка в архитектуре, в общей схеме организации работы службы, а не техническая проблема…


C>>А работать с БД, и слушать сокет нельзя что ли в соседнем потоке? Когда основной поток службы получит сигнал о завершении, то пусть взведет какой-нить примитив синхронизации (mutex, event и.т.п). Ну, а соответственно фоновые потоки (работа с БД, сокет) пусть периодически проверяют этот примитив синхронизации. И коль примитив взведен, то сразу на выход…


C>>Не? Так не подойдет разве?

N>Архитектуру менять — большие затраты.
А зачем ее менять, если архитектура, судя во Вашей цитате, и так «Архитектура такая, как вы описали»!?!
Значит всё и так боле-менее хорошо…

N>Соседние потоки есть и они выполняют свои функции. Архитектура такая, как вы описали.

N>Нужно ждать завершения фоновых потоков, сразу на выход они не могут.
Это кагбэ понятно, что нужно ждать… А почему фоновые потоки так долго выходят?

PS: полагаю, ересь, сейчас напишу… Понимаю, что все равно это из разряда изменения архитектуры.
Но всё ж… В стиле мозгового штурма, когда даже идиотские идеи вносятся в список, и откладываются на время для дальнейшего анализа.

А что если код этих самых "тяжелых" задач (БД, слушки сокетов) вынести вообще в отдельный процесс (не службу)?
И схема работы примерно такая:
    1) Стартует служба
    2) Запускает все эти "тяжелые" задачи (БД и прочия) в отдельном обычном процессе..
    3) Синхронится с этими отдельными процессами как-то там через примитив (какой-нить именованный эвент что ли).
    4) Служба получает сигнал "остановиться".
    5) Взводит какой-нить примитив для тяжелых задач на предмет "давай заканчивай" и быстренько выходит…
    6) Эти "тяжелые" задачи посматривают на примитив "давай заканчивай" и по мере возможности постепенно завершаются.

В этом случае служба суть всего лишь управление, и она может быстро завершиться. А "тяжелые" задачи — не службы. Они могут завершаться сколько им надо.
Если системе невтерпёж, и она прибьет такой фоновый процесс с тяжелой задачей, то и фиг с ним. Это те же проблемы, что и с TerminateThread, только в профиль. Но есть вариант, что фоновый процесс с тяжелой задачей завершиться нормально, пусть хоть и большЕй задержкой…

Как-то так!?!
Правда, синхронизацию там более чем аккуратно придется писать. Плюс (возможно) понадобится еще и передача данных между процессами (pipes, shared memory и иже с ними). Та еще развлекуха, если вдруг что пойдет не так…
Aml Pages Home
Re: Правильная остановка долгозавершающейся службы
От: nekoriu Россия  
Дата: 01.04.21 07:13
Оценка:
Возможно, приложению Службы по-барабану dwCheckPoint, dwWaitHint.

У меня ошибка в SvcCtrlHandler(). Обработка сообщения SERVICE_CONTROL_INTERROGATE должна быть такой.
      case SERVICE_CONTROL_INTERROGATE: 
     ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
         break;
Отредактировано 01.04.2021 7:22 nekoriu . Предыдущая версия .
Re: Правильная остановка долгозавершающейся службы
От: Mr.Delphist  
Дата: 02.04.21 12:17
Оценка: 19 (2)
Здравствуйте, nekoriu, Вы писали:

N>Написал тестовую службу для отработки завершения, которая завершается 5 минут. Через 2 минуты после отправки команды stop приложение Cлужбы выдает ошибку, Служба не ответила на запрос своевременно.


N>Возможно ли без ошибок завершить тестовую службу в приложении Службы?


Я бы попробовал вот это:
https://www.coretechnologies.com/blog/windows-services/increase-shutdown-time/

With a simple code change, a preshutdown service can instruct Windows to wait more than the default 3 minutes for the service to end in the preshutdown phase.

You must add code to call the ChangeServiceConfig2 function with the SERVICE_CONFIG_PRESHUTDOWN_INFO level, and the timeout value in the SERVICE_PRESHUTDOWN_INFO structure set to an appropriate value.

Here is what the addition looks like in C++ (error checking omitted for brevity):

   SC_HANDLE hService =    OpenService(...);
   ...
   SERVICE_PRESHUTDOWN_INFO info;
   info.dwPreshutdownTimeout = 5 * 60 * 1000; // 5 minutes
   ChangeServiceConfig2(hService, SERVICE_CONFIG_PRESHUTDOWN_INFO, &info);


The new code is best called once when you install/setup your service, but it can be invoked during normal operation as well. Whichever works better in your situation.

Re: Правильная остановка долгозавершающейся службы
От: Игорь Вартанов https://mvp.support.microsoft.com/profile=3317CC31-AB7A-4D36-864E-47DEFF433151
Дата: 14.04.22 17:08
Оценка:
Здравствуйте, nekoriu, Вы писали:

N>Есть задача правильно завершить долгозавершающуюся рабочую службу. Служба моя.


N>Написал тестовую службу для отработки завершения, которая завершается 5 минут. Через 2 минуты после отправки команды stop приложение Cлужбы выдает ошибку, Служба не ответила на запрос своевременно.


Несмотря на застарелый тред (больше года утекло) попытаюсь ответить. Возможно, кому-то будет полезно.

Я написал свой тестовый сервис, который использует механизм чекпоинтов. 5 минут стопится, отрабатывает без проблем, Win7 Pro. Здесь нужно добавить, что управлял я сервисом при помощи утилиты sc.exe, поскольку давно привык к этому в разработке. Запомним этот момент.

Затем посетила меня мысль пойти в оснастку сервисов и поуправлять при помощи неё. Вуаля, через 2 минуты получаю "Служба не ответила на запрос..." Опять же нужно добавить, что при остановке службы я любовался прогресс-баром оснастки. Окей, кто-то здесь лишний, ведь я точно знаю, что сервис рабочий.

(между тем, пришлось ребутнуть винду, т.к. сервис не хотел уходить из оснастки)

После перезагрузки снова запускаю и останавливаю мой сервис из оснастки, но теперь сразу отказываюсь от диалога с прогресс-баром. Контролирую остановку, как и раньше, командами sc query SvcLongStop. (SvcLongStop — название сервиса) Через 5 минут сервис штатно останавливается, но оснастка об этом не знает. Жму "Обновить" и вижу что сервис отражается теперь и в оснастке, как остановленный.

Итого в сухом остатке:

Нелишне добавить, что документация требует указывать dwWaitHint:

The estimated time required for a pending start, stop, pause, or continue operation, in milliseconds.

Какбы нелогично указывать его равным нулю. Но я потратил время на проверки (Win7 Pro) и не увидел разницы в поведении между 0 ms, точным значением таймаута между чекпоинтами 1000 ms и целиком пятиминутным значением. Сервисменеджеру просто по барабану. Ну и ладно. Смотри выше про майкрософт.



Тестовый сервис: http://files.rsdn.org/83/SvcLongStop.zip
---
С уважением,
Игорь
Отредактировано 14.04.2022 17:13 Игорь Вартанов . Предыдущая версия .
Re[2]: Правильная остановка долгозавершающейся службы
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 14.04.22 17:20
Оценка: 1 (1) +1
Здравствуйте, Игорь Вартанов, Вы писали:

ИВ>

Или не выпендриваться, и не допускать задержек, не имеющих убедительного обоснования.
Re[3]: Правильная остановка долгозавершающейся службы
От: paradok  
Дата: 19.08.22 12:30
Оценка: 9 (1) -2
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Здравствуйте, Игорь Вартанов, Вы писали:


ИВ>>

ЕМ>Или не выпендриваться, и не допускать задержек, не имеющих убедительного обоснования.


Да, да. Обязательно надо убедительно убедить и не а бы кого, а именно Музыченко!

очевидно же, что обоснованность относительна!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.