Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shared mutable state in Hyper

I'm trying to create a counter in a Hyper web server that counts the number of requests it has received. I'm using a Arc<Mutex<u64>> to hold onto count. However, I haven't been able to figure out the right combination of move and .clone() to satisfy the types of the closures. Here's some code that compiles, but resets the counter on each request:

extern crate hyper;

use hyper::rt::Future;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use std::sync::{Arc, Mutex};

fn main() {
    let addr = "0.0.0.0:3000".parse().unwrap();
    // FIXME want to create the counter here, not below
    let server = Server::bind(&addr)
        .serve(|| {
            service_fn_ok(|_req| {
                let counter = Arc::new(Mutex::new(0));
                use_counter(counter)
            })
        })
        .map_err(|e| eprintln!("Error: {}", e));
    hyper::rt::run(server)
}

fn use_counter(counter: Arc<Mutex<u64>>) -> Response<Body> {
    let mut data = counter.lock().unwrap();
    *data += 1;
    Response::new(Body::from(format!("Counter: {}\n", data)))
}
like image 799
Michael Snoyman Avatar asked Oct 24 '18 16:10

Michael Snoyman


1 Answers

It turns out I was pretty close, and looking at a few other examples helped me realize the problem. Since there are two layers of closures at play here, I need to move the counter into the outer closure, clone it, and then move that clone into the inner closure and clone there again. To wit:

extern crate hyper; // 0.12.10

use hyper::rt::Future;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use std::sync::{Arc, Mutex};

fn main() {
    let addr = "0.0.0.0:3000".parse().unwrap();
    let counter = Arc::new(Mutex::new(0));
    let server = Server::bind(&addr)
        .serve(move || {
            let counter = counter.clone();
            service_fn_ok(move |_req| use_counter(counter.clone()))
        })
        .map_err(|e| eprintln!("Error: {}", e));
    hyper::rt::run(server)
}

fn use_counter(counter: Arc<Mutex<u64>>) -> Response<Body> {
    let mut data = counter.lock().unwrap();
    *data += 1;
    Response::new(Body::from(format!("Counter: {}\n", data)))
}

Update February 2020 Here's a version using hyper 0.13:

use hyper::{Body, Response, Server, Request};
use std::sync::{Arc, Mutex};
use hyper::service::{make_service_fn, service_fn};
use std::convert::Infallible;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "0.0.0.0:3000".parse()?;
    let counter = Arc::new(Mutex::new(0));

    let make_service = make_service_fn(move |_conn| {
        let counter = counter.clone();
        async move {
            Ok::<_, Infallible>(service_fn(move |_req: Request<Body>| {
                let counter = counter.clone();
                async move {
                    Ok::<_, Infallible>(use_counter(counter))
                }
            }))
        }
    });

    Server::bind(&addr).serve(make_service).await?;
    Ok(())
}

fn use_counter(counter: Arc<Mutex<u64>>) -> Response<Body> {
    let mut data = counter.lock().unwrap();
    *data += 1;
    Response::new(Body::from(format!("Counter: {}\n", data)))
}
like image 161
Michael Snoyman Avatar answered Nov 09 '22 06:11

Michael Snoyman