Мой сервер использует Barrier
, чтобы уведомить клиента, когда можно попытаться подключиться. Без барьера мы рискуем случайно выйти из строя, так как нет гарантии, что сокет сервера был бы связан.
Теперь представьте, что сервер паникует - например, пытается привязать сокет к порту 80. Клиент останется wait()
-ing навсегда. Мы не можем join()
серверный поток, чтобы выяснить, не запаниковал ли он, потому что join()
является блокирующей операцией - если мы join()
, мы не сможем connect()
.
Как правильно выполнять такую синхронизацию, учитывая, что API std::sync
не предоставляют методы с тайм-аутами?
Это просто MCVE, чтобы продемонстрировать проблему. У меня был похожий случай в модульном тесте - он был запущен навсегда.
use std::{
io::prelude::*,
net::{SocketAddr, TcpListener, TcpStream},
sync::{Arc, Barrier},
};
fn main() {
let port = 9090;
//let port = 80;
let barrier = Arc::new(Barrier::new(2));
let server_barrier = barrier.clone();
let client_sync = move || {
barrier.wait();
};
let server_sync = Box::new(move || {
server_barrier.wait();
});
server(server_sync, port);
//server(Box::new(|| { no_sync() }), port); //use to test without synchronisation
client(&client_sync, port);
//client(&no_sync, port); //use to test without synchronisation
}
fn no_sync() {
// do nothing in order to demonstrate the need for synchronization
}
fn server(sync: Box<Fn() + Send + Sync>, port: u16) {
std::thread::spawn(move || {
std::thread::sleep_ms(100); //there is no guarantee when the os will schedule the thread. make it 100% reproducible
let addr = SocketAddr::from(([127, 0, 0, 1], port));
let socket = TcpListener::bind(&addr).unwrap();
println!("server socket bound");
sync();
let (mut client, _) = socket.accept().unwrap();
client.write_all(b"hello mcve").unwrap();
});
}
fn client(sync: &Fn(), port: u16) {
sync();
let addr = SocketAddr::from(([127, 0, 0, 1], port));
let mut socket = TcpStream::connect(&addr).unwrap();
println!("client socket connected");
let mut buf = String::new();
socket.read_to_string(&mut buf).unwrap();
println!("client received: {}", buf);
}