F: FnOnce(&mut AccountRecord, AccountId) -> T
>(
&mut self,
+ games: &mut GamesGuard,
key: K,
auth: Authorisation<AccountName>,
set_access: Option<AccessRecord>,
if let Some(new_access) = set_access {
process_all_players_for_account(
+ games,
acctid,
|ig, player| ig.invalidate_tokens(player)
)?;
#[throws(MgmtError)]
pub fn remove_entry(&mut self,
+ games: &mut GamesGuard,
account: &AccountName,
_auth: Authorisation<AccountName>)
{
then { (accounts, acctid) }
else { throw!(AccountNotFound) }
};
- process_all_players_for_account(acctid, |ig,player| {
+ process_all_players_for_account(games, acctid, |ig,player| {
ig.players_remove(&[player].iter().cloned().collect())?;
Ok::<_,ME>(())
})?;
UpdateAccont(AccountDetails { account, nick, timezone, access }) => {
let mut ag = AccountsGuard::lock();
+ let mut games = games_lock();
let auth = authorise_for_account(cs, &ag, &account)?;
let access = cs.accountrecord_from_spec(access)?;
- ag.with_entry_mut(&account, auth, access, |record, _acctid|{
+ ag.with_entry_mut(&mut games, &account, auth, access, |record, _acctid|{
fn update_from<T>(spec: Option<T>, record: &mut T) {
if let Some(new) = spec { *record = new; }
}
DeleteAccount(account) => {
let mut ag = AccountsGuard::lock();
+ let mut games = games_lock();
let auth = authorise_for_account(cs, &ag, &account)?;
- ag.remove_entry(&account, auth)?;
+ ag.remove_entry(&mut games, &account, auth)?;
Fine
}
CreateGame { game, insns } => {
let mut ag = AccountsGuard::lock();
+ let mut games = games_lock();
let auth = authorise_by_account(cs, &ag, &game)?;
let gs = crate::gamestate::GameState {
};
let acl = default();
- let gref = Instance::new(game, gs, acl, auth)?;
+ let gref = Instance::new(game, gs, &mut games, acl, auth)?;
let ig = gref.lock()?;
let mut ig = Unauthorised::of(ig);
let ig = ig.by(Authorisation::authorise_any());
let name = ig.name.clone();
let InstanceGuard { c, .. } = ig;
- Instance::destroy_game(c, auth)
+ Instance::destroy_game(&mut games, c, auth)
.unwrap_or_else(|e| warn!(
"failed to tidy up failecd creation of {:?}: {:?}",
&name, &e
DestroyGame { game } => {
let mut ag = AccountsGuard::lock();
+ let mut games = games_lock();
let auth = authorise_by_account(cs, &mut ag, &game)?;
let gref = Instance::lookup_by_name(&game, auth)?;
let ig = gref.lock_even_poisoned();
- Instance::destroy_game(ig, auth)?;
+ Instance::destroy_game(&mut games, ig, auth)?;
Fine
}
// slow global locks:
save_area_lock : Mutex<Option<File>>,
// <- accounts::accounts ->
- games : RwLock<HashMap<Arc<InstanceName>,InstanceRef>>,
+ games_table: RwLock<GamesTable>,
// per-game lock:
// <- InstanceContainer ->
clients : RwLock<TokenTable<ClientId>>,
}
+pub type GamesGuard = RwLockWriteGuard<'static, GamesTable>;
+pub type GamesTable = HashMap<Arc<InstanceName>,InstanceRef>;
+
#[derive(Debug)]
pub struct InstanceContainer {
live : bool,
#[allow(clippy::new_ret_no_self)]
#[throws(MgmtError)]
pub fn new(name: InstanceName, gs: GameState,
+ games: &mut GamesGuard,
acl: LoadedAcl<TablePermission>, _: Authorisation<InstanceName>)
-> InstanceRef {
let name = Arc::new(name);
let gref = InstanceRef(Arc::new(Mutex::new(cont)));
let mut ig = gref.lock()?;
- // We hold the GLOBAL.games lock while we save the new game.
- // That lock is not on the hot path.
- let mut games = GLOBAL.games.write().unwrap();
let entry = games.entry(name);
use hash_map::Entry::*;
-> Unauthorised<InstanceRef, InstanceName>
{
Unauthorised::of(
- GLOBAL.games.read().unwrap()
+ GLOBAL.games_table.read().unwrap()
.get(name)
.ok_or(MgmtError::GameNotFound)?
.clone()
}
#[throws(InternalError)]
- pub fn destroy_game(mut g: MutexGuard<InstanceContainer>,
+ pub fn destroy_game(games: &mut GamesGuard,
+ mut g: MutexGuard<InstanceContainer>,
_: Authorisation<InstanceName>) {
let a_savefile = savefilename(&g.g.name, "a-", "");
- let mut gw = GLOBAL.games.write().unwrap();
- // xxx lock order wrong, this function should unlock and then relock
let g_file = savefilename(&g.g.name, "g-", "");
fs::remove_file(&g_file).context("remove").context(g_file)?;
(||{ // Infallible:
g.live = false;
- gw.remove(&g.g.name);
+ games.remove(&g.g.name);
InstanceGuard::forget_all_tokens(&mut g.g.tokens_clients);
InstanceGuard::forget_all_tokens(&mut g.g.tokens_players);
})(); // <- No ?, ensures that IEFE is infallible (barring panics)
pub fn list_names(account: Option<&AccountName>,
_: Authorisation<AccountName>)
-> Vec<Arc<InstanceName>> {
- let games = GLOBAL.games.read().unwrap();
+ let games = GLOBAL.games_table.read().unwrap();
let out : Vec<Arc<InstanceName>> =
games.keys()
.filter(|k| account == None || account == Some(&k.account))
}
}
+pub fn games_lock() -> RwLockWriteGuard<'static, GamesTable> {
+ GLOBAL.games_table.write().unwrap()
+}
+
// ---------- Simple trait implementations ----------
impl Deref for InstanceGuard<'_> {
#[throws(StartupError)]
fn load_game(accounts: &AccountsGuard,
+ games: &mut GamesGuard,
name: InstanceName) -> Option<InstanceRef> {
{
let mut st = GLOBAL.save_area_lock.lock().unwrap();
} }
drop(global);
drop(g);
- GLOBAL.games.write().unwrap().insert(name.clone(), gref.clone());
+ games.insert(name.clone(), gref.clone());
info!("loadewd {:?}", &name);
Some(gref)
}
}
#[throws(anyhow::Error)]
-pub fn load_games(accounts: &mut AccountsGuard) {
+pub fn load_games(accounts: &mut AccountsGuard,
+ games: &mut GamesGuard) {
enum AFState { Found(PathBuf), Used };
use AFState::*;
use SavefilenameParseResult::*;
);
},
GameFile { access_leaf, name } => {
- InstanceGuard::load_game(accounts, name)?;
+ InstanceGuard::load_game(accounts, games, name)?;
a_leaves.insert(access_leaf, Used);
},
}
E: Error,
F: FnMut(&mut InstanceGuard<'_>, PlayerId) -> Result<(),E>
>
- (acctid: AccountId, mut f: F)
+ (games: &mut GamesGuard, acctid: AccountId, mut f: F)
{
- let games = GLOBAL.games.write().unwrap();
for gref in games.values() {
let c = gref.lock_even_poisoned();
let remove : Vec<_> = c.g.iplayers.iter().filter_map(|(player,pr)| {
fn old(&mut self, client: ClientId) -> Option<Self::Ret>;
}
- for gref in GLOBAL.games.read().unwrap().values() {
+ for gref in GLOBAL.games_table.read().unwrap().values() {
struct Any;
impl ClientIterator for Any {
type Ret = ();
let mut want_expire = vec![];
- let read = GLOBAL.games.read().unwrap();
+ let read = GLOBAL.games_table.read().unwrap();
for gref in read.values() {
if gref.lock_even_poisoned().g.gs.want_expire_some_logs(cutoff) {
want_expire.push(gref.clone())