Simplify flow of cancellation token

This commit is contained in:
Dean Herbert 2022-01-28 23:14:26 +09:00
parent 3d59bab7c6
commit daea13f491
3 changed files with 55 additions and 58 deletions

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -13,7 +14,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Threading;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -44,10 +44,9 @@ namespace osu.Game.Online.Leaderboards
private readonly LoadingSpinner loading; private readonly LoadingSpinner loading;
private CancellationTokenSource showScoresCancellationSource; private CancellationTokenSource currentFetchCancellationSource;
private APIRequest getScoresRequest; private APIRequest fetchScoresRequest;
private ScheduledDelegate getScoresRequestCallback;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
@ -62,7 +61,6 @@ namespace osu.Game.Online.Leaderboards
protected set protected set
{ {
scores = value; scores = value;
updateScoresDrawables(); updateScoresDrawables();
} }
} }
@ -177,9 +175,10 @@ namespace osu.Game.Online.Leaderboards
/// <summary> /// <summary>
/// Performs a fetch/refresh of scores to be displayed. /// Performs a fetch/refresh of scores to be displayed.
/// </summary> /// </summary>
/// <param name="cancellationToken"></param>
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns> /// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
[CanBeNull] [CanBeNull]
protected abstract APIRequest FetchScores(); protected abstract APIRequest FetchScores(CancellationToken cancellationToken);
protected abstract LeaderboardScore CreateDrawableScore(TScoreInfo model, int index); protected abstract LeaderboardScore CreateDrawableScore(TScoreInfo model, int index);
@ -187,37 +186,36 @@ namespace osu.Game.Online.Leaderboards
private void refetchScores() private void refetchScores()
{ {
cancelPendingWork(); Reset();
PlaceholderState = PlaceholderState.Retrieving; PlaceholderState = PlaceholderState.Retrieving;
loading.Show(); loading.Show();
getScoresRequest = FetchScores(); currentFetchCancellationSource = new CancellationTokenSource();
if (getScoresRequest == null) fetchScoresRequest = FetchScores(currentFetchCancellationSource.Token);
if (fetchScoresRequest == null)
return; return;
getScoresRequest.Failure += e => getScoresRequestCallback = Schedule(() => fetchScoresRequest.Failure += e => Schedule(() =>
{ {
if (e is OperationCanceledException) if (e is OperationCanceledException || currentFetchCancellationSource.IsCancellationRequested)
return; return;
PlaceholderState = PlaceholderState.NetworkFailure; PlaceholderState = PlaceholderState.NetworkFailure;
}); });
api?.Queue(getScoresRequest); api?.Queue(fetchScoresRequest);
} }
private void cancelPendingWork() private void cancelPendingWork()
{ {
showScoresCancellationSource?.Cancel(); currentFetchCancellationSource?.Cancel();
showScoresCancellationSource = null; currentFetchCancellationSource = null;
getScoresRequest?.Cancel(); fetchScoresRequest?.Cancel();
getScoresRequest = null; fetchScoresRequest = null;
getScoresRequestCallback?.Cancel();
getScoresRequestCallback = null;
} }
#region Placeholder handling #region Placeholder handling
@ -279,8 +277,6 @@ namespace osu.Game.Online.Leaderboards
scrollFlow?.FadeOut(fade_duration, Easing.OutQuint).Expire(); scrollFlow?.FadeOut(fade_duration, Easing.OutQuint).Expire();
scrollFlow = null; scrollFlow = null;
showScoresCancellationSource?.Cancel();
if (scores?.Any() != true) if (scores?.Any() != true)
{ {
loading.Hide(); loading.Hide();
@ -288,6 +284,8 @@ namespace osu.Game.Online.Leaderboards
return; return;
} }
Debug.Assert(!currentFetchCancellationSource.IsCancellationRequested);
// ensure placeholder is hidden when displaying scores // ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful; PlaceholderState = PlaceholderState.Successful;
@ -315,7 +313,7 @@ namespace osu.Game.Online.Leaderboards
scrollContainer.ScrollToStart(false); scrollContainer.ScrollToStart(false);
loading.Hide(); loading.Hide();
}, (showScoresCancellationSource = new CancellationTokenSource()).Token); }, currentFetchCancellationSource.Token);
}, false); }, false);
private void replacePlaceholder(Placeholder placeholder) private void replacePlaceholder(Placeholder placeholder)

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Threading;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Online.API; using osu.Game.Online.API;
@ -30,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
protected override bool IsOnlineScope => true; protected override bool IsOnlineScope => true;
protected override APIRequest FetchScores() protected override APIRequest FetchScores(CancellationToken cancellationToken)
{ {
if (roomId.Value == null) if (roomId.Value == null)
return null; return null;
@ -39,6 +40,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
req.Success += r => req.Success += r =>
{ {
if (cancellationToken.IsCancellationRequested)
return;
Scores = r.Leaderboard; Scores = r.Leaderboard;
TopScore = r.UserScore; TopScore = r.UserScore;
}; };

View File

@ -108,13 +108,8 @@ namespace osu.Game.Screens.Select.Leaderboards
protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local;
private CancellationTokenSource loadCancellationSource; protected override APIRequest FetchScores(CancellationToken cancellationToken)
protected override APIRequest FetchScores()
{ {
loadCancellationSource?.Cancel();
loadCancellationSource = new CancellationTokenSource();
var fetchBeatmapInfo = BeatmapInfo; var fetchBeatmapInfo = BeatmapInfo;
if (fetchBeatmapInfo == null) if (fetchBeatmapInfo == null)
@ -125,7 +120,7 @@ namespace osu.Game.Screens.Select.Leaderboards
if (Scope == BeatmapLeaderboardScope.Local) if (Scope == BeatmapLeaderboardScope.Local)
{ {
subscribeToLocalScores(); subscribeToLocalScores(cancellationToken);
return null; return null;
} }
@ -160,10 +155,10 @@ namespace osu.Game.Screens.Select.Leaderboards
req.Success += r => req.Success += r =>
{ {
scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets, fetchBeatmapInfo)).ToArray(), loadCancellationSource.Token) scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets, fetchBeatmapInfo)).ToArray(), cancellationToken)
.ContinueWith(task => Schedule(() => .ContinueWith(task => Schedule(() =>
{ {
if (loadCancellationSource.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return;
Scores = task.GetResultSafely(); Scores = task.GetResultSafely();
@ -184,7 +179,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Action = () => ScoreSelected?.Invoke(model) Action = () => ScoreSelected?.Invoke(model)
}; };
private void subscribeToLocalScores() private void subscribeToLocalScores(CancellationToken cancellationToken)
{ {
scoreSubscription?.Dispose(); scoreSubscription?.Dispose();
scoreSubscription = null; scoreSubscription = null;
@ -197,35 +192,35 @@ namespace osu.Game.Screens.Select.Leaderboards
+ $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" + $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1"
+ $" AND {nameof(ScoreInfo.DeletePending)} == false" + $" AND {nameof(ScoreInfo.DeletePending)} == false"
, beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); , beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged);
}
private void localScoresChanged(IRealmCollection<ScoreInfo> sender, ChangeSet changes, Exception exception) void localScoresChanged(IRealmCollection<ScoreInfo> sender, ChangeSet changes, Exception exception)
{
if (IsOnlineScope)
return;
var scores = sender.AsEnumerable();
if (filterMods && !mods.Value.Any())
{ {
// we need to filter out all scores that have any mods to get all local nomod scores if (IsOnlineScope || cancellationToken.IsCancellationRequested)
scores = scores.Where(s => !s.Mods.Any()); return;
}
else if (filterMods)
{
// otherwise find all the scores that have *any* of the currently selected mods (similar to how web applies mod filters)
// we're creating and using a string list representation of selected mods so that it can be translated into the DB query itself
var selectedMods = mods.Value.Select(m => m.Acronym);
scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym)));
}
scores = scores.Detach(); var scores = sender.AsEnumerable();
scoreManager.OrderByTotalScoreAsync(scores.ToArray(), loadCancellationSource.Token) if (filterMods && !mods.Value.Any())
.ContinueWith(ordered => {
{ // we need to filter out all scores that have any mods to get all local nomod scores
Scores = ordered.GetResultSafely(); scores = scores.Where(s => !s.Mods.Any());
}, TaskContinuationOptions.OnlyOnRanToCompletion); }
else if (filterMods)
{
// otherwise find all the scores that have *any* of the currently selected mods (similar to how web applies mod filters)
// we're creating and using a string list representation of selected mods so that it can be translated into the DB query itself
var selectedMods = mods.Value.Select(m => m.Acronym);
scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym)));
}
scores = scores.Detach();
scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken)
.ContinueWith(ordered =>
{
Scores = ordered.GetResultSafely();
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)