Do not use cpu when waiting for opponent move.

This commit is contained in:
Artlef 2020-05-04 23:45:34 +02:00
parent 2c4b339bc2
commit c692be6159

View File

@ -6,18 +6,18 @@ use std::collections::HashMap;
use std::fs; use std::fs;
use std::io; use std::io;
use std::os::unix::net::{UnixListener, UnixStream}; use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::mpsc::{channel, Receiver, TryRecvError}; use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Condvar, Mutex};
use std::thread; use std::thread;
use std::time;
struct Server { struct Server {
id: usize, id: usize,
chess_position: Arc<Mutex<Chess>>, chess_position: Arc<Mutex<Chess>>,
players: Arc<Mutex<HashMap<usize, (Player, Arc<Mutex<String>>)>>>, players: Arc<Mutex<HashMap<usize, (Player, Sender<String>, Arc<(Mutex<bool>, Condvar)>)>>>,
others_serv_msg_buf: Arc<Mutex<String>>, others_serv_msg_recv: Receiver<String>,
client_message_recv: Receiver<String>, client_message_recv: Receiver<String>,
cvar: Arc<(Mutex<bool>, Condvar)>,
} }
fn main() { fn main() {
@ -32,16 +32,20 @@ fn main() {
for stream in listener.incoming() { for stream in listener.incoming() {
match stream { match stream {
Ok(stream) => { Ok(stream) => {
let client_message_recv = setup_client_message_recv(&stream).unwrap(); let condvar_pair = Arc::new((Mutex::new(false), Condvar::new()));
let client_message_recv =
setup_client_message_recv(&stream, condvar_pair.clone()).unwrap();
let (others_serv_msg_sender, others_serv_msg_recv) = channel();
let server = Server { let server = Server {
id: counter, id: counter,
chess_position: chess.clone(), chess_position: chess.clone(),
players: players.clone(), players: players.clone(),
others_serv_msg_buf: Arc::new(Mutex::new(String::new())), others_serv_msg_recv,
client_message_recv, client_message_recv,
cvar: condvar_pair,
}; };
/* connection succeeded */ /* connection succeeded */
thread::spawn(move || handle_player(stream, server)); thread::spawn(move || handle_player(stream, server, others_serv_msg_sender));
counter += 1; counter += 1;
} }
Err(err) => { Err(err) => {
@ -52,8 +56,8 @@ fn main() {
} }
} }
fn handle_player(mut stream: UnixStream, server: Server) { fn handle_player(mut stream: UnixStream, server: Server, others_serv_msg_sender: Sender<String>) {
match initialize_client(&mut stream, &server) { match initialize_client(&mut stream, &server, others_serv_msg_sender) {
Ok((player, player_turn)) => main_loop(&mut stream, &server, player, player_turn), Ok((player, player_turn)) => main_loop(&mut stream, &server, player, player_turn),
Err(e) => println!("User id {} could not be initialized: {}", server.id, e), Err(e) => println!("User id {} could not be initialized: {}", server.id, e),
}; };
@ -63,9 +67,10 @@ fn handle_player(mut stream: UnixStream, server: Server) {
fn initialize_client( fn initialize_client(
stream: &mut UnixStream, stream: &mut UnixStream,
server: &Server, server: &Server,
others_serv_msg_sender: Sender<String>,
) -> Result<(Player, Color), RecvPositionError> { ) -> Result<(Player, Color), RecvPositionError> {
//create player //create player
let player = create_player(server, stream)?; let player = create_player(server, stream, others_serv_msg_sender)?;
let player_turn: Color; let player_turn: Color;
//send current position to the player //send current position to the player
println!( println!(
@ -106,9 +111,13 @@ fn main_loop(stream: &mut UnixStream, server: &Server, player: Player, mut playe
Err(e) => println!("Error: {}", e), Err(e) => println!("Error: {}", e),
}; };
let chessfen = fen::fen(&*chess); let chessfen = fen::fen(&*chess);
for (id, (_, others_serv_msg_buf)) in players.iter() { for (id, (_, others_serv_msg_sender, cvar_pair)) in players.iter() {
if server.id != *id { if server.id != *id {
others_serv_msg_buf.lock().unwrap().push_str(&chessfen); others_serv_msg_sender.send(chessfen.clone());
let (lock, cvar) = &**cvar_pair;
let mut message_sent = lock.lock().unwrap();
*message_sent = true;
cvar.notify_one();
} }
} }
if clichess::write_to_stream(stream, chessfen).is_err() { if clichess::write_to_stream(stream, chessfen).is_err() {
@ -134,40 +143,44 @@ fn wait_for_opponent_move(server: &Server) -> Result<String, RecvPositionError>
println!("server id: {}, waiting for move to send...", server.id); println!("server id: {}, waiting for move to send...", server.id);
//wait: either we receive next position from other server threads, or we receive //wait: either we receive next position from other server threads, or we receive
//"exit" from the client. //"exit" from the client.
let returned_result: Result<String, RecvPositionError>; let mut returned_result = Err(RecvPositionError::UserCanceledError);
loop { let (lock, cvar) = &*server.cvar;
{ let mut message_sent = lock.lock().unwrap();
let mut others_serv_msg_buf = server.others_serv_msg_buf.lock().unwrap(); *message_sent = false;
if !others_serv_msg_buf.is_empty() { while !*message_sent {
returned_result = Ok(others_serv_msg_buf.clone()); message_sent = cvar.wait(message_sent).unwrap();
others_serv_msg_buf.clear(); }
break; *message_sent = false;
match server.others_serv_msg_recv.try_recv() {
Ok(msg) => {
returned_result = Ok(msg);
}
Err(TryRecvError::Disconnected) => {
println!("Error: other server thread disconnected while sending move.")
}
Err(TryRecvError::Empty) => { /*nothing to do*/ }
}
match server.client_message_recv.try_recv() {
Ok(msg) => {
if msg == EXIT_MSG {
returned_result = Err(RecvPositionError::UserCanceledError);
} else { } else {
match server.client_message_recv.try_recv() { println!("Client sent message while it's not its turn, this is an error.");
Ok(msg) => { println!("Here is the message: {}", msg);
if msg == EXIT_MSG { returned_result = Err(RecvPositionError::UserCanceledError);
returned_result = Err(RecvPositionError::UserCanceledError);
break;
} else {
println!(
"Client sent message while it's not its turn, this is an error."
);
println!("Here is the message: {}", msg);
returned_result = Err(RecvPositionError::UserCanceledError);
break;
}
}
Err(TryRecvError::Disconnected) => println!("Error: client disconnected."),
Err(TryRecvError::Empty) => continue,
}
} }
} }
thread::sleep(time::Duration::from_millis(10)); Err(TryRecvError::Disconnected) => println!("Error: client disconnected."),
Err(TryRecvError::Empty) => {}
} }
returned_result returned_result
} }
fn create_player(server: &Server, stream: &mut UnixStream) -> Result<Player, RecvPositionError> { fn create_player(
server: &Server,
stream: &mut UnixStream,
others_serv_msg_sender: Sender<String>,
) -> Result<Player, RecvPositionError> {
println!("Creating player {}...", server.id); println!("Creating player {}...", server.id);
//get player name and pubkey //get player name and pubkey
let username_pubkey_json = server let username_pubkey_json = server
@ -188,7 +201,7 @@ fn create_player(server: &Server, stream: &mut UnixStream) -> Result<Player, Rec
}; };
players.insert( players.insert(
server.id, server.id,
(player.clone(), server.others_serv_msg_buf.clone()), (player.clone(), others_serv_msg_sender, server.cvar.clone()),
); );
println!("Created player {}", server.id); println!("Created player {}", server.id);
Ok(player) Ok(player)
@ -224,7 +237,7 @@ fn receive_user_role(
let mut available_roles_after_choice = let mut available_roles_after_choice =
vec![UserRole::White, UserRole::Black, UserRole::Spectator]; vec![UserRole::White, UserRole::Black, UserRole::Spectator];
let players = server.players.lock().unwrap(); let players = server.players.lock().unwrap();
for (_, (player, _)) in players.iter() { for (_, (player, _, _)) in players.iter() {
match available_roles_after_choice match available_roles_after_choice
.iter() .iter()
.position(|r| *r == player.role) .position(|r| *r == player.role)
@ -258,7 +271,7 @@ fn receive_user_role(
fn compute_available_roles_to_str(server: &Server) -> String { fn compute_available_roles_to_str(server: &Server) -> String {
let mut available_roles = vec![UserRole::White, UserRole::Black, UserRole::Spectator]; let mut available_roles = vec![UserRole::White, UserRole::Black, UserRole::Spectator];
let players = server.players.lock().unwrap(); let players = server.players.lock().unwrap();
for (_, (player, _)) in players.iter() { for (_, (player, _, _)) in players.iter() {
match available_roles.iter().position(|r| *r == player.role) { match available_roles.iter().position(|r| *r == player.role) {
Some(index) => available_roles.remove(index), Some(index) => available_roles.remove(index),
None => continue, None => continue,
@ -275,13 +288,16 @@ fn player_disconnected(server: &Server) {
let mut players = server.players.lock().unwrap(); let mut players = server.players.lock().unwrap();
let player_and_receiving_buf = players.get(&server.id); let player_and_receiving_buf = players.get(&server.id);
if player_and_receiving_buf.is_some() { if player_and_receiving_buf.is_some() {
let (player, _) = player_and_receiving_buf.expect("is some"); let (player, _, _) = player_and_receiving_buf.expect("is some");
println!("Player {} disconnected.", player.username); println!("Player {} disconnected.", player.username);
} }
players.remove(&server.id); players.remove(&server.id);
} }
fn setup_client_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>> { fn setup_client_message_recv(
stream: &UnixStream,
cvar_pair: Arc<(Mutex<bool>, Condvar)>,
) -> io::Result<Receiver<String>> {
let (sender, receiver) = channel(); let (sender, receiver) = channel();
let thread_stream = stream.try_clone()?; let thread_stream = stream.try_clone()?;
@ -290,6 +306,11 @@ fn setup_client_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>
//wait for client message //wait for client message
let buffer = let buffer =
clichess::read_line_from_stream(&thread_stream).expect("Error message from server"); clichess::read_line_from_stream(&thread_stream).expect("Error message from server");
//notify the server if waiting
let (lock, cvar) = &*cvar_pair;
let mut message_sent = lock.lock().unwrap();
*message_sent = true;
cvar.notify_one();
let send_result = sender.send(buffer); let send_result = sender.send(buffer);
// stop the thread during a disconnection // stop the thread during a disconnection
if send_result.is_err() { if send_result.is_err() {