One little snippet I’ve found myself reusing surprisingly often is how to write a daemon program with graceful shutdown in windows. To recap, a daemon is a program that sits and does ‘background work’ until it is explicitly shut down by the user. For my purposes, it is also a console program. Like this one:
int main(int argn, char** argv)
{
while (true)
{
std::cout << "ping!" << std::endl;
std::this_thread::sleep_for(100ms);
}
std::cout << "shutdown!" << std::endl;
return EXIT_SUCCESS;
}
If you run this program, it will, of course, continuously print “ping!”. And you can kill it by entering ctrl+C on the console. But the shutdown will not be graceful: “shutdown!” will not be printed. It’ll just look like this:
ping!
ping!
ping!
^C
C++20 introduced std::stop_source
and std::stop_token
, which help to implement a graceful shutdown. We’ll use the following code:
'namespace
{
static std::stop_source exit_source;
static std::atomic<bool> main_exited = false;
static bool already_registered = false;
static void atexit_handler()
{
main_exited = true;
}
BOOL control_handler(DWORD Type)
{
switch (Type)
{
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
exit_source.request_stop();
while (!main_exited)
Sleep(10);
return TRUE;
// Pass other signals to the next handler.
default:
return FALSE;
}
}
} // namespace
std::stop_token register_exit_signal()
{
if (!already_registered)
{
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)control_handler, TRUE))
throw std::runtime_error("Unable to register control handler");
atexit(&atexit_handler);
already_registered = true;
}
return exit_source.get_token();
}'namespace
{
static std::stop_source exit_source;
static std::atomic<bool> main_exited = false;
static bool already_registered = false;
static void atexit_handler()
{
main_exited = true;
}
BOOL control_handler(DWORD Type)
{
switch (Type)
{
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
exit_source.request_stop();
while (!main_exited)
Sleep(10);
return TRUE;
// Pass other signals to the next handler.
default:
return FALSE;
}
}
} // namespace
std::stop_token register_exit_signal()
{
if (!already_registered)
{
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)control_handler, TRUE))
throw std::runtime_error("Unable to register control handler");
atexit(&atexit_handler);
already_registered = true;
}
return exit_source.get_token();
}
You’re going to have to include both <stop_token>
and <Window.h>
for this. Now we can adapt our daemon loop slightly:
int main(int argn, char** argv)
{
auto token = register_exit_signal(); // <-- register the exit signal here
while (!token.stop_requested()) // ... and test the current state here
{
std::cout << "ping!" << std::endl;
std::this_thread::sleep_for(100ms);
}
std::cout << "shutdown!" << std::endl;
return EXIT_SUCCESS;
}
Note that this requires cooperatively handling the shutdown. But now the output correctly prints “shutdown” when killed with ctrl+C.
ping!
ping!
shutdown!
There’s linux/macOS code for this same interface too. It works by handling SIGINT
/SIGTERM
. But that information is somewhat easier to come by, so I’ll leave it out for brevity. Feel free to comment if you think that’d be interesting as well.
> There’s linux/macOS code for this same interface too. It works by handling SIGINT/SIGTERM.
Unfortunately, the list of signal-safe functions is very short and stop_token APIs are not part of it. To properly await signals one would use sigwait() or build a custom channel with send()/recv().
You cannot use stop_token -directly-, yea. But you can use std::atomic_flag and a separate thread to run it.