use traceforge::msg::Message;
use traceforge::*;
use std::collections::HashMap;
mod utils;
pub trait MessageTraits: Message + Send + 'static {}
impl<E> MessageTraits for E where E: Message + Send + 'static {}
pub(crate) trait Actor<M: MessageTraits, RetType> {
fn handle(&mut self, _msg: M) {}
fn stop(&self) -> RetType;
fn can_shutdown(&self) -> bool;
fn execute(&mut self) -> RetType {
while !self.can_shutdown() {
let msg = traceforge::recv_msg_block();
self.handle(msg);
}
self.stop()
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum TokenStatus {
Generated,
Consumed,
}
type Token = usize;
#[derive(Clone, Debug, PartialEq)]
pub enum ServerMessage {
Generate(ThreadId), Consume(ThreadId, Token),
Terminate, }
#[derive(Clone, Debug, PartialEq)]
pub enum ClientMessage {
Token(Token),
Ok,
NoSuchToken,
ConsumedToken,
}
use traceforge::thread::{JoinHandle, ThreadId};
struct Server {
tokens: HashMap<Token, TokenStatus>,
next_token: usize,
can_shutdown: bool,
}
impl Server {
pub fn default() -> Self {
Self {
tokens: HashMap::new(),
next_token: 0,
can_shutdown: false,
}
}
fn generate(&mut self) -> Token {
let t = self.next_token;
self.tokens.insert(t, TokenStatus::Generated);
self.next_token += 1;
t
}
}
impl Actor<ServerMessage, ()> for Server {
fn handle(&mut self, m: ServerMessage) {
match m {
ServerMessage::Generate(tid) => {
let t = self.generate();
traceforge::send_msg(tid, ClientMessage::Token(t));
}
ServerMessage::Consume(tid, token) => {
let t = self.tokens.get(&token);
if t.is_none() {
traceforge::send_msg(tid, ClientMessage::NoSuchToken);
return;
}
let t = t.unwrap();
match t {
TokenStatus::Generated => {
traceforge::send_msg(tid, ClientMessage::Ok);
let _ = self.tokens.insert(token, TokenStatus::Consumed);
}
TokenStatus::Consumed => traceforge::send_msg(tid, ClientMessage::ConsumedToken),
}
}
ServerMessage::Terminate => self.can_shutdown = true,
}
}
fn can_shutdown(&self) -> bool {
self.can_shutdown
}
fn stop(&self) -> () {}
}
fn client(server_id: ThreadId) {
let myid = thread::current().id();
traceforge::send_msg(server_id, ServerMessage::Generate(myid));
let r: ClientMessage = recv_msg_block();
match r {
ClientMessage::Token(t) => {
traceforge::send_msg(server_id, ServerMessage::Consume(myid, t));
let ok: ClientMessage = recv_msg_block();
assert_eq!(ok, ClientMessage::Ok);
traceforge::send_msg(server_id, ServerMessage::Consume(myid, t));
let nok: ClientMessage = recv_msg_block();
assert_eq!(nok, ClientMessage::ConsumedToken);
}
_ => panic!("Strange response"),
}
}
fn client2(server_id: ThreadId) {
let myid = thread::current().id();
traceforge::send_msg(
server_id,
ServerMessage::Consume(myid, Token::from(134usize)),
);
let r: ClientMessage = recv_msg_block();
assert_eq!(r, ClientMessage::NoSuchToken);
}
fn client_server_scenario() {
let server: JoinHandle<()> = thread::spawn(|| {
let mut s = Server::default();
s.execute();
});
let server_id = server.thread().id();
let _ = thread::spawn(move || {
client(server_id.clone());
});
let _ = thread::spawn(move || {
client2(server_id.clone());
});
traceforge::send_msg(server_id, ServerMessage::Terminate);
let _ = server.join();
}
#[test]
fn client_server_test() {
let stats = traceforge::verify(Config::builder().build(), client_server_scenario);
println!("Stats = {}, {}", stats.execs, stats.block);
}