boostorg / beast

HTTP and WebSocket built on Boost.Asio in C++11
http://www.boost.org/libs/beast
Boost Software License 1.0
4.36k stars 635 forks source link

Example of explicit, graceful shutdown #1394

Open vinniefalco opened 5 years ago

vinniefalco commented 5 years ago

A new example, or the advanced examples, should demonstrate a clean shutdown with a grace period. It could be triggered by the first SIGINT, or perhaps by an HTTP GET request from the loopback address to a special URL (e.g. "GET /cgi-bin/stop HTTP/1.1").

See: https://github.com/ripple/rippled/commit/9ad2b9be45e768dc2bb088cf9fae71eaf11a7349

benstadin commented 5 years ago

I'm interested in an example to gracefully shutdown a Beast based server. Background (long story short) for my case is that I use catch2 in a cross platform project, on OS X with XCode this requires some workarounds and force me to start / stop the server for test cases.

vinniefalco commented 5 years ago

Here's one example https://github.com/vinniefalco/BeastLounge/blob/develop/server/server.cpp#L365

benstadin commented 5 years ago

Thanks for the quick reply! Starting to check port this. Though it would be nice if that behavior was part of the default implementation.

benstadin commented 5 years ago

It works for me now, but I intentionally did not implement the server_impl class for maintenance reasons. My current implementation is based on the http async server example. I added a close method to the listener like below to shutdown the io context and the acceptor:

    void close() {
        if (!_stop) {
            _stop = true;
            ioc_.stop();
            acceptor_.close();
        }
    }

The server is handling the shutdown similar to your example, but calls to close the listener like this:

// Block the main thread until stop() is called
    {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_.wait(lock, [] {
            return stop_.load();
        });
    }

    listenerInstance->close();

    // services must be kept alive until after
    // all executor threads are joined.

    // If we get here, then the server has
    // stopped, so join the threads before
    // destroying them.

    for(auto& t : v)
        t.join();

The stop() method looks same as in your example, just without the signal handling.

Is this ok to do it this way?

vinniefalco commented 5 years ago

Is this ok to do it this way?

I/O objects are not thread safe. You have to post to the strand or executor if you are calling acceptor_.close(), and acceptor_ is being accessed from another thread.

However, since you are calling ioc_.stop();, you don't need to close acceptor_ at all, just let it get destroyed normally.