1

I am writting a small server with forever loop and I want to have an ability to stop it correctly, for instance, on Ctrl + C (SIGINT).

class Server
{
...
public:
  void loop()
  {
    while(_run_flag)
    {
      // do something
    }
  }
  void stop()
  {
    _run_flag = 0;
  }
...
private:
  volatile sig_atomic_t _run_flag = 1;
};

namespace
{
std::function<void()> callback_wrapper;

void signal_handler(int)
{
  callback_wrapper();
}

}

int main()
{
  std::unique_ptr<WSGIServer> server = CreateServer("127.0.0.1", 4009);
  callback_wrapper = [&](){ server->stop(); };

  struct sigaction sigIntHandler;
  sigIntHandler.sa_handler = signal_handler;
  sigemptyset(&sigIntHandler.sa_mask);
  sigIntHandler.sa_flags = 0;
  sigaction(SIGINT, &sigIntHandler, NULL);

  server->loop();

  return 0;
}

And I have the following questions:

  1. What is the type of run_flag_variable I should use? Is using volatile sig_atomic_t ok?
  2. Is it safe to call stop method in signal_handler?
Igor
  • 477
  • 5
  • 13

1 Answers1

1

Seems almost legit, except I could suggest few improvements. One is not so serious: if you can use C++ only, I mean primitives form csignal header, and try to avoid volatile we have already atomic stuff, more you can read my answer here, thought the question is not much relevant. For the second, there are many caveats with what you can and cannot do in signal handlers, C++-14,17 have changed a lot, besides there are some non standards, but platform-specific ways of doing stuff, however the simplest rule is you must use atomic/volatile stuff, if you do not, you must have a good reason and be platform-specific.

std::atomic_bool global_run_flag(true);

class Server {
public:
  Server() : _local_run_flag(true) {}

  void loop() {
    while (_local_run_flag && global_run_flag) {
      // do something
    }
  }
  void stop() { _local_run_flag = false; }

private:
  std::atomic_bool _local_run_flag;
};

void signal_handler(int) { global_run_flag = false; }

int main() {
  std::unique_ptr<Server> server(new Server);

  std::signal(SIGINT, &signal_handler);

  server->loop();

  return 0;
}

I gave an example how you can track a global condition set from a signal handler, and a local, if you need one, set from other threads, for example.

Yuki
  • 3,857
  • 5
  • 25
  • 43