chiark / gitweb /
compiles again
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 26 Jul 2020 19:11:25 +0000 (20:11 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 26 Jul 2020 19:11:25 +0000 (20:11 +0100)
src/api.rs
src/cmdlistener.rs
src/commands.rs
src/error.rs
src/global.rs
src/http.rs
src/pieces.rs
src/session.rs
src/sse.rs

index 8eafb80505b65b97f9cf9883c95bda9523c3ff2a..295a5e7b6901e28cc9b8b2fe0f7ca7e21ee7e909 100644 (file)
@@ -59,7 +59,7 @@ fn api_piece_op<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
 //  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
index 94716c9e06ba5f70eb00d2cea921557f918fad42..5a291d0852b38e94cc5dcdca89483da133e1f83a 100644 (file)
@@ -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<PieceId, PieceUpdateOp<()>>,
 }
 
+#[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<MgmtGameUpdate>,
-                    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<MgmtGameUpdate>,
+                    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<LogEntry>) {
+  use MgmtGameUpdate::*;
   match update {
+    Noop { } => (vec![], vec![]),
   }
 }
 
index 1439141119157b3677edc4c3e11098d61322863f..1f1b6d08a31c40e093f8073648f36941152a254f 100644 (file)
@@ -3,7 +3,7 @@ use crate::imports::*;
 
 #[derive(Debug,Serialize,Deserialize)]
 pub enum MgmtCommand {
-  Noop { },
+  Noop(),
   SetScope(ManagementScope),
   CreateGame(String, Vec<MgmtGameUpdate>),
 //  AddPiece(Box<dyn PieceSpec>),
@@ -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}
 
index b0b12b9cbab0187f27703ba1958a437c51799a31..e62837dfdcea86e6c47f2d56602779c101c805a2 100644 (file)
@@ -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<X> From<PoisonError<X>> for OnlineError {
-  fn from(_: PoisonError<X>) -> OnlineError { GameCorrupted }
+#[derive(Error,Debug)]
+pub enum InstanceLockError {
+  GameCorrupted,
+  GameBeingDestroyed,
+}
+#[macro_export]
+macro_rules! from_instance_lock_error {
+  ($into:ident) => {
+    impl From<InstanceLockError> for $into {
+      fn from(e: InstanceLockError) -> $into {
+        use InstanceLockError::*;
+        match e {
+          GameCorrupted      => $into::GameCorrupted,
+          GameBeingDestroyed => $into::GameBeingDestroyed,
+        }
+      }
+    }
+  }
 }
 
 pub trait ById {
index 9464ea4c2eeeadbb661d4493e583313470f20945..9bfdced9f864b25cb0912eae6163ac533a157aeb 100644 (file)
@@ -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<Mutex<Instance>>;
+
+#[derive(Debug,Clone)]
+pub struct InstanceRef (Arc<Mutex<InstanceContainer>>);
+
+#[derive(Debug)]
+pub struct InstanceContainer {
+  live : bool,
+  g : Instance,
+}
 
 #[derive(Debug)]
 pub struct Instance {
-  pub live : bool,
   pub name : Arc<InstanceName>,
   pub gs : GameState,
   pub clients : DenseSlotMap<ClientId,Client>,
@@ -48,8 +57,8 @@ pub struct Client {
 
 #[derive(Debug)]
 pub struct InstanceGuard<'g> {
-  ig : MutexGuard<'g,Instance>,
-  amu : Arc<Mutex<Instance>>,
+  pub c : MutexGuard<'g,InstanceContainer>,
+  pub gref : InstanceRef,
 }
 
 #[derive(Default)]
@@ -72,11 +81,16 @@ struct InstanceSaveAccesses<RawTokenStr> {
   tokens_players : Vec<(RawTokenStr, PlayerId)>,
 }
 
+display_as_debug!{InstanceLockError}
+impl<X> From<PoisonError<X>> for InstanceLockError {
+  fn from(_: PoisonError<X>) -> Self { Self::GameCorrupted }
+}
+
 // ---------- API ----------
 
 #[derive(Clone,Debug)]
 pub struct InstanceAccessDetails<Id> {
-  pub g : Arc<Mutex<Instance>>,
+  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<Mutex<Instance>>) -> 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>
   ) {
-    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<Mutex<Instance>> {
+  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)?;
   }
 }
index 9a4efc5e7f91b48c448f8a42fa8c7dcd470b4449..e9a85753a259ca157f2e22cb75a00807f78317b0 100644 (file)
@@ -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();
index 1ed2e3660d95ec8c29a6e17992c7fa7e237821da..b0ddddd4d7ca12ff933eb45efbad19463358b6de 100644 (file)
@@ -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,
index a1ac516fe711c954f265477e6fb10737f19a3bdb..82ab3a5f1d3193aaca94d9fe6e6ca52a4abfdb92 100644 (file)
@@ -47,12 +47,12 @@ fn session(form : Json<SessionForm>) -> Result<Template,OE> {
   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)?;
index e345b04807d0493e4cd8af81932fd24bf759824b..6e4442de99917dc4d78720b14567bef36734b420 100644 (file)
@@ -22,7 +22,7 @@ struct UpdateReader {
   init_confirmation_send : iter::Once<()>,
   keepalives : Wrapping<u32>,
   to_send : UpdateId,
-  ami : Arc<Mutex<Instance>>,
+  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<ClientId>, 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(()),