6

I'm trying to implement shared state in the Actix-Web framework with Arc and Mutex. The following code compiles, but when I run it, the counter sometimes goes all the way back to 0. How do I prevent that from happening?

use actix_web::{web, App, HttpServer};
use std::sync::{Arc, Mutex};

// This struct represents state
struct AppState {
    app_name: String,
    counter: Arc<Mutex<i64>>,
}

fn index(data: web::Data<AppState>) -> String {
    let mut counter = data.counter.lock().unwrap();
    *counter += 1;
    format!("{}", counter)
}

pub fn main() {
    HttpServer::new(|| {
        App::new()
            .hostname("hello world")
            .register_data(web::Data::new(AppState {
                app_name: String::from("Actix-web"),
                counter: Arc::new(Mutex::new(0)),
            }))
            .route("/", web::get().to(index))
    })
    .bind("127.0.0.1:8088")
    .unwrap()
    .run()
    .unwrap();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
HelloWorld
  • 973
  • 2
  • 9
  • 23
  • See also [Does actix_web's App::register_data create a single instance or one instance per thread?](https://stackoverflow.com/q/59255498/155423); [Actix-Web reports “App data is not configured” when processing a file upload](https://stackoverflow.com/q/56117273/155423) – Shepmaster Dec 11 '19 at 02:26

1 Answers1

8

HttpServer::new takes a closure which is called for each thread that runs the server. This means that multiple instances of AppState is created, one for each thread. Depending on which thread answers the HTTP request, you will get different instances of data and therefor different counter values.

To prevent this from happening, create web::Data<AppState> outside the closure and use a cloned reference inside the HttpServer::new closure.

arve0
  • 3,424
  • 26
  • 33