Writing windows daemons in C++20

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.

My favorite C++20 feature

As I evolved my programming style away from mutating long-lived “big” objects and structures and towards are more functional and data-oriented style based mainly on pure functions, I also find myself needing a lot more structs. These naturally occur as return types for functions with ‘richer’ output if you do not want to use std::tuple or other ad-hoc types everywhere. If you see a program as a sequence of data-transformations, I guess the structs are the immediate representations encoded in the type system.

Let my first clarify what I mean by structs, as opposed to what the language says: A type that has all public data members, obeys the rule of zero, and is valid in any configuration. A typical struct v3 { float x{},y{},z{};}; 3d vector is a struct, std::vector is not.

These types are great. You can copy them around, use them with structured binding, they correctly propagate constness, and they are a great fit to pass them through layers of functions calls. And, when used as function parameters, they are great for evolving your program over time, because you can just change the single struct, as opposed to every function call that uses this parameter combination. Or you can easily batch, or otherwise ‘delay’, calls by recording the function parameters. Just throw the parameters into a container and execute the code later.

And with C++20, they got even better, because now you can use them with my favorite new feature: designated initializers, which allows you to use the member names at the initialization site and use RAII. E.g., for a struct that symbolizes an http request: struct http_request { http_method method; std::string url; std::vector<header_entry> headers; }; You can now initialize it like this:

auto request = http_request{
  .method = http_method::get,
  .uri = "localhost:7634",
  .headers = { { .name = "Authorization", .value = "Bearer TOKEN" } },
};

You can even use this directly as a parameter without repeating the type name, de facto giving your named parameters for a pair of extra curlys:

run_request({
    .method = http_method::get,
    .uri = "localhost:7634",
    .headers = { { .name = "Authorization", .value = "Bearer TOKEN" } },
});

You can, of course, combine this named-parameter style-struct with other function parameters in your API, but like with lambdas, I think they are most readable as the last parameter. Hence, also like with lambdas, you probably never want to have more than one at each call-site. I’m very happy with this new feature and it’s already making the code using my APIs a lot more readable.