Improve safety of ongoing operation tracker

Finishing an operation started via
`OngoingOperationTracker.BeginOperation()` was risky in cases where the
operation ended at a callback on another thread (which, in the case of
multiplayer, is *most* cases). In particular, if any consumer registered
a callback that mutates transforms when the operation ends, it would
result in crashes after the framework-side safety checks.

Rework `OngoingOperationTracker` into an always-present component
residing in the drawable hierarchy, and ensure that the
`operationInProgress` bindable is always updated on the update thread.
This way consumers don't have to add local schedules in multiple places.
This commit is contained in:
Bartłomiej Dach
2021-01-09 21:38:20 +01:00
parent c8d83a9fb3
commit 8c3955d341
5 changed files with 19 additions and 11 deletions

View File

@ -4,6 +4,7 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
namespace osu.Game.Screens.OnlinePlay
{
@ -11,7 +12,7 @@ namespace osu.Game.Screens.OnlinePlay
/// Utility class to track ongoing online operations' progress.
/// Can be used to disable interactivity while waiting for a response from online sources.
/// </summary>
public class OngoingOperationTracker
public class OngoingOperationTracker : Component
{
/// <summary>
/// Whether there is an online operation in progress.
@ -22,6 +23,11 @@ namespace osu.Game.Screens.OnlinePlay
private LeasedBindable<bool> leasedInProgress;
public OngoingOperationTracker()
{
AlwaysPresent = true;
}
/// <summary>
/// Begins tracking a new online operation.
/// </summary>
@ -37,7 +43,8 @@ namespace osu.Game.Screens.OnlinePlay
leasedInProgress = inProgress.BeginLease(true);
leasedInProgress.Value = true;
return new InvokeOnDisposal(endOperation);
// for extra safety, marshal the end of operation back to the update thread if necessary.
return new InvokeOnDisposal(() => Scheduler.Add(endOperation, false));
}
private void endOperation()