From 58d02c86e21cc72936e86d9c30982d8e9a2a0247 Mon Sep 17 00:00:00 2001 From: p11 Date: Mon, 7 Apr 2025 01:19:14 +0200 Subject: [PATCH] Don't put the complete site info into a mutex, use atomic ints where possible --- src/main.rs | 110 ++++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/src/main.rs b/src/main.rs index bf325d7..7c32128 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,10 @@ use std::{ io::{BufReader, Error, ErrorKind, Result, prelude::*}, net::{TcpListener, TcpStream}, path::{Path, PathBuf}, - sync::{Arc, Mutex}, + sync::{ + Arc, Mutex, + atomic::{AtomicUsize, Ordering}, + }, }; use data_stream::{ @@ -79,18 +82,21 @@ struct Comment { text: Box, } -#[derive(Default, Clone)] +#[derive(Default)] struct SiteInfo { - comments: Vec, - visits: usize, - up: usize, - down: usize, + comments: Mutex>, + visits: AtomicUsize, + up: AtomicUsize, + down: AtomicUsize, } impl ToStream for SiteInfo { fn to_stream(&self, stream: &mut W) -> Result<()> { - S::size_to_stream(self.comments.len(), stream)?; - for Comment { name, text } in &self.comments { + let Ok(comments) = self.comments.lock() else { + return Err(std::io::ErrorKind::ResourceBusy.into()); + }; + S::size_to_stream(comments.len(), stream)?; + for Comment { name, text } in comments.iter() { let name_bytes = name.as_bytes(); S::size_to_stream(name_bytes.len(), stream)?; stream.write_all(name_bytes)?; @@ -100,9 +106,9 @@ impl ToStream for SiteInfo { stream.write_all(text_bytes)?; } - S::size_to_stream(self.visits, stream)?; - S::size_to_stream(self.up, stream)?; - S::size_to_stream(self.down, stream)?; + S::size_to_stream(self.visits.load(Ordering::Acquire), stream)?; + S::size_to_stream(self.up.load(Ordering::Acquire), stream)?; + S::size_to_stream(self.down.load(Ordering::Acquire), stream)?; Ok(()) } @@ -111,25 +117,27 @@ impl ToStream for SiteInfo { impl FromStream for SiteInfo { fn from_stream(stream: &mut R) -> Result { let size = S::size_from_stream(stream)?; - let comments = (0..size) - .map(|_| { - let name_bytes = as FromStream>::from_stream(stream)?; - let name = std::str::from_utf8(&name_bytes) - .map_err(|e| Error::new(ErrorKind::InvalidData, e))? - .into(); + let comments = Mutex::new( + (0..size) + .map(|_| { + let name_bytes = as FromStream>::from_stream(stream)?; + let name = std::str::from_utf8(&name_bytes) + .map_err(|e| Error::new(ErrorKind::InvalidData, e))? + .into(); - let text_bytes = as FromStream>::from_stream(stream)?; - let text = std::str::from_utf8(&text_bytes) - .map_err(|e| Error::new(ErrorKind::InvalidData, e))? - .into(); + let text_bytes = as FromStream>::from_stream(stream)?; + let text = std::str::from_utf8(&text_bytes) + .map_err(|e| Error::new(ErrorKind::InvalidData, e))? + .into(); - Ok(Comment { name, text }) - }) - .collect::>>()?; + Ok(Comment { name, text }) + }) + .collect::>>()?, + ); - let visits = S::size_from_stream(stream)?; - let up = S::size_from_stream(stream)?; - let down = S::size_from_stream(stream)?; + let visits = S::size_from_stream(stream)?.into(); + let up = S::size_from_stream(stream)?.into(); + let down = S::size_from_stream(stream)?.into(); Ok(Self { comments, @@ -142,7 +150,7 @@ impl FromStream for SiteInfo { #[derive(Default)] struct Context { - infos: HashMap, Arc>>, + infos: HashMap, Arc>, } fn handle_connection( @@ -345,13 +353,13 @@ fn handle_connection( match context.infos.entry(relative_path.clone().into_boxed_str()) { Occupied(o) => o.get().clone(), Vacant(v) => v - .insert(Arc::new(Mutex::new( + .insert(Arc::new( File::open(&data_path) .map(|mut file| { from_stream::(&mut file).unwrap_or_default() }) .unwrap_or_default(), - ))) + )) .clone(), } } else { @@ -408,7 +416,7 @@ struct DocumentPaths<'a> { } fn handle_relative_connection( - info: Arc>, + info: Arc, mut stream: TcpStream, body: &str, relative_path: &str, @@ -464,26 +472,30 @@ fn handle_relative_connection( } } - let info = if let Ok(mut info) = info.lock() { + let comments = { + let Ok(mut comments) = info.comments.lock() else { + return; + }; if let (Some(name), Some(text)) = (name, text) { - info.comments.push(Comment { name, text }); + comments.push(Comment { name, text }); } - if up { - info.up += 1; - } - if down { - info.down += 1; - } - - info.visits += 1; - - info.clone() + comments.clone() + }; + let up = if up { + info.up.fetch_add(1, Ordering::Relaxed) } else { - return; + info.up.load(Ordering::Relaxed) + }; + let down = if down { + info.down.fetch_add(1, Ordering::Relaxed) + } else { + info.down.load(Ordering::Relaxed) }; + let visits = info.visits.fetch_add(1, Ordering::Relaxed); + if let Ok(mut file) = File::create(file_paths.data) { - if to_stream::(&info, &mut file).is_err() { + if to_stream::(&*info, &mut file).is_err() { eprintln!("Error saving data!"); eprintln!(); } @@ -507,11 +519,7 @@ fn handle_relative_connection( let _ = writeln!(stream, "<< Back"); }; - let _ = writeln!( - stream, - "

👁️{} 💖️{} 💔️{}

", - info.visits, info.up, info.down - ); + let _ = writeln!(stream, "

👁️{visits} 💖️{up} 💔️{down}

"); section(&mut stream); @@ -688,7 +696,7 @@ fn handle_relative_connection( }; let _ = stream.write_all(html.into_string().as_bytes()); - for Comment { name, text } in &info.comments { + for Comment { name, text } in comments { let html = html! { fieldset { legend { (name) }