Правильная остановка долгозавершающейся службы
От: 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
Оценка: 1 (1)
Здравствуйте, nekoriu, Вы писали:

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


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

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

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


Рабочая служба моя. Она может завершиться самостоятельно, просто делает это за 40 секунд. Наверно, может и дольше завершаться.
Re[3]: Правильная остановка долгозавершающейся службы
От: Carc Россия AmlPages.com — http://www.amlpages.com/home.php
Дата: 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 Россия AmlPages.com — http://www.amlpages.com/home.php
Дата: 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 Россия AmlPages.com — http://www.amlpages.com/home.php
Дата: 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
Оценка: 9 (1)
Здравствуйте, 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.

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