From 690713358ed972700f14f2b116af68409a1ce3b4 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sun, 26 Jul 2020 20:11:25 +0100 Subject: [PATCH] compiles again --- src/api.rs | 2 +- src/cmdlistener.rs | 68 ++++++++++++++++++----------- src/commands.rs | 12 +++--- src/error.rs | 25 ++++++++--- src/global.rs | 105 ++++++++++++++++++++++++++++----------------- src/http.rs | 2 +- src/pieces.rs | 2 +- src/session.rs | 4 +- src/sse.rs | 22 +++++----- 9 files changed, 150 insertions(+), 92 deletions(-) diff --git a/src/api.rs b/src/api.rs index 8eafb805..295a5e7b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -59,7 +59,7 @@ fn api_piece_op(form : Json>) // thread::sleep(Duration::from_millis(2000)); let iad = lookup_token(&form.ctoken)?; let client = iad.ident; - let mut g = iad.g.lock()?; + let mut g = iad.gref.lock()?; let g = &mut *g; let cl = &g.clients.byid(client)?; // ^ can only fail if we raced diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 94716c9e..5a291d08 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] +pub use crate::from_instance_lock_error; + use crate::imports::*; //use std::os::unix::prelude; @@ -61,6 +63,7 @@ use MgmtResponse::*; use MgmtError::*; type ME = MgmtError; +from_instance_lock_error!{MgmtError} #[throws(CSE)] fn decode_and_process(cs: &mut CommandStream, s: &str) { @@ -210,12 +213,12 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { scoped_name : name, }; - let ami = Instance::new(name, gs)?; - let g = ami.lock(); + let gref = Instance::new(name, gs)?; + let mut ig = gref.lock()?; - execute_for_game(cs, &ami, subcommands, MgmtGameUpdateMode::Bulk) + execute_for_game(cs, &mut ig, subcommands, MgmtGameUpdateMode::Bulk) .map_err(|e|{ - Instance::destroy(ami); + Instance::destroy(ig); e })?; @@ -237,16 +240,26 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { } } +#[derive(Debug,Default)] struct UpdateHandlerBulk { pieces : SecondarySlotMap>, } +#[derive(Debug)] enum UpdateHandler { Bulk(UpdateHandlerBulk), Online, } impl UpdateHandler { + fn from_how(how: MgmtGameUpdateMode) -> Self { + use UpdateHandler::*; + match how { + MgmtGameUpdateMode::Bulk => Bulk(Default::default()), + MgmtGameUpdateMode::Online => Online, + } + } + #[throws(SVGProcessingError)] fn accumulate(&mut self, g: &mut Instance, upieces: Vec<(PieceId,PieceUpdateOp<()>)>, @@ -257,22 +270,22 @@ impl UpdateHandler { for (upiece, uuop) in upieces { use PieceUpdateOp::*; let ne = match (bulk.pieces.get(upiece), uuop) { - ( None , e ) => e, - ( Some( Insert(()) ) , Some( Delete(()) ) ) => None, - ( Some( Insert(()) ) , _ ) => Some( Insert(()) ), - ( Some( Delete(()) ) , _ ) => Some( Modify(()) ), - ( _ , _ ) => Some( Modify(()) ), + ( None , e ) => Some( e ), + ( Some( Insert(()) ) , Delete() ) => None, + ( Some( Insert(()) ) , _ ) => Some( Insert(()) ), + ( Some( Delete( ) ) , _ ) => Some( Modify(()) ), + ( _ , _ ) => Some( Modify(()) ), }; match ne { - Some(ne) => bulk.pieces[upiece] = ne, - None => bulk.pieces.remove(upiece), + Some(ne) => { bulk.pieces[upiece] = ne; }, + None => { bulk.pieces.remove(upiece); }, }; } let _logs = ulogs; }, Online => { let estimate = upieces.len() + ulogs.len(); - let buf = PrepareUpdatesBuffer::new(g, None, Some(estimate)); + let mut buf = PrepareUpdatesBuffer::new(g, None, Some(estimate)); for (upiece, uuop) in upieces { let lens = TransparentLens { }; buf.piece_update(upiece, uuop, &lens)?; @@ -283,19 +296,20 @@ impl UpdateHandler { } #[throws(SVGProcessingError)] - fn complete(&mut self, g: &mut Instance) { + fn complete(self, _cs: &CommandStream, g: &mut InstanceGuard) { use UpdateHandler::*; match self { Bulk(bulk) => { - let buf = PrepareUpdatesBuffer::new(g, None, None); - for (upiece, uuop) in self.pieces { + let mut buf = PrepareUpdatesBuffer::new(g, None, None); + for (upiece, uuop) in bulk.pieces { let lens = TransparentLens { }; buf.piece_update(upiece, uuop, &lens)?; } - buf.log_updates(LogEntry { + buf.log_updates(vec![LogEntry { html: "The facilitator (re)configured the game".to_owned(), - })?; + // xxx use cs.desc + }])?; }, Online => { }, } @@ -303,11 +317,10 @@ impl UpdateHandler { } #[throws(ME)] -fn execute_for_game(cs: &CommandStream, amu: &InstanceRef, - subcommands: Vec, - how: MgmtGameUpdateMode) { - let g = amu.lock()?; - let mut subcommands = subcommands.drain.zip(0..); +fn execute_for_game(cs: &CommandStream, ig: &mut InstanceGuard, + mut subcommands: Vec, + how: MgmtGameUpdateMode) -> MgmtResponse { + let mut subcommands = subcommands.drain(0..).zip(0..); let mut uh = UpdateHandler::from_how(how); let response = 'subcommands: loop { @@ -316,22 +329,25 @@ fn execute_for_game(cs: &CommandStream, amu: &InstanceRef, Some(r) => r, }; - let (upieces, ulogs) = match execute_game_update(&mut g.gs, subcommand) { + let (upieces, ulogs) = match execute_game_update(&mut ig.gs, subcommand) { Err(e) => break 'subcommands ErrorAfter(index, e), Ok(r) => r, }; - uh.accumulate(upieces, ulogs)?; + uh.accumulate(ig, upieces, ulogs)?; }; - uh.complete()?; + uh.complete(cs,ig)?; + response } #[throws(ME)] -fn execute_game_update(gs: &mut GameState, update: MgmtGameUpdate) +fn execute_game_update(_gs: &mut GameState, update: MgmtGameUpdate) -> (Vec<(PieceId,PieceUpdateOp<()>)>, Vec) { + use MgmtGameUpdate::*; match update { + Noop { } => (vec![], vec![]), } } diff --git a/src/commands.rs b/src/commands.rs index 14391411..1f1b6d08 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -3,7 +3,7 @@ use crate::imports::*; #[derive(Debug,Serialize,Deserialize)] pub enum MgmtCommand { - Noop { }, + Noop(), SetScope(ManagementScope), CreateGame(String, Vec), // AddPiece(Box), @@ -11,12 +11,12 @@ pub enum MgmtCommand { #[derive(Debug,Serialize,Deserialize)] pub enum MgmtGameUpdate { - Noop { }, + Noop(), } #[derive(Debug,Serialize,Deserialize)] pub enum MgmtResponse { - Fine { }, + Fine(), Error(MgmtError), ErrorAfter(usize, MgmtError), } @@ -27,13 +27,15 @@ pub enum MgmtGameUpdateMode { Bulk, } -#[derive(Debug,Error)] +#[derive(Debug,Error,Serialize,Deserialize)] pub enum MgmtError { ParseFailed(String), AuthorisationError, NoScope, AlreadyExists, - XXXU(&'static str), + GameBeingDestroyed, + GameCorrupted, + SVGProcessingFailed(#[from] SVGProcessingError), } display_as_debug!{MgmtError} diff --git a/src/error.rs b/src/error.rs index b0b12b9c..e62837df 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,6 @@ use crate::imports::*; -use std::sync::PoisonError; - #[derive(Error,Debug)] #[error("operation error {:?}",self)] pub enum GameError { @@ -35,15 +33,30 @@ pub enum OnlineError { #[error("Server MessagePack decoding error (game load failed) {0:?}")] ServerMessagePackDecodeFail(#[from] rmp_serde::decode::Error), } +from_instance_lock_error!{OnlineError} pub type StartupError = anyhow::Error; pub use OnlineError::{NoClient,NoPlayer}; -use OnlineError::*; - -impl From> for OnlineError { - fn from(_: PoisonError) -> OnlineError { GameCorrupted } +#[derive(Error,Debug)] +pub enum InstanceLockError { + GameCorrupted, + GameBeingDestroyed, +} +#[macro_export] +macro_rules! from_instance_lock_error { + ($into:ident) => { + impl From for $into { + fn from(e: InstanceLockError) -> $into { + use InstanceLockError::*; + match e { + GameCorrupted => $into::GameCorrupted, + GameBeingDestroyed => $into::GameBeingDestroyed, + } + } + } + } } pub trait ById { diff --git a/src/global.rs b/src/global.rs index 9464ea4c..9bfdced9 100644 --- a/src/global.rs +++ b/src/global.rs @@ -2,6 +2,8 @@ use crate::imports::*; use lazy_static::lazy_static; +use std::sync::PoisonError; + #[allow(dead_code)] const SAVE_DIRECTORY : &str = "save"; @@ -21,11 +23,18 @@ pub struct InstanceName { pub scope: ManagementScope, pub scoped_name: String, } -pub type InstanceRef = Arc>; + +#[derive(Debug,Clone)] +pub struct InstanceRef (Arc>); + +#[derive(Debug)] +pub struct InstanceContainer { + live : bool, + g : Instance, +} #[derive(Debug)] pub struct Instance { - pub live : bool, pub name : Arc, pub gs : GameState, pub clients : DenseSlotMap, @@ -48,8 +57,8 @@ pub struct Client { #[derive(Debug)] pub struct InstanceGuard<'g> { - ig : MutexGuard<'g,Instance>, - amu : Arc>, + pub c : MutexGuard<'g,InstanceContainer>, + pub gref : InstanceRef, } #[derive(Default)] @@ -72,11 +81,16 @@ struct InstanceSaveAccesses { tokens_players : Vec<(RawTokenStr, PlayerId)>, } +display_as_debug!{InstanceLockError} +impl From> for InstanceLockError { + fn from(_: PoisonError) -> Self { Self::GameCorrupted } +} + // ---------- API ---------- #[derive(Clone,Debug)] pub struct InstanceAccessDetails { - pub g : Arc>, + pub gref : InstanceRef, pub ident : Id, } @@ -116,14 +130,22 @@ lazy_static! { // ---------- Player and instance API ---------- +impl InstanceRef { + #[throws(InstanceLockError)] + pub fn lock<'g>(&'g self) -> InstanceGuard<'g> { + let c = self.0.lock()?; + if !c.live { throw!(InstanceLockError::GameBeingDestroyed) } + InstanceGuard { c, gref: self.clone() } + } +} + impl Instance { /// Returns `None` if a game with this name already exists #[throws(MgmtError)] pub fn new(name: InstanceName, gs: GameState) -> InstanceRef { let name = Arc::new(name); - let inst = Instance { - live : true, + let g = Instance { name : name.clone(), gs, clients : Default::default(), @@ -132,6 +154,13 @@ impl Instance { tokens_clients : Default::default(), }; + let cont = InstanceContainer { + live : true, + g, + }; + + let gref = InstanceRef(Arc::new(Mutex::new(cont))); + let mut games = GLOBAL.games.write().unwrap(); let entry = games.entry(name); @@ -141,20 +170,15 @@ impl Instance { Occupied(_) => throw!(MgmtError::AlreadyExists), }; - let ami = Arc::new(Mutex::new(inst)); - entry.insert(ami.clone()); - ami - } + entry.insert(gref.clone()); - #[throws(OE)] - pub fn lock<'g>(amu : &'g Arc>) -> InstanceGuard<'g> { - let ig = amu.lock()?; - if !ig.live { throw!(OnlineError::GameBeingDestroyed) } - InstanceGuard { ig, amu: amu.clone() } + // xxx save, but first release the GLOBAL.games lock + + gref } - pub fn destroy(g: InstanceGuard) { - g.live = false; + pub fn destroy(mut g: InstanceGuard) { + g.c.live = false; // remove the files GLOBAL.games.write().unwrap().remove(&g.name); InstanceGuard::forget_all_tokens(&mut g.tokens_players); @@ -164,24 +188,24 @@ impl Instance { impl Deref for InstanceGuard<'_> { type Target = Instance; - fn deref(&self) -> &Instance { &self.ig } + fn deref(&self) -> &Instance { &self.c.g } } impl DerefMut for InstanceGuard<'_> { - fn deref_mut(&mut self) -> &mut Instance { &mut self.ig } + fn deref_mut(&mut self) -> &mut Instance { &mut self.c.g } } impl InstanceGuard<'_> { #[throws(OE)] pub fn player_new(&mut self, newplayer: PlayerState) -> PlayerId { - let player = self.ig.gs.players.insert(newplayer); - self.ig.updates.insert(player, Default::default()); + let player = self.c.g.gs.players.insert(newplayer); + self.c.g.updates.insert(player, Default::default()); self.save_game_now()?; player } #[throws(OE)] pub fn player_access_register(&mut self, token: RawToken, player: PlayerId) { - let iad = InstanceAccessDetails { g : self.amu.clone(), ident : player }; + let iad = InstanceAccessDetails { gref : self.gref.clone(), ident : player }; self.token_register(token, iad); self.save_access_now()?; } @@ -191,7 +215,7 @@ impl InstanceGuard<'_> { token: RawToken, iad: InstanceAccessDetails ) { - Id::tokens_registry(&mut *self.ig, PRIVATE_Y).tr.insert(token.clone()); + Id::tokens_registry(&mut self.c.g, PRIVATE_Y).tr.insert(token.clone()); Id::global_tokens(PRIVATE_Y).write().unwrap().insert(token, iad); } @@ -232,7 +256,7 @@ impl InstanceGuard<'_> { #[throws(OE)] fn save_game_now(&mut self) { self.save_something("g-", |s,w| { - rmp_serde::encode::write_named(w, &s.ig.gs) + rmp_serde::encode::write_named(w, &s.c.g.gs) })?; } @@ -241,7 +265,7 @@ impl InstanceGuard<'_> { self.save_something("a-", |s,w| { let global_players = GLOBAL.players.read().unwrap(); let tokens_players : Vec<(&str, PlayerId)> = - s.ig.tokens_players.tr + s.c.g.tokens_players.tr .iter() .map(|token| global_players.get(token) @@ -262,7 +286,7 @@ impl InstanceGuard<'_> { } #[throws(OE)] - pub fn load(name: InstanceName) -> Arc> { + pub fn load(name: InstanceName) -> InstanceRef { // xxx scan on startup, rather than asking caller to specify names // xxx should take a file lock on save area let gs : GameState = Self::load_something(&name, "g-")?; @@ -274,30 +298,33 @@ impl InstanceGuard<'_> { } let name = Arc::new(name); - let inst = Instance { - live: true, + let g = Instance { name, gs, updates, clients : Default::default(), tokens_clients : Default::default(), tokens_players : Default::default(), }; + let cont = InstanceContainer { + live: true, + g, + }; // xxx record in GLOBAL.games - let amu = Arc::new(Mutex::new(inst)); - let mut ig = amu.lock().unwrap(); + let gref = InstanceRef(Arc::new(Mutex::new(cont))); + let mut g = gref.lock().unwrap(); for (token, _) in &al.tokens_players { - ig.tokens_players.tr.insert(RawToken(token.clone())); + g.tokens_players.tr.insert(RawToken(token.clone())); } let mut global = GLOBAL.players.write().unwrap(); for (token, player) in al.tokens_players.drain(0..) { let iad = InstanceAccessDetails { - g : amu.clone(), + gref : gref.clone(), ident : player, }; global.insert(RawToken(token), iad); } drop(global); - drop(ig); - amu + drop(g); + gref } } @@ -375,15 +402,15 @@ const XXX_PLAYERS_TOKENS : &[(&str, &str)] = &[ #[throws(OE)] pub fn xxx_global_setup() { let gs = xxx_gamestate_init(); - let ami = Instance::new(InstanceName { + let gref = Instance::new(InstanceName { scope: ManagementScope::XXX, scoped_name: "dummy".to_string() }, gs).expect("xxx create dummy"); - let mut ig = Instance::lock(&ami)?; + let mut g = gref.lock()?; for (token, nick) in XXX_PLAYERS_TOKENS { - let player = ig.player_new(PlayerState { + let player = g.player_new(PlayerState { nick : nick.to_string(), })?; - ig.player_access_register(RawToken(token.to_string()), player)?; + g.player_access_register(RawToken(token.to_string()), player)?; } } diff --git a/src/http.rs b/src/http.rs index 9a4efc5e..e9a85753 100644 --- a/src/http.rs +++ b/src/http.rs @@ -19,7 +19,7 @@ impl<'r> Responder<'r> for OnlineError { | ServerMessagePackEncodeFail(_) | ServerMessagePackDecodeFail(_) => Status::InternalServerError, - NoClient | NoPlayer => Status::NotFound, + NoClient | NoPlayer | GameBeingDestroyed => Status::NotFound, InvalidZCoord => Status::BadRequest, }; let mut resp = Responder::respond_to(msg,req).unwrap(); diff --git a/src/pieces.rs b/src/pieces.rs index 1ed2e366..b0ddddd4 100644 --- a/src/pieces.rs +++ b/src/pieces.rs @@ -14,7 +14,7 @@ struct SimpleShape { const SELECT_SCALE : f64 = 1.1; -#[derive(Copy,Clone,Debug,Error)] +#[derive(Copy,Clone,Debug,Error,Serialize,Deserialize)] pub enum SVGProcessingError { UnknownOperator, BadNumber, diff --git a/src/session.rs b/src/session.rs index a1ac516f..82ab3a5f 100644 --- a/src/session.rs +++ b/src/session.rs @@ -47,12 +47,12 @@ fn session(form : Json) -> Result { let iad = lookup_token(&form.ptoken)?; let player = iad.ident; let c = { - let mut ig = Instance::lock(&iad.g)?; + let mut ig = iad.gref.lock()?; let cl = Client { player }; let client = ig.clients.insert(cl); let ciad = InstanceAccessDetails { - g : iad.g.clone(), + gref : iad.gref.clone(), ident : client, }; let ctoken = record_token(&mut ig, ciad)?; diff --git a/src/sse.rs b/src/sse.rs index e345b048..6e4442de 100644 --- a/src/sse.rs +++ b/src/sse.rs @@ -22,7 +22,7 @@ struct UpdateReader { init_confirmation_send : iter::Once<()>, keepalives : Wrapping, to_send : UpdateId, - ami : Arc>, + gref : InstanceRef, } #[derive(Error,Debug)] @@ -34,7 +34,7 @@ impl Read for UpdateReader { let em : fn(&'static str) -> io::Error = |s| io::Error::new(io::ErrorKind::Other, anyhow!(s)); - let mut amig = self.ami.lock().map_err(|_| em("poison"))?; + let mut ig = self.gref.lock().map_err(|_| em("poison"))?; let orig_wanted = orig_buf.len(); let mut buf = orig_buf.as_mut(); @@ -44,7 +44,7 @@ impl Read for UpdateReader { self.player, self.client, self.to_send)?; } - let pu = &mut amig.updates.get(self.player) + let pu = &mut ig.updates.get(self.player) .ok_or_else(|| em("player gonee"))?; let cv = pu.cv.clone(); @@ -83,7 +83,7 @@ impl Read for UpdateReader { // xxx this endless stream is a leak // restart it occasionally - amig = cv.wait_timeout(amig, UPDATE_KEEPALIVE) + ig.c = cv.wait_timeout(ig.c, UPDATE_KEEPALIVE) .map_err(|_| em("poison"))?.0; write!(buf, "event: commsworking\n\ @@ -129,15 +129,15 @@ pub fn content(iad : InstanceAccessDetails, gen: Generation) let client = iad.ident; let content = { - let mut ig = iad.g.lock()?; - let _g = &mut ig.gs; - let cl = ig.clients.byid(client)?; + let mut g = iad.gref.lock()?; + let _g = &mut g.gs; + let cl = g.clients.byid(client)?; let player = cl.player; eprintln!("updates content iad={:?} player={:?} cl={:?} updates={:?}", - &iad, &player, &cl, &ig.updates); - let ami = iad.g.clone(); + &iad, &player, &cl, &g.updates); + let gref = iad.gref.clone(); - let log = &ig.updates.byid(player)?.log; + let log = &g.updates.byid(player)?.log; let to_send = match log.into_iter().rev() .find(|(_,update)| update.gen <= gen) { @@ -146,7 +146,7 @@ eprintln!("updates content iad={:?} player={:?} cl={:?} updates={:?}", }; UpdateReader { - player, client, to_send, ami, + player, client, to_send, gref, need_flush : false, keepalives : Wrapping(0), init_confirmation_send : iter::once(()), -- 2.30.2