Graceful handling of SIGTERM (Ctrl-c) and shutdown a Threadpool

Viewed 64

Currently, I am at the last chapter of the rust book, implementing the graceful shutdown of the HTTP server.

Now I want to extend the logic a bit and trigger the graceful shutdown after pressing Ctrl-c. Therefore I use the ctrlc crate.

But I cannot make it work due to various borrowing checker errors:

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    let pool = ThreadPool::new(4);

    ctrlc::set_handler(|| {
        // dropping will trigger ThreadPool::drop and gracefully shutdown the running workers
        drop(pool); // compile error, variable is moved here
    })
    .unwrap();

    for stream in listener.incoming() {
        let stream = stream.unwrap();

        pool.execute(|| {
            handle_connection(stream);
        });
    }
}

I tried multiple approaches with Arc<> and additional mpsc channels, but without success.

What is the best practice here in order to make it work?

1 Answers

I ended up with:

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    let pool = ThreadPool::new(4);

    let (tx, rx) = channel();

    ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel."))
        .expect("Error setting Ctrl-C handler");

    for stream in listener.incoming() {
        let stream = stream.unwrap();

        pool.execute(|| {
            handle_connection(stream);
        });

        match rx.try_recv() {
            Ok(_) => break,
            Err(_) => continue,
        }
    }
}

It has a flaw though. Namely after pressing Ctrl-c the shutdown process is not triggered immediately, but only after receiving another request. Then the loop will break and the ThreadPool goes out of scope and gets dropped which triggers the graceful shutdown logic.

The solution is adequate for my learning purposes. In a production environment, one would rely on the graceful shutdown from a web-framework like actix

Related