perf: use async locks

This commit is contained in:
2026-05-30 20:10:11 +02:00
parent fed9d4d8e9
commit 8bee404f80
+34 -29
View File
@@ -2,8 +2,8 @@ use std::{
collections::HashMap, collections::HashMap,
fs, fs,
io::Write, io::Write,
sync::{Arc, Mutex, mpsc},
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::Arc,
}; };
use axum::{ use axum::{
@@ -19,7 +19,10 @@ use futures::StreamExt as _;
use rand::random_bool; use rand::random_bool;
use serde::{Deserialize, Serialize, de}; use serde::{Deserialize, Serialize, de};
use serde_json::json; use serde_json::json;
use tokio::time::{Duration, sleep}; use tokio::{
sync::{Mutex, mpsc},
time::{Duration, sleep},
};
#[derive(Deserialize, Serialize, Debug, Ord, Eq, PartialEq, PartialOrd, Clone)] #[derive(Deserialize, Serialize, Debug, Ord, Eq, PartialEq, PartialOrd, Clone)]
struct Entry { struct Entry {
@@ -62,24 +65,23 @@ async fn main() {
let loscores: Arc<Mutex<Vec<Entry>>> = read_file(PATH_LOSCORES); let loscores: Arc<Mutex<Vec<Entry>>> = read_file(PATH_LOSCORES);
let pingscores: Arc<Mutex<HashMap<String, (u64, u32)>>> = read_file(PATH_PINGSCORES); let pingscores: Arc<Mutex<HashMap<String, (u64, u32)>>> = read_file(PATH_PINGSCORES);
let (tx, rx) = mpsc::channel::<LeaderboardUpdate>(); let (tx, rx) = mpsc::channel::<LeaderboardUpdate>(1024);
{ {
let (hiscores, loscores, pingscores) = let (hiscores, loscores, pingscores) =
(hiscores.clone(), loscores.clone(), pingscores.clone()); (hiscores.clone(), loscores.clone(), pingscores.clone());
tokio::spawn(async move { tokio::spawn(async move {
handle_hiscores(rx, &hiscores, &loscores, &pingscores); handle_hiscores(rx, &hiscores, &loscores, &pingscores).await;
}); });
} }
{ {
let pingscores = pingscores.clone(); let pingscores = pingscores.clone();
tokio::spawn(async move { tokio::spawn(async move {
loop
// write pingscores every 30s // write pingscores every 30s
{ loop {
sleep(Duration::from_millis(30000)).await; sleep(Duration::from_millis(30000)).await;
let pingscores = pingscores.lock().unwrap(); let pingscores = pingscores.lock().await;
let file_contents: String = serde_json::to_string(&pingscores.clone()).unwrap(); let file_contents: String = serde_json::to_string(&pingscores.clone()).unwrap();
drop(pingscores); drop(pingscores);
let mut file = fs::OpenOptions::new() let mut file = fs::OpenOptions::new()
@@ -119,8 +121,8 @@ async fn leaderboard() -> Html<&'static str> {
Html(include_str!("../leaderboard.html")) Html(include_str!("../leaderboard.html"))
} }
// receiver: 0 for hiscore, 1 for loscore, 2 for pingscore // receiver: 0 for hiscore, 1 for loscore, 2 for pingscore
fn handle_hiscores( async fn handle_hiscores(
rx: mpsc::Receiver<LeaderboardUpdate>, mut rx: mpsc::Receiver<LeaderboardUpdate>,
hiscores: &Mutex<Vec<Entry>>, hiscores: &Mutex<Vec<Entry>>,
loscores: &Mutex<Vec<Entry>>, loscores: &Mutex<Vec<Entry>>,
pingscores: &Mutex<HashMap<String, (u64, u32)>>, pingscores: &Mutex<HashMap<String, (u64, u32)>>,
@@ -154,7 +156,7 @@ fn handle_hiscores(
} }
// Panic galore // Panic galore
let mut hiscores_lock = hiscores.lock().unwrap(); let mut hiscores_lock = hiscores.lock().await;
hiscores_lock.sort(); hiscores_lock.sort();
hiscores_lock.reverse(); hiscores_lock.reverse();
let file_contents: String = serde_json::to_string(&hiscores_lock.clone()).unwrap(); let file_contents: String = serde_json::to_string(&hiscores_lock.clone()).unwrap();
@@ -168,24 +170,23 @@ fn handle_hiscores(
drop(file); drop(file);
loop { loop {
let LeaderboardUpdate { name, update } = rx.recv().expect("channel error"); let LeaderboardUpdate { name, update } = rx.recv().await.expect("channel error");
match update { match update {
LeaderboardUpdateType::Reset { hiscore_pingscore } => { LeaderboardUpdateType::Reset { hiscore_pingscore } => {
// Hiscore // Hiscore
update_scoretable( update_scoretable(
"hiscore", "hiscore",
hiscores.lock().unwrap(), hiscores.lock().await,
&name, &name,
hiscore_pingscore, hiscore_pingscore,
PATH_HISCORES, PATH_HISCORES,
); );
// Pingscore // Pingscore
let mut pingscores = pingscores.lock().unwrap(); let mut pingscores = pingscores.lock().await;
if hiscore_pingscore > pingscores.get(&*name).unwrap_or(&(0, 0)).1
// pb // pb
{ if hiscore_pingscore > pingscores.get(&*name).unwrap_or(&(0, 0)).1 {
pingscores.entry(name.to_string()).or_insert((0, 0)).1 = hiscore_pingscore; pingscores.entry(name.to_string()).or_insert((0, 0)).1 = hiscore_pingscore;
println!("{name} new PB: {hiscore_pingscore}"); println!("{name} new PB: {hiscore_pingscore}");
}; };
@@ -196,7 +197,7 @@ fn handle_hiscores(
LeaderboardUpdateType::Increment { loscore } => { LeaderboardUpdateType::Increment { loscore } => {
update_scoretable( update_scoretable(
"loscore", "loscore",
loscores.lock().unwrap(), loscores.lock().await,
&name, &name,
loscore, loscore,
PATH_LOSCORES, PATH_LOSCORES,
@@ -229,9 +230,9 @@ async fn handle_socket(
let mut value: u32 = 0; let mut value: u32 = 0;
let msg = { let msg = {
let hiscores = hiscores.lock().unwrap(); let hiscores = hiscores.lock().await;
let loscores = loscores.lock().unwrap(); let loscores = loscores.lock().await;
let pingscores = pingscores.lock().unwrap(); let pingscores = pingscores.lock().await;
json!({ "hiscores": &*hiscores, "loscores": &*loscores, "pingscores": &*pingscores}) json!({ "hiscores": &*hiscores, "loscores": &*loscores, "pingscores": &*pingscores})
.to_string() .to_string()
}; };
@@ -258,12 +259,14 @@ async fn handle_socket(
Ok(Message::Text(_)) => { Ok(Message::Text(_)) => {
if random_bool(CHANCE) { if random_bool(CHANCE) {
// reset // reset
let _ = tx.send(LeaderboardUpdate { let _ = tx
name: name.clone(), .send(LeaderboardUpdate {
update: LeaderboardUpdateType::Reset { name: name.clone(),
hiscore_pingscore: value, update: LeaderboardUpdateType::Reset {
}, hiscore_pingscore: value,
}); },
})
.await;
resets += 1; resets += 1;
value = 0 value = 0
} }
@@ -271,10 +274,12 @@ async fn handle_socket(
else { else {
value += 1; value += 1;
if prev == 0 { if prev == 0 {
let _ = tx.send(LeaderboardUpdate { let _ = tx
name: name.clone(), .send(LeaderboardUpdate {
update: LeaderboardUpdateType::Increment { loscore: resets }, name: name.clone(),
}); update: LeaderboardUpdateType::Increment { loscore: resets },
})
.await;
resets = 0; resets = 0;
} }
} }