Don't put the complete site info into a mutex, use atomic ints where possible

This commit is contained in:
p11 2025-04-07 01:19:14 +02:00
parent 999290b1df
commit 58d02c86e2

View File

@ -5,7 +5,10 @@ use std::{
io::{BufReader, Error, ErrorKind, Result, prelude::*}, io::{BufReader, Error, ErrorKind, Result, prelude::*},
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, Mutex}, sync::{
Arc, Mutex,
atomic::{AtomicUsize, Ordering},
},
}; };
use data_stream::{ use data_stream::{
@ -79,18 +82,21 @@ struct Comment {
text: Box<str>, text: Box<str>,
} }
#[derive(Default, Clone)] #[derive(Default)]
struct SiteInfo { struct SiteInfo {
comments: Vec<Comment>, comments: Mutex<Vec<Comment>>,
visits: usize, visits: AtomicUsize,
up: usize, up: AtomicUsize,
down: usize, down: AtomicUsize,
} }
impl<S: SizeSettings> ToStream<S> for SiteInfo { impl<S: SizeSettings> ToStream<S> for SiteInfo {
fn to_stream<W: Write>(&self, stream: &mut W) -> Result<()> { fn to_stream<W: Write>(&self, stream: &mut W) -> Result<()> {
S::size_to_stream(self.comments.len(), stream)?; let Ok(comments) = self.comments.lock() else {
for Comment { name, text } in &self.comments { 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(); let name_bytes = name.as_bytes();
S::size_to_stream(name_bytes.len(), stream)?; S::size_to_stream(name_bytes.len(), stream)?;
stream.write_all(name_bytes)?; stream.write_all(name_bytes)?;
@ -100,9 +106,9 @@ impl<S: SizeSettings> ToStream<S> for SiteInfo {
stream.write_all(text_bytes)?; stream.write_all(text_bytes)?;
} }
S::size_to_stream(self.visits, stream)?; S::size_to_stream(self.visits.load(Ordering::Acquire), stream)?;
S::size_to_stream(self.up, stream)?; S::size_to_stream(self.up.load(Ordering::Acquire), stream)?;
S::size_to_stream(self.down, stream)?; S::size_to_stream(self.down.load(Ordering::Acquire), stream)?;
Ok(()) Ok(())
} }
@ -111,7 +117,8 @@ impl<S: SizeSettings> ToStream<S> for SiteInfo {
impl<S: SizeSettings> FromStream<S> for SiteInfo { impl<S: SizeSettings> FromStream<S> for SiteInfo {
fn from_stream<R: Read>(stream: &mut R) -> Result<Self> { fn from_stream<R: Read>(stream: &mut R) -> Result<Self> {
let size = S::size_from_stream(stream)?; let size = S::size_from_stream(stream)?;
let comments = (0..size) let comments = Mutex::new(
(0..size)
.map(|_| { .map(|_| {
let name_bytes = <Vec<_> as FromStream<S>>::from_stream(stream)?; let name_bytes = <Vec<_> as FromStream<S>>::from_stream(stream)?;
let name = std::str::from_utf8(&name_bytes) let name = std::str::from_utf8(&name_bytes)
@ -125,11 +132,12 @@ impl<S: SizeSettings> FromStream<S> for SiteInfo {
Ok(Comment { name, text }) Ok(Comment { name, text })
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?,
);
let visits = S::size_from_stream(stream)?; let visits = S::size_from_stream(stream)?.into();
let up = S::size_from_stream(stream)?; let up = S::size_from_stream(stream)?.into();
let down = S::size_from_stream(stream)?; let down = S::size_from_stream(stream)?.into();
Ok(Self { Ok(Self {
comments, comments,
@ -142,7 +150,7 @@ impl<S: SizeSettings> FromStream<S> for SiteInfo {
#[derive(Default)] #[derive(Default)]
struct Context { struct Context {
infos: HashMap<Box<str>, Arc<Mutex<SiteInfo>>>, infos: HashMap<Box<str>, Arc<SiteInfo>>,
} }
fn handle_connection( fn handle_connection(
@ -345,13 +353,13 @@ fn handle_connection(
match context.infos.entry(relative_path.clone().into_boxed_str()) { match context.infos.entry(relative_path.clone().into_boxed_str()) {
Occupied(o) => o.get().clone(), Occupied(o) => o.get().clone(),
Vacant(v) => v Vacant(v) => v
.insert(Arc::new(Mutex::new( .insert(Arc::new(
File::open(&data_path) File::open(&data_path)
.map(|mut file| { .map(|mut file| {
from_stream::<PortableSettings, _, _>(&mut file).unwrap_or_default() from_stream::<PortableSettings, _, _>(&mut file).unwrap_or_default()
}) })
.unwrap_or_default(), .unwrap_or_default(),
))) ))
.clone(), .clone(),
} }
} else { } else {
@ -408,7 +416,7 @@ struct DocumentPaths<'a> {
} }
fn handle_relative_connection( fn handle_relative_connection(
info: Arc<Mutex<SiteInfo>>, info: Arc<SiteInfo>,
mut stream: TcpStream, mut stream: TcpStream,
body: &str, body: &str,
relative_path: &str, relative_path: &str,
@ -464,26 +472,30 @@ fn handle_relative_connection(
} }
} }
let info = if let Ok(mut info) = info.lock() { let comments = {
if let (Some(name), Some(text)) = (name, text) { let Ok(mut comments) = info.comments.lock() else {
info.comments.push(Comment { name, text });
}
if up {
info.up += 1;
}
if down {
info.down += 1;
}
info.visits += 1;
info.clone()
} else {
return; return;
}; };
if let (Some(name), Some(text)) = (name, text) {
comments.push(Comment { name, text });
}
comments.clone()
};
let up = if up {
info.up.fetch_add(1, Ordering::Relaxed)
} else {
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 let Ok(mut file) = File::create(file_paths.data) {
if to_stream::<PortableSettings, _, _>(&info, &mut file).is_err() { if to_stream::<PortableSettings, _, _>(&*info, &mut file).is_err() {
eprintln!("Error saving data!"); eprintln!("Error saving data!");
eprintln!(); eprintln!();
} }
@ -507,11 +519,7 @@ fn handle_relative_connection(
let _ = writeln!(stream, "<a href=\"/{parent_path}\">&lt;&lt; Back</a>"); let _ = writeln!(stream, "<a href=\"/{parent_path}\">&lt;&lt; Back</a>");
}; };
let _ = writeln!( let _ = writeln!(stream, "<p>👁️{visits} 💖️{up} 💔️{down}</p>");
stream,
"<p>👁️{} 💖️{} 💔️{}</p>",
info.visits, info.up, info.down
);
section(&mut stream); section(&mut stream);
@ -688,7 +696,7 @@ fn handle_relative_connection(
}; };
let _ = stream.write_all(html.into_string().as_bytes()); 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! { let html = html! {
fieldset { fieldset {
legend { (name) } legend { (name) }