I try to implement a multi-thread web-server. Considering the following code:
pub trait Handler: Sync {
fn handle_request(&self, request: &Request) -> Response;
fn handle_bad_request(&self, e: &ParseError) -> Response {
error!("Failed to parse request: {}", e);
Response::new(StatusCode::BadRequest, None)
}
}
pub struct Server {
addr: IpAddr,
port: u16,
}
impl Server {
pub fn new(addr: IpAddr, port: u16) -> Self {
Self { addr, port }
}
pub fn run(self, handler: impl Handler) {
let socket_addr = SocketAddr::new(self.addr, self.port);
info!("Listening on {}", socket_addr);
let listener = TcpListener::bind(socket_addr).unwrap();
let mut handles = Vec::new();
for _n in 0..16 {
if let Ok(thread_listener) = listener.try_clone() {
handles.push(thread::spawn(|| {
loop {
match listener.accept() {
Ok((stream, remote_addr)) => Self::process_request(&handler, stream, remote_addr),
Err(e) => error!("Failed to establish the connection: {}", e),
}
}
}));
}
}
for handle in handles.drain(..) {
drop(handle.join());
}
}
fn process_request(handler: &impl Handler, mut stream: TcpStream, remote_addr: SocketAddr) {
/* ... */
}
}
...how can I get the compiler to understand that the Handler
does live long enough, as we join()
all threads before the function returns? Currently I get this error:
error[E0373]: closure may outlive the current function, but it borrows `handler`, which is owned by the current function
I also tried to give each thread a separate clone of the handler - which I think should not be necessary, as Handler
is totally "stateless". Anyways, the clone approach also doesn't work for me:
let mut handles = Vec::new();
for _n in 0..16 {
if let Ok(thread_listener) = listener.try_clone() {
handles.push(thread::spawn(move || {
let thread_handler = handler.clone();
loop {
match listener.accept() {
Ok((stream, remote_addr)) => Self::process_request(&thread_handler, stream, remote_addr),
Err(e) => error!("Failed to establish the connection: {}", e),
}
}
}));
}
}
Error is:
error[E0277]: `impl Handler` cannot be shared between threads safely
But it's not "shared", really. Each thread has it's own clone that is moved into the thread/closure
Thanks for any suggestions!