Created
May 1, 2022 18:16
-
-
Save l0s/9ea897bb9a8b89ab999fa0c75a8d2989 to your computer and use it in GitHub Desktop.
Simple pluggable request handler mechanism
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use rayon::{ThreadPool, ThreadPoolBuilder}; | |
| #[derive(Copy, Clone)] | |
| pub struct Request { | |
| request_id: u8, | |
| } | |
| pub struct Response { | |
| response_id: u8, | |
| } | |
| pub struct Failure; | |
| pub trait RequestHandler: Sync + Send { | |
| fn handle_request(&self, request: &Request) -> Result<Response, Failure>; | |
| } | |
| impl<F> RequestHandler for F | |
| where F: Fn(&Request) -> Result<Response, Failure> + Sync + Send { | |
| fn handle_request(&self, request: &Request) -> Result<Response, Failure> { | |
| self(request) | |
| } | |
| } | |
| struct Server { | |
| handler: Box<dyn RequestHandler>, | |
| pool: ThreadPool, | |
| } | |
| impl Server { | |
| fn with_handler(handler: Box<dyn RequestHandler>) -> Server { | |
| let builder = ThreadPoolBuilder::new(); | |
| let pool = builder.build().unwrap(); | |
| Server { | |
| handler, | |
| pool, | |
| } | |
| } | |
| } | |
| impl Server { | |
| pub fn run(&self) { | |
| self.pool.scope(|scope| { | |
| for request_id in 0..8u8 { // simulate reading infinite network events | |
| let request = Request { request_id }; | |
| scope.spawn(move |_| { // pass each event to a worker | |
| if let Ok(response) = self.handler.handle_request(&request) { | |
| eprintln!("response_id: {}", response.response_id); | |
| } | |
| }); | |
| } | |
| }); | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use std::sync::atomic::{AtomicU8, Ordering}; | |
| use crate::handler::{Failure, Request, RequestHandler, Response, Server}; | |
| #[test] | |
| fn stateless_closure() { | |
| let handler = |request: &Request| -> Result<Response, Failure> { | |
| Ok(Response { | |
| response_id: request.request_id, | |
| }) | |
| }; | |
| let handler = Box::new(handler); | |
| let server = Server::with_handler(handler); | |
| server.run(); | |
| } | |
| // #[test] | |
| // fn stateful_closure() { | |
| // let counter = AtomicU8::new(0); | |
| // let response_handler = |_request: &Request| -> Result<Response, Failure> { // `counter` captured here | |
| // Ok(Response { | |
| // response_id: counter.fetch_add(1, Ordering::Relaxed), // error[E0597]: `counter` does not live long enough | |
| // }) | |
| // }; | |
| // let handler = Box::new(response_handler); | |
| // let server = Server::with_handler(handler); // cast requires that `counter` is borrowed for `'static` | |
| // | |
| // server.run(); | |
| // } // `counter` dropped here while still borrowed | |
| #[test] | |
| fn stateless_trait() { | |
| struct Handler; | |
| impl RequestHandler for Handler { | |
| fn handle_request(&self, request: &Request) -> Result<Response, Failure> { | |
| Ok(Response { response_id: request.request_id }) | |
| } | |
| } | |
| let handler = Box::new(Handler {}); | |
| let server = Server::with_handler(handler); | |
| server.run(); | |
| } | |
| #[test] | |
| fn stateful_trait() { | |
| #[derive(Default)] | |
| struct Handler { | |
| counter: AtomicU8, | |
| } | |
| impl RequestHandler for Handler { | |
| fn handle_request(&self, _request: &Request) -> Result<Response, Failure> { | |
| Ok(Response { response_id: self.counter.fetch_add(1, Ordering::Relaxed) }) | |
| } | |
| } | |
| let handler = Box::new(Handler::default()); | |
| let server = Server::with_handler(handler); | |
| server.run(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment