From 03dd308449dd643edecf0d8f602d7a049bdfbfb9 Mon Sep 17 00:00:00 2001 From: jheaps Date: Fri, 3 Oct 2025 11:24:02 -0600 Subject: [PATCH] game removal code cleanup --- JoshHeaps.Net/Controllers/ChessController.cs | 70 ++++++++++---------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/JoshHeaps.Net/Controllers/ChessController.cs b/JoshHeaps.Net/Controllers/ChessController.cs index e567298..157cdd7 100644 --- a/JoshHeaps.Net/Controllers/ChessController.cs +++ b/JoshHeaps.Net/Controllers/ChessController.cs @@ -18,8 +18,12 @@ public class ChessController( /// Store of ongoing games. /// private static readonly ConcurrentDictionary _games = []; + private static readonly ConcurrentDictionary _gameRemovalTasks = []; + private static readonly ConcurrentDictionary _gameRemovalCancellationTokens = []; - private static ConcurrentDictionary _gameRemovalTasks = []; + private static readonly TimeSpan _computerGameTimeout = TimeSpan.FromHours(1); + private static readonly TimeSpan _multiplayerGameTimeout = TimeSpan.FromSeconds(5); + private static readonly TimeSpan _gameCleanupTimeout = TimeSpan.FromMinutes(1); /// /// Create a new chess game and store it in-memory. @@ -57,7 +61,7 @@ public class ChessController( }); } - ScheduleRemoveGame(gameState.GameId, TimeSpan.FromHours(1)); + ScheduleRemoveGame(gameState.GameId, _computerGameTimeout); return Ok(new { @@ -99,7 +103,7 @@ public class ChessController( isWhite = false; } - ScheduleRemoveGame(gameState.GameId, TimeSpan.FromDays(1)); + ScheduleRemoveGame(gameState.GameId, _multiplayerGameTimeout); return Ok(new { @@ -178,18 +182,11 @@ public class ChessController( return BadRequest(result); if (result.IsCheckmate || result.IsStalemate) - { - // queue game removal - ScheduleRemoveGame(gameState.GameId, TimeSpan.FromMinutes(1)); - } + ScheduleRemoveGame(gameState.GameId, _gameCleanupTimeout); + else if (gameState.IsVsComputer) + ScheduleRemoveGame(gameState.GameId, _computerGameTimeout); else - { - // increase timeout if play continues. - if (gameState.IsVsComputer) - ScheduleRemoveGame(gameState.GameId, TimeSpan.FromHours(1)); - else - ScheduleRemoveGame(gameState.GameId, TimeSpan.FromDays(1)); - } + ScheduleRemoveGame(gameState.GameId, _multiplayerGameTimeout); if (gameState.IsVsComputer && gameState.Computer is not null) queue.Queue(() => gameState.Computer.MakeMove(gameState, chessHub, chessService)); @@ -232,31 +229,36 @@ public class ChessController( private static void ScheduleRemoveGame(Guid id, TimeSpan delay) { - if (_gameRemovalTasks.ContainsKey(id)) + if (_gameRemovalCancellationTokens.TryRemove(id, out var oldCts)) { - _gameRemovalTasks[id] = Task.Run(async () => - { - await Task.Delay(delay); - - if (_games[id].Computer is not null) - await _games[id].Computer!.DisposeAsync(); - - _games.Remove(id, out _); - _gameRemovalTasks.Remove(id, out _); - }); - - return; + oldCts.Cancel(); + oldCts.Dispose(); } - _gameRemovalTasks.TryAdd(id, Task.Run(async () => + var cts = new CancellationTokenSource(); + _gameRemovalCancellationTokens[id] = cts; + + _gameRemovalTasks[id] = Task.Run(async () => { - await Task.Delay(delay); + try + { + await Task.Delay(delay, cts.Token); - if (_games[id].Computer is not null) - await _games[id].Computer!.DisposeAsync(); + if (_games.TryGetValue(id, out var game) && game.Computer is not null) + await game.Computer.DisposeAsync(); - _games.Remove(id, out _); - _gameRemovalTasks.Remove(id, out _); - })); + _games.Remove(id, out _); + } + catch (OperationCanceledException) { } + finally + { + if (_gameRemovalCancellationTokens.TryGetValue(id, out var currentCts) && currentCts == cts) + { + _gameRemovalCancellationTokens.TryRemove(id, out _); + } + + cts.Dispose(); + } + }); } }