From 1069dfa177c766ae34b60c6861abe6cc460a69ee Mon Sep 17 00:00:00 2001 From: Mark Lodato Date: Mon, 5 Apr 2021 19:41:26 -0400 Subject: [PATCH] Clarify use of Arc in shared state (#221) --- content/docs/extractors.md | 10 ++++++-- examples/request-handlers/src/handlers_arc.rs | 24 +++++++++++++++---- examples/request-handlers/src/main.rs | 2 +- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/content/docs/extractors.md b/content/docs/extractors.md index 52fd021..d5cdf08 100644 --- a/content/docs/extractors.md +++ b/content/docs/extractors.md @@ -99,11 +99,15 @@ Here is an example of a handler that stores the number of processed requests: {{< include-example example="request-handlers" file="main.rs" section="data" >}} -Although this handler will work, `self.0` will be different depending on the number of threads and -number of requests processed per thread. A proper implementation would use `web::Data` and `AtomicUsize`. +Although this handler will work, `data.count` will only count the number of requests +handled *by each thread*. To count the number of total requests across all threads, +one should use `Arc` and [atomics][atomics]. {{< include-example example="request-handlers" file="handlers_arc.rs" section="arc" >}} +> **Note**, if you want the *entire* state to be shared across all threads, use +> `web::Data` and `app_data` as described in [Shared Mutable State][shared_mutable_state]. + > Be careful with synchronization primitives like `Mutex` or `RwLock`. The `actix-web` framework > handles requests asynchronously. By blocking thread execution, all concurrent > request handling processes would block. If you need to share or update some state @@ -119,3 +123,5 @@ number of requests processed per thread. A proper implementation would use `web: [bytesexample]: https://docs.rs/actix-web/3/actix_web/trait.FromRequest.html#example-4 [payloadexample]: https://docs.rs/actix-web/3/actix_web/web/struct.Payload.html [actix]: https://actix.github.io/actix/actix/ +[atomics]: https://doc.rust-lang.org/std/sync/atomic/ +[shared_mutable_state]: ../application#shared-mutable-state diff --git a/examples/request-handlers/src/handlers_arc.rs b/examples/request-handlers/src/handlers_arc.rs index f7dbde8..47abff4 100644 --- a/examples/request-handlers/src/handlers_arc.rs +++ b/examples/request-handlers/src/handlers_arc.rs @@ -1,29 +1,43 @@ // use actix_web::{get, web, App, HttpServer, Responder}; +use std::cell::Cell; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; #[derive(Clone)] struct AppState { - count: Arc, + local_count: Cell, + global_count: Arc, } #[get("/")] async fn show_count(data: web::Data) -> impl Responder { - format!("count: {}", data.count.load(Ordering::Relaxed)) + format!( + "global_count: {}\nlocal_count: {}", + data.global_count.load(Ordering::Relaxed), + data.local_count.get() + ) } #[get("/add")] async fn add_one(data: web::Data) -> impl Responder { - data.count.fetch_add(1, Ordering::Relaxed); + data.global_count.fetch_add(1, Ordering::Relaxed); - format!("count: {}", data.count.load(Ordering::Relaxed)) + let local_count = data.local_count.get(); + data.local_count.set(local_count + 1); + + format!( + "global_count: {}\nlocal_count: {}", + data.global_count.load(Ordering::Relaxed), + data.local_count.get() + ) } #[actix_web::main] async fn main() -> std::io::Result<()> { let data = AppState { - count: Arc::new(AtomicUsize::new(0)), + local_count: Cell::new(0), + global_count: Arc::new(AtomicUsize::new(0)), }; HttpServer::new(move || { diff --git a/examples/request-handlers/src/main.rs b/examples/request-handlers/src/main.rs index d287c92..34f72b6 100644 --- a/examples/request-handlers/src/main.rs +++ b/examples/request-handlers/src/main.rs @@ -5,7 +5,7 @@ use std::cell::Cell; #[derive(Clone)] struct AppState { - count: Cell, + count: Cell, } async fn show_count(data: web::Data) -> impl Responder {