MonoGame Game Loop and .NET CancellationToken Frame Interruption
0 reputation · 11 Nov 2020, 16:10 UTC
0 reputation · 11 Nov 2020, 16:10 UTC
I need to cancel an in-progress MonoGame frame when a background operation’s CancellationToken is triggered, without leaving the graphics device in an inconsistent state.
MonoGame’s Game runs Update and Draw synchronously on the main thread, and GameTime does not expose a cancellation token. Game.Exit() only stops after the current frame finishes. Throwing OperationCanceledException inside Update crashes the process because MonoGame does not catch user exceptions. Accessing GraphicsDevice from a background thread can cause backend-specific device errors. Behavior may differ between MonoGame 3.8 and later builds.
Update or Draw frame without throwing inside the game loop?34775 reputation · 12 Nov 2020, 01:51 UTC
No. A CancellationToken cannot safely interrupt a running MonoGame Update or Draw frame by throwing from another thread. MonoGame calls both methods synchronously on the main game thread, does not wrap user code in try/catch, and GameTime exposes no cancellation token. Throwing OperationCanceledException inside Update or Draw escapes Run() and crashes the process. The only safe method is cooperative cancellation: poll the token on the game thread, abandon the current frame’s work, and return normally.
Confirmed: MonoGame’s game loop invokes Update and Draw synchronously on the main thread. Game.Exit() only stops the loop after the current frame finishes. GraphicsDevice must be accessed from the main game thread; background-thread access can cause backend-specific device errors or inconsistent device state.
Likely explanation: CancellationToken is cooperative, not preemptive. Because MonoGame does not catch exceptions from user code, the main thread cannot be forced to stop mid-frame unless the code running on that thread checks the token and chooses to stop.
CancellationTokenSource between the background operation and the Game class.volatile bool _cancelRequested field to the game class._cts.Cancel() and set _cancelRequested = true. Do not access GraphicsDevice from this thread.Update, check _cts.Token.IsCancellationRequested. If true, set the flag, skip simulation work, and return.Draw, check the same flag. If true, skip rendering or draw a safe fallback, then return normally.No public frame-level cancellation hook is known in MonoGame 3.8 or later builds. Treat this as version-dependent: verify the exact NuGet package and target platform before assuming a hook exists.
protected override void Update(GameTime gameTime)
{
System.Diagnostics.Debug.WriteLine("$"Update thread id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
// normal update logic
}
protected override void Draw(GameTime gameTime)
{
System.Diagnostics.Debug.WriteLine("$"Draw thread id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
// normal draw logic
}
Compare these IDs with the background thread that calls Cancel(). If they differ, keep all GraphicsDevice calls on the main thread only.
What is your exact MonoGame NuGet version and target platform? A platform-specific or pre-release build could expose behavior not present in the public 3.8 API, which would change whether a frame-level hook is available.
Use comments to ask for clarification. Post a solution as an answer.
6,260 reputation · 12 Nov 2020, 02:22 UTC
MonoGame runs Update and Draw synchronously on the main thread, so a token cannot pre‑empt the current frame. The recommended pattern is to register a callback on the CancellationToken that flips a volatile bool flag and, if desired, enqueues a result or posts a message to the main thread. Then, at the very beginning of Update, test the flag, skip heavy work, and optionally call Game.Exit() to finish after the frame.
All GPU work must remain on the main thread; any background thread should only signal or queue data.
cts.Token.Register(() => cancelRequested = true);