Fixed‑time‑step vs. variable‑time‑step: which better supports graceful cancellation in MonoGame?
0 reputation · 10 Jul 2026, 22:57 UTC
0 reputation · 10 Jul 2026, 22:57 UTC
The goal is to ensure that when a cancellation token is triggered, the game loop stops heavy update work promptly while still allowing rendering to finish or exit cleanly.
MonoGame's Game class exposes IsFixedTimeStep and TargetElapsedTime for a deterministic loop, but it does not automatically inject a CancellationToken into Update or Draw, leaving the developer to poll the token manually. It is unclear whether the fixed‑time‑step’s built‑in step capping or the variable‑time‑step’s manual accumulator provides a clearer, lower‑overhead path to respect cancellation without missing draw calls.
Does fixing the time step simplify timeout detection because each Update call is bounded by TargetElapsedTime, making it easier to check a CancellationToken at the start without risking missed frames?
Does a variable‑time‑step approach, where developers accumulate elapsed time and cap each step, provide lower latency cancellation but require more careful token polling to avoid corrupting state?
Which strategy yields fewer chances of a deadlock or indefinite block when the token is signaled during a long‑running update, while still allowing Draw to be called unless the game exits?
35275 reputation · 11 Jul 2026, 02:52 UTC
When a CancellationToken is signaled, MonoGame will only stop the Update loop when it reaches a safe point inside Update or Draw. Neither time‑step mode automatically injects the token, so the developer must poll it. The choice of time‑step affects how quickly that polling can happen and how many Update calls may be queued.
IsFixedTimeStep = true) automatically calls Update multiple times per frame to catch up to TargetElapsedTime. Each call is bounded by the target time.IsFixedTimeStep = false) passes the actual elapsed time to Update. No additional Update calls are queued until the current call returns.Update or Draw or any blocking work called from them.Update and returning immediately if cancelled prevents further heavy work for that frame.Update performs blocking I/O or CPU‑bound work that cannot be interrupted, cancellation will not be detected until that work completes.Because each Update call in fixed‑time‑step mode is limited by TargetElapsedTime, a cancellation check at the beginning of Update is evaluated on a predictable cadence—roughly every 16 ms if the target is 60 Hz. This makes it easier to detect a cancellation before the next frame begins, and fewer frames are lost if the token is fired during a heavy frame.
In variable‑time‑step mode, the Update call receives the actual elapsed time. If that call takes longer than the frame budget, the next Update is delayed until the current one finishes, so the token check is only performed after the long work completes. The latency to react to cancellation is therefore bounded by the duration of the current Update.
In practice, the overhead of multiple Update calls per frame in fixed‑time‑step mode is usually negligible, but if you set an extremely small TargetElapsedTime (e.g., 1 ms) you may observe many rapid calls that could delay cancellation detection if a single call is blocked. Conversely, variable‑time‑step can be simpler to implement but gives no guarantee about how quickly cancellation will be noticed.
IsFixedTimeStep = true for deterministic physics or replay. IsFixedTimeStep = false if you want a single Update per frame and can tolerate longer cancellation latency.Update.
public override void Update(GameTime gameTime)
{
if (_cancellationToken.IsCancellationRequested)
{
// Skip heavy work and return immediately.
return;
}
// Normal update logic here.
}
Draw to skip rendering.
private bool _cancelRequested;
public override void Update(GameTime gameTime)
{
_cancelRequested = _cancellationToken.IsCancellationRequested;
if (_cancelRequested) return;
// ...heavy work
}
public override void Draw(GameTime gameTime)
{
if (_cancelRequested) return; // Skip drawing for this frame.
// Normal draw logic
}
Update calls synchronous I/O or long CPU loops, wrap them in Task.Run or use CancellationToken-aware APIs so the operation can be cancelled before Update returns.Update calls occur and that Draw continues until the next frame boundary.If your game is heavily I/O‑bound and you want to avoid the overhead of multiple Update calls per frame, variable‑time‑step may be acceptable. Just remember that cancellation will only be detected after the current Update finishes.
Does your Update method perform blocking I/O or CPU‑bound work that cannot be interrupted? If so, neither time‑step mode will interrupt that work; you must handle cancellation inside the blocking operation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.