Most minimal example I could make (playground):
use crossbeam::queue::SegQueue;
use parking_lot::{Mutex, MutexGuard};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
struct ReadySignal {
client: Arc<Mutex<Client>>,
section: usize,
}
struct Client {
id: usize,
file: Vec<usize>,
}
fn main() {
let max_clients = 10;
let file_size = 1000;
let req_q = Arc::new(SegQueue::new());
let v: Vec<Arc<Mutex<Client>>> = Vec::new();
let client_arr = Arc::new(Mutex::new(v));
let client_arr_c = Arc::clone(&client_arr);
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(10));
let mut display = String::new();
let client_arr_c = client_arr_c.lock();
for i in client_arr_c.iter() {
let client = i.lock();
let total = client.file.len();
let mut done = 0;
for j in client.file.iter() {
if *j == 1 {
done += 1;
}
}
let percentage = (done as f64 / total as f64) * 100.0;
let client_per: String = format!("Client {}: {:.2}%\n", client.id, percentage);
MutexGuard::unlock_fair(client);
display += &client_per;
}
MutexGuard::unlock_fair(client_arr_c);
println!("\x1B[2J\x1B[1;1H{}", display);
});
let client_arr_c = Arc::clone(&client_arr);
std::thread::spawn(move || {
for i in 0..max_clients {
thread::sleep(Duration::from_millis(1000));
let mut file_init = 0;
if i == 0 {
file_init = 1;
}
let client = Arc::new(Mutex::new(Client {
id: i + 1,
file: vec![file_init; file_size],
}));
let clientc = Arc::clone(&client);
let req_qc = Arc::clone(&req_q);
thread::spawn(move || {
for j in 0..file_size {
thread::sleep(Duration::from_millis(10));
let clientcc = clientc.lock();
if clientcc.file[j] == 0 {
let req = ReadySignal {
client: Arc::clone(&clientc),
section: j,
};
req_qc.push(req);
MutexGuard::unlock_fair(clientcc);
continue;
}
}
});
let clientc = Arc::clone(&client);
let req_qc = Arc::clone(&req_q);
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(20));
let clientc = clientc.lock();
if req_qc.is_empty() {
continue;
}
let req = req_qc.pop().unwrap();
if clientc.file[req.section] == 1 {
let mut req_client = req.client.lock();
req_client.file[req.section] = 1;
MutexGuard::unlock_fair(req_client);
MutexGuard::unlock_fair(clientc);
} else {
req_qc.push(req);
}
});
let clientc = Arc::clone(&client);
client_arr_c.lock().push(clientc);
}
});
loop {}
}
Dependencies are crossbeam = "0.7.3" and parking_lot = "0.11.0".
Better run it in a new Cargo project rather than the playground because the stdout formatting is messed up there.
The basic idea of this program is to simulate a simple p2p network. The first client already has the file (client.file has a Vec filled with 1s). A new client joins every second with no file (client.file has a Vec filled with 0s). Right after a new client is created, 2 threads are created for that client. The first thread (line 62-77) checks which parts of the client's file is 0 and puts a request in the queue for each. The second thread (line 80-97) takes requests out from the queue and changes those 0s into 1s.
The thread at the start (line 24-47) is for checking the progress and printing the output by looping over a vec containing all the clients, every 10ms.
If you run this you can see the percentages of each client keep increasing up to a point where it stops. This point varies from run to run. The intended behavior is for all clients to reach 100%. But this intended behavior has happened only a very few times. What could be causing the deadlock? And how do I stop that?