chore: refactor to use RwLock

This commit is contained in:
michel
2025-11-01 14:57:27 +01:00
committed by Michel de Graaf
parent 4932386056
commit ff30db3e29
5 changed files with 47 additions and 49 deletions
+22 -22
View File
@@ -14,7 +14,7 @@ use std::{
net::TcpStream, net::TcpStream,
sync::{ sync::{
mpsc::{Receiver, Sender}, mpsc::{Receiver, Sender},
Mutex, RwLock,
}, },
thread::{self, sleep}, thread::{self, sleep},
}; };
@@ -73,7 +73,7 @@ pub struct Client {
username: String, username: String,
password: String, password: String,
server: Option<Server>, server: Option<Server>,
context: Arc<Mutex<ClientContext>>, context: Arc<RwLock<ClientContext>>,
} }
impl Client { impl Client {
@@ -83,10 +83,10 @@ impl Client {
password: String, password: String,
) -> Self { ) -> Self {
crate::utils::logger::init(); crate::utils::logger::init();
let context = Arc::new(Mutex::new(ClientContext::new())); let context = Arc::new(RwLock::new(ClientContext::new()));
debug!( debug!(
"ThreadPool initialized with {} threads", "ThreadPool initialized with {} threads",
context.lock().unwrap().thread_pool.thread_count() context.read().unwrap().thread_pool.thread_count()
); );
Self { Self {
address, address,
@@ -103,7 +103,7 @@ impl Client {
Receiver<ClientOperation>, Receiver<ClientOperation>,
) = mpsc::channel(); ) = mpsc::channel();
self.context.lock().unwrap().sender = Some(sender.clone()); self.context.write().unwrap().sender = Some(sender.clone());
let client_sender = sender.clone(); let client_sender = sender.clone();
@@ -118,7 +118,7 @@ impl Client {
thread::spawn(move || { thread::spawn(move || {
Listen::start(2234, client_sender.clone()); Listen::start(2234, client_sender.clone());
}); });
let mut unlocked_context = self.context.lock().unwrap(); let mut unlocked_context = self.context.write().unwrap();
unlocked_context.server_sender = unlocked_context.server_sender =
Some(server.get_sender().clone()); Some(server.get_sender().clone());
@@ -153,7 +153,7 @@ impl Client {
#[allow(dead_code)] #[allow(dead_code)]
pub fn remove_peer(&self, username: &str) { pub fn remove_peer(&self, username: &str) {
let mut context = self.context.lock().unwrap(); let mut context = self.context.write().unwrap();
if let Some(peer) = context.peers.remove(username) { if let Some(peer) = context.peers.remove(username) {
drop(peer); drop(peer);
} }
@@ -180,7 +180,7 @@ impl Client {
break; break;
} }
} }
Ok(self.context.lock().unwrap().search_results.clone()) Ok(self.context.read().unwrap().search_results.clone())
} }
pub fn download( pub fn download(
@@ -206,7 +206,7 @@ impl Client {
download_directory, download_directory,
}; };
let mut context = self.context.lock().unwrap(); let mut context = self.context.write().unwrap();
context.download_tokens.insert(token, download.clone()); context.download_tokens.insert(token, download.clone());
let download_initiated = context let download_initiated = context
.peers .peers
@@ -242,7 +242,7 @@ impl Client {
fn listen_to_client_operations( fn listen_to_client_operations(
reader: Receiver<ClientOperation>, reader: Receiver<ClientOperation>,
client_context: Arc<Mutex<ClientContext>>, client_context: Arc<RwLock<ClientContext>>,
own_username: String, own_username: String,
) { ) {
thread::spawn(move || loop { thread::spawn(move || loop {
@@ -259,13 +259,13 @@ impl Client {
ClientOperation::SearchResult(file_search) => { ClientOperation::SearchResult(file_search) => {
trace!("[client] SearchResult {:?}", file_search); trace!("[client] SearchResult {:?}", file_search);
client_context client_context
.lock() .write()
.unwrap() .unwrap()
.search_results .search_results
.push(file_search.clone()); .push(file_search.clone());
} }
ClientOperation::PeerDisconnected(username) => { ClientOperation::PeerDisconnected(username) => {
let mut context = client_context.lock().unwrap(); let mut context = client_context.write().unwrap();
if let Some(peer) = context.peers.remove(&username) { if let Some(peer) = context.peers.remove(&username) {
drop(peer); // Explicitly drop to trigger cleanup drop(peer); // Explicitly drop to trigger cleanup
} }
@@ -279,7 +279,7 @@ impl Client {
} }
ClientOperation::DownloadFromPeer(token, peer) => { ClientOperation::DownloadFromPeer(token, peer) => {
let maybe_download = { let maybe_download = {
let client_context = client_context.lock().unwrap(); let client_context = client_context.read().unwrap();
client_context.download_tokens.get(&token).cloned() client_context.download_tokens.get(&token).cloned()
}; };
let own_username = own_username.clone(); let own_username = own_username.clone();
@@ -332,7 +332,7 @@ impl Client {
} }
ClientOperation::NewPeer(new_peer) => { ClientOperation::NewPeer(new_peer) => {
if client_context if client_context
.lock() .read()
.unwrap() .unwrap()
.peers .peers
.contains_key(&new_peer.username) .contains_key(&new_peer.username)
@@ -342,7 +342,7 @@ impl Client {
new_peer.username new_peer.username
); );
} else if let Some(server_sender) = } else if let Some(server_sender) =
&client_context.lock().unwrap().server_sender &client_context.read().unwrap().server_sender
{ {
server_sender server_sender
.send(ServerOperation::GetPeerAddress( .send(ServerOperation::GetPeerAddress(
@@ -386,7 +386,7 @@ impl Client {
); );
if let Some(_peer) = if let Some(_peer) =
client_context.lock().unwrap().peers.get(&username) client_context.read().unwrap().peers.get(&username)
{ {
// don't know if i should update? and or reconnect the peer // don't know if i should update? and or reconnect the peer
// debug!( // debug!(
@@ -426,7 +426,7 @@ impl Client {
transfer, transfer,
username, username,
) => { ) => {
let mut context = client_context.lock().unwrap(); let mut context = client_context.write().unwrap();
let key_to_remove = context let key_to_remove = context
.download_tokens .download_tokens
@@ -468,12 +468,12 @@ impl Client {
fn connect_to_peer( fn connect_to_peer(
peer: Peer, peer: Peer,
client_context: Arc<Mutex<ClientContext>>, client_context: Arc<RwLock<ClientContext>>,
own_username: String, own_username: String,
stream: Option<TcpStream>, stream: Option<TcpStream>,
) { ) {
let client_context2 = client_context.clone(); let client_context2 = client_context.clone();
let unlocked_context = client_context.lock().unwrap(); let unlocked_context = client_context.read().unwrap();
trace!("[client] connect_to_peer: {}", peer.username); trace!("[client] connect_to_peer: {}", peer.username);
if let Some(sender) = unlocked_context.sender.clone() { if let Some(sender) = unlocked_context.sender.clone() {
@@ -494,7 +494,7 @@ impl Client {
match connect_result { match connect_result {
Ok(p) => { Ok(p) => {
trace!("[client] connected to: {}", peer.username); trace!("[client] connected to: {}", peer.username);
client_context2.lock().unwrap().peers.insert(peer.username, p); client_context2.write().unwrap().peers.insert(peer.username, p);
} }
Err(e) => { Err(e) => {
trace!( trace!(
@@ -545,12 +545,12 @@ impl Client {
} }
fn pierce_firewall( fn pierce_firewall(
peer: Peer, peer: Peer,
client_context: Arc<Mutex<ClientContext>>, client_context: Arc<RwLock<ClientContext>>,
own_username: String, own_username: String,
) { ) {
debug!("Piercing firewall for peer: {:?}", peer); debug!("Piercing firewall for peer: {:?}", peer);
let context = client_context.lock().unwrap(); let context = client_context.read().unwrap();
if let Some(server_sender) = &context.server_sender { if let Some(server_sender) = &context.server_sender {
if let Some(token) = peer.token { if let Some(token) = peer.token {
match server_sender.send(ServerOperation::PierceFirewall(token)) match server_sender.send(ServerOperation::PierceFirewall(token))
+1 -1
View File
@@ -8,7 +8,7 @@ pub mod server;
pub use handlers::{Handlers, MessageHandler}; pub use handlers::{Handlers, MessageHandler};
pub use message_reader::MessageReader; pub use message_reader::MessageReader;
use std::{str, usize}; use std::str;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[allow(dead_code)] #[allow(dead_code)]
+19 -20
View File
@@ -6,8 +6,8 @@ use std::fs::{self, File};
use std::io::{self, BufWriter, Read, Write}; use std::io::{self, BufWriter, Read, Write};
use std::net::TcpStream; use std::net::TcpStream;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use std::path::{Path, PathBuf}; use std::path::Path;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, RwLock};
use std::time::Duration; use std::time::Duration;
#[derive(Debug)] #[derive(Debug)]
@@ -22,10 +22,10 @@ struct FileManager;
#[allow(dead_code)] #[allow(dead_code)]
impl FileManager { impl FileManager {
fn expand_path(mut path: &str) -> String { fn expand_path(path: &str) -> String {
if path.starts_with('~') { if let Some(stripped) = path.strip_prefix('~') {
let home = env::var("HOME").expect("HOME not set"); let home = env::var("HOME").expect("HOME not set");
format!("{}{}", home, &path[1..]) format!("{}{}", home, stripped)
} else { } else {
path.to_string() path.to_string()
} }
@@ -244,7 +244,7 @@ impl DownloadPeer {
pub fn download_file( pub fn download_file(
self, self,
client_context: Arc<Mutex<ClientContext>>, client_context: Arc<RwLock<ClientContext>>,
mut expected_size: Option<usize>, mut expected_size: Option<usize>,
mut download_path: Option<String>, mut download_path: Option<String>,
) -> Result<(usize, String), io::Error> { ) -> Result<(usize, String), io::Error> {
@@ -289,7 +289,7 @@ impl DownloadPeer {
); );
download_info = client_context download_info = client_context
.lock() .read()
.unwrap() .unwrap()
.download_tokens .download_tokens
.get(&token_u32) .get(&token_u32)
@@ -392,12 +392,12 @@ mod tests {
use std::fs; use std::fs;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::TcpListener; use std::net::TcpListener;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, RwLock};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
pub fn build_test_server() -> (u16, Arc<Mutex<Vec<Vec<u8>>>>) { pub fn build_test_server() -> (u16, Arc<RwLock<Vec<Vec<u8>>>>) {
let messages = Arc::new(Mutex::new(Vec::<Vec<u8>>::new())); let messages = Arc::new(RwLock::new(Vec::<Vec<u8>>::new()));
let messages_clone = Arc::clone(&messages); let messages_clone = Arc::clone(&messages);
// Find an available port by binding to port 0 // Find an available port by binding to port 0
@@ -412,7 +412,7 @@ mod tests {
match stream.read(&mut buffer) { match stream.read(&mut buffer) {
Ok(bytes_read) if bytes_read > 0 => { Ok(bytes_read) if bytes_read > 0 => {
let data = buffer[..bytes_read].to_vec(); let data = buffer[..bytes_read].to_vec();
messages_clone.lock().unwrap().push(data); messages_clone.write().unwrap().push(data);
} }
_ => {} _ => {}
} }
@@ -440,7 +440,7 @@ mod tests {
false, false,
"own_username".to_string(), "own_username".to_string(),
); );
let dummy_context = Arc::new(Mutex::new(ClientContext::new())); let dummy_context = Arc::new(RwLock::new(ClientContext::new()));
let _ = download_peer let _ = download_peer
.download_file(dummy_context, None, None) .download_file(dummy_context, None, None)
.unwrap(); .unwrap();
@@ -448,7 +448,7 @@ mod tests {
// Give the client time to send messages // Give the client time to send messages
thread::sleep(Duration::from_millis(10)); thread::sleep(Duration::from_millis(10));
let received_messages = messages.lock().unwrap(); let received_messages = messages.read().unwrap();
assert_eq!(received_messages.len(), 1); assert_eq!(received_messages.len(), 1);
assert_eq!(received_messages[0], vec![0, 0, 0, 0, 33, 0, 0, 0]); assert_eq!(received_messages[0], vec![0, 0, 0, 0, 33, 0, 0, 0]);
} }
@@ -465,7 +465,7 @@ mod tests {
true, // no_pierce = true, should send init message true, // no_pierce = true, should send init message
"own_username".to_string(), "own_username".to_string(),
); );
let dummy_context = Arc::new(Mutex::new(ClientContext::new())); let dummy_context = Arc::new(RwLock::new(ClientContext::new()));
let _ = download_peer let _ = download_peer
.download_file(dummy_context, None, None) .download_file(dummy_context, None, None)
.unwrap(); .unwrap();
@@ -473,7 +473,7 @@ mod tests {
// Give the client time to send messages // Give the client time to send messages
thread::sleep(Duration::from_millis(10)); thread::sleep(Duration::from_millis(10));
let received_messages = messages.lock().unwrap(); let received_messages = messages.read().unwrap();
assert_eq!(received_messages.len(), 1); // Expect one init message assert_eq!(received_messages.len(), 1); // Expect one init message
assert_eq!( assert_eq!(
received_messages[0], received_messages[0],
@@ -489,7 +489,7 @@ mod tests {
#[test] #[test]
pub fn test_download_file() { pub fn test_download_file() {
let test_data = b"test file content"; let test_data = b"test file content";
let messages = Arc::new(Mutex::new(Vec::<Vec<u8>>::new())); let messages = Arc::new(RwLock::new(Vec::<Vec<u8>>::new()));
let messages_clone = Arc::clone(&messages); let messages_clone = Arc::clone(&messages);
// Create a test server that sends test data // Create a test server that sends test data
@@ -509,7 +509,7 @@ mod tests {
if let Ok(bytes_read) = stream.read(&mut buffer) { if let Ok(bytes_read) = stream.read(&mut buffer) {
if bytes_read > 0 { if bytes_read > 0 {
messages_clone messages_clone
.lock() .write()
.unwrap() .unwrap()
.push(buffer[..bytes_read].to_vec()); .push(buffer[..bytes_read].to_vec());
} }
@@ -537,7 +537,7 @@ mod tests {
"test_user".to_string(), "test_user".to_string(),
); );
let dummy_context = Arc::new(Mutex::new(ClientContext::new())); let dummy_context = Arc::new(RwLock::new(ClientContext::new()));
let result = download_peer.download_file( let result = download_peer.download_file(
dummy_context, dummy_context,
Some(test_data.len()), Some(test_data.len()),
@@ -654,8 +654,7 @@ mod tests {
let mut processor = StreamProcessor::new(false, 123); let mut processor = StreamProcessor::new(false, 123);
let data = b"test data"; let data = b"test data";
let result = processor.process_data_chunk(data); processor.process_data_chunk(data);
assert!(result.is_ok());
assert_eq!(processor.total_bytes, data.len()); assert_eq!(processor.total_bytes, data.len());
assert_eq!(processor.buffer, data); assert_eq!(processor.buffer, data);
} }
+5 -5
View File
@@ -23,7 +23,7 @@ use std::io::{self, Write};
use std::net::{TcpStream, ToSocketAddrs}; use std::net::{TcpStream, ToSocketAddrs};
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Barrier, Mutex}; use std::sync::{Arc, Barrier, RwLock};
use std::thread::{self}; use std::thread::{self};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@@ -193,7 +193,7 @@ pub enum ServerOperation {
pub struct Server { pub struct Server {
address: PeerAddress, address: PeerAddress,
sender: Sender<ServerOperation>, sender: Sender<ServerOperation>,
context: Arc<Mutex<Context>>, context: Arc<RwLock<Context>>,
} }
impl Server { impl Server {
pub fn new( pub fn new(
@@ -205,7 +205,7 @@ impl Server {
Receiver<ServerOperation>, Receiver<ServerOperation>,
) = mpsc::channel(); ) = mpsc::channel();
let context = Arc::new(Mutex::new(Context::new())); let context = Arc::new(RwLock::new(Context::new()));
let mut server = Self { let mut server = Self {
address, address,
@@ -353,7 +353,7 @@ impl Server {
} }
} }
ServerOperation::LoginStatus(message) => { ServerOperation::LoginStatus(message) => {
context.lock().unwrap().logged_in = Some(message); context.write().unwrap().logged_in = Some(message);
} }
ServerOperation::PierceFirewall(token) => { ServerOperation::PierceFirewall(token) => {
if let Err(e) = write_stream.write_all( if let Err(e) = write_stream.write_all(
@@ -457,7 +457,7 @@ impl Server {
} }
{ {
logged_in = context.lock().unwrap().logged_in logged_in = context.read().unwrap().logged_in
} }
if logged_in.is_some() { if logged_in.is_some() {
-1
View File
@@ -1 +0,0 @@
test file content