mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 08:32:57 +08:00
Merge pull request #23844 from peppy/memory-caching-nrt
Apply NRT to `MemoryCachingComponent` classes
This commit is contained in:
commit
659a042065
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@ -24,8 +22,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
private readonly APIUser streamingUser = new APIUser { Id = 2, Username = "Test user" };
|
||||
|
||||
private TestSpectatorClient spectatorClient;
|
||||
private CurrentlyPlayingDisplay currentlyPlaying;
|
||||
private TestSpectatorClient spectatorClient = null!;
|
||||
private CurrentlyPlayingDisplay currentlyPlaying = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
@ -88,13 +86,13 @@ namespace osu.Game.Tests.Visual.Online
|
||||
"pishifat"
|
||||
};
|
||||
|
||||
protected override Task<APIUser> ComputeValueAsync(int lookup, CancellationToken token = default)
|
||||
protected override Task<APIUser?> ComputeValueAsync(int lookup, CancellationToken token = default)
|
||||
{
|
||||
// tests against failed lookups
|
||||
if (lookup == 13)
|
||||
return Task.FromResult<APIUser>(null);
|
||||
return Task.FromResult<APIUser?>(null);
|
||||
|
||||
return Task.FromResult(new APIUser
|
||||
return Task.FromResult<APIUser?>(new APIUser
|
||||
{
|
||||
Id = lookup,
|
||||
Username = usernames[lookup % usernames.Length],
|
||||
|
@ -1,10 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -24,10 +23,10 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
public partial class TestSceneBeatmapMetadataDisplay : OsuTestScene
|
||||
{
|
||||
private BeatmapMetadataDisplay display;
|
||||
private BeatmapMetadataDisplay display = null!;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager manager { get; set; }
|
||||
private BeatmapManager manager { get; set; } = null!;
|
||||
|
||||
[Cached(typeof(BeatmapDifficultyCache))]
|
||||
private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache();
|
||||
@ -121,7 +120,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
private partial class TestBeatmapDifficultyCache : BeatmapDifficultyCache
|
||||
{
|
||||
private TaskCompletionSource<bool> calculationBlocker;
|
||||
private TaskCompletionSource<bool>? calculationBlocker;
|
||||
|
||||
private bool blockCalculation;
|
||||
|
||||
@ -142,10 +141,13 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<StarDifficulty?> GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo rulesetInfo = null, IEnumerable<Mod> mods = null, CancellationToken cancellationToken = default)
|
||||
public override async Task<StarDifficulty?> GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, IEnumerable<Mod>? mods = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (blockCalculation)
|
||||
{
|
||||
Debug.Assert(calculationBlocker != null);
|
||||
await calculationBlocker.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
@ -21,8 +18,7 @@ namespace osu.Game.Database
|
||||
/// <param name="beatmapId">The beatmap to lookup.</param>
|
||||
/// <param name="token">An optional cancellation token.</param>
|
||||
/// <returns>The populated beatmap, or null if the beatmap does not exist or the request could not be satisfied.</returns>
|
||||
[ItemCanBeNull]
|
||||
public Task<APIBeatmap> GetBeatmapAsync(int beatmapId, CancellationToken token = default) => LookupAsync(beatmapId, token);
|
||||
public Task<APIBeatmap?> GetBeatmapAsync(int beatmapId, CancellationToken token = default) => LookupAsync(beatmapId, token);
|
||||
|
||||
/// <summary>
|
||||
/// Perform an API lookup on the specified beatmaps, populating a <see cref="APIBeatmap"/> model.
|
||||
@ -30,10 +26,10 @@ namespace osu.Game.Database
|
||||
/// <param name="beatmapIds">The beatmaps to lookup.</param>
|
||||
/// <param name="token">An optional cancellation token.</param>
|
||||
/// <returns>The populated beatmaps. May include null results for failed retrievals.</returns>
|
||||
public Task<APIBeatmap[]> GetBeatmapsAsync(int[] beatmapIds, CancellationToken token = default) => LookupAsync(beatmapIds, token);
|
||||
public Task<APIBeatmap?[]> GetBeatmapsAsync(int[] beatmapIds, CancellationToken token = default) => LookupAsync(beatmapIds, token);
|
||||
|
||||
protected override GetBeatmapsRequest CreateRequest(IEnumerable<int> ids) => new GetBeatmapsRequest(ids.ToArray());
|
||||
|
||||
protected override IEnumerable<APIBeatmap> RetrieveResults(GetBeatmapsRequest request) => request.Response?.Beatmaps;
|
||||
protected override IEnumerable<APIBeatmap>? RetrieveResults(GetBeatmapsRequest request) => request.Response?.Beatmaps;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Statistics;
|
||||
@ -19,8 +17,9 @@ namespace osu.Game.Database
|
||||
/// Currently not persisted between game sessions.
|
||||
/// </summary>
|
||||
public abstract partial class MemoryCachingComponent<TLookup, TValue> : Component
|
||||
where TLookup : notnull
|
||||
{
|
||||
private readonly ConcurrentDictionary<TLookup, TValue> cache = new ConcurrentDictionary<TLookup, TValue>();
|
||||
private readonly ConcurrentDictionary<TLookup, TValue?> cache = new ConcurrentDictionary<TLookup, TValue?>();
|
||||
|
||||
private readonly GlobalStatistic<MemoryCachingStatistics> statistics;
|
||||
|
||||
@ -37,12 +36,12 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
/// <param name="lookup">The lookup to retrieve.</param>
|
||||
/// <param name="token">An optional <see cref="CancellationToken"/> to cancel the operation.</param>
|
||||
protected async Task<TValue> GetAsync([NotNull] TLookup lookup, CancellationToken token = default)
|
||||
protected async Task<TValue?> GetAsync(TLookup lookup, CancellationToken token = default)
|
||||
{
|
||||
if (CheckExists(lookup, out TValue performance))
|
||||
if (CheckExists(lookup, out TValue? existing))
|
||||
{
|
||||
statistics.Value.HitCount++;
|
||||
return performance;
|
||||
return existing;
|
||||
}
|
||||
|
||||
var computed = await ComputeValueAsync(lookup, token).ConfigureAwait(false);
|
||||
@ -73,7 +72,7 @@ namespace osu.Game.Database
|
||||
statistics.Value.Usage = cache.Count;
|
||||
}
|
||||
|
||||
protected bool CheckExists([NotNull] TLookup lookup, out TValue value) =>
|
||||
protected bool CheckExists(TLookup lookup, [MaybeNullWhen(false)] out TValue value) =>
|
||||
cache.TryGetValue(lookup, out value);
|
||||
|
||||
/// <summary>
|
||||
@ -82,7 +81,7 @@ namespace osu.Game.Database
|
||||
/// <param name="lookup">The lookup to retrieve.</param>
|
||||
/// <param name="token">An optional <see cref="CancellationToken"/> to cancel the operation.</param>
|
||||
/// <returns>The computed value.</returns>
|
||||
protected abstract Task<TValue> ComputeValueAsync(TLookup lookup, CancellationToken token = default);
|
||||
protected abstract Task<TValue?> ComputeValueAsync(TLookup lookup, CancellationToken token = default);
|
||||
|
||||
private class MemoryCachingStatistics
|
||||
{
|
||||
|
@ -1,14 +1,11 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
@ -21,7 +18,7 @@ namespace osu.Game.Database
|
||||
where TRequest : APIRequest
|
||||
{
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="APIRequest"/> to retrieve the values for a given collection of <typeparamref name="TLookup"/>s.
|
||||
@ -32,8 +29,7 @@ namespace osu.Game.Database
|
||||
/// <summary>
|
||||
/// Retrieves a list of <typeparamref name="TValue"/>s from a successful <typeparamref name="TRequest"/> created by <see cref="CreateRequest"/>.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
protected abstract IEnumerable<TValue> RetrieveResults(TRequest request);
|
||||
protected abstract IEnumerable<TValue>? RetrieveResults(TRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Perform a lookup using the specified <paramref name="id"/>, populating a <typeparamref name="TValue"/>.
|
||||
@ -41,8 +37,7 @@ namespace osu.Game.Database
|
||||
/// <param name="id">The ID to lookup.</param>
|
||||
/// <param name="token">An optional cancellation token.</param>
|
||||
/// <returns>The populated <typeparamref name="TValue"/>, or null if the value does not exist or the request could not be satisfied.</returns>
|
||||
[ItemCanBeNull]
|
||||
protected Task<TValue> LookupAsync(TLookup id, CancellationToken token = default) => GetAsync(id, token);
|
||||
protected Task<TValue?> LookupAsync(TLookup id, CancellationToken token = default) => GetAsync(id, token);
|
||||
|
||||
/// <summary>
|
||||
/// Perform an API lookup on the specified <paramref name="ids"/>, populating a <typeparamref name="TValue"/>.
|
||||
@ -50,9 +45,9 @@ namespace osu.Game.Database
|
||||
/// <param name="ids">The IDs to lookup.</param>
|
||||
/// <param name="token">An optional cancellation token.</param>
|
||||
/// <returns>The populated values. May include null results for failed retrievals.</returns>
|
||||
protected Task<TValue[]> LookupAsync(TLookup[] ids, CancellationToken token = default)
|
||||
protected Task<TValue?[]> LookupAsync(TLookup[] ids, CancellationToken token = default)
|
||||
{
|
||||
var lookupTasks = new List<Task<TValue>>();
|
||||
var lookupTasks = new List<Task<TValue?>>();
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
@ -69,18 +64,18 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
// cannot be sealed due to test usages (see TestUserLookupCache).
|
||||
protected override async Task<TValue> ComputeValueAsync(TLookup lookup, CancellationToken token = default)
|
||||
protected override async Task<TValue?> ComputeValueAsync(TLookup lookup, CancellationToken token = default)
|
||||
=> await queryValue(lookup).ConfigureAwait(false);
|
||||
|
||||
private readonly Queue<(TLookup id, TaskCompletionSource<TValue>)> pendingTasks = new Queue<(TLookup, TaskCompletionSource<TValue>)>();
|
||||
private Task pendingRequestTask;
|
||||
private readonly Queue<(TLookup id, TaskCompletionSource<TValue?>)> pendingTasks = new Queue<(TLookup, TaskCompletionSource<TValue?>)>();
|
||||
private Task? pendingRequestTask;
|
||||
private readonly object taskAssignmentLock = new object();
|
||||
|
||||
private Task<TValue> queryValue(TLookup id)
|
||||
private Task<TValue?> queryValue(TLookup id)
|
||||
{
|
||||
lock (taskAssignmentLock)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<TValue>();
|
||||
var tcs = new TaskCompletionSource<TValue?>();
|
||||
|
||||
// Add to the queue.
|
||||
pendingTasks.Enqueue((id, tcs));
|
||||
@ -96,14 +91,14 @@ namespace osu.Game.Database
|
||||
private async Task performLookup()
|
||||
{
|
||||
// contains at most 50 unique IDs from tasks, which is used to perform the lookup.
|
||||
var nextTaskBatch = new Dictionary<TLookup, List<TaskCompletionSource<TValue>>>();
|
||||
var nextTaskBatch = new Dictionary<TLookup, List<TaskCompletionSource<TValue?>>>();
|
||||
|
||||
// Grab at most 50 unique IDs from the queue.
|
||||
lock (taskAssignmentLock)
|
||||
{
|
||||
while (pendingTasks.Count > 0 && nextTaskBatch.Count < 50)
|
||||
{
|
||||
(TLookup id, TaskCompletionSource<TValue> task) next = pendingTasks.Dequeue();
|
||||
(TLookup id, TaskCompletionSource<TValue?> task) next = pendingTasks.Dequeue();
|
||||
|
||||
// Perform a secondary check for existence, in case the value was queried in a previous batch.
|
||||
if (CheckExists(next.id, out var existing))
|
||||
@ -113,7 +108,7 @@ namespace osu.Game.Database
|
||||
if (nextTaskBatch.TryGetValue(next.id, out var tasks))
|
||||
tasks.Add(next.task);
|
||||
else
|
||||
nextTaskBatch[next.id] = new List<TaskCompletionSource<TValue>> { next.task };
|
||||
nextTaskBatch[next.id] = new List<TaskCompletionSource<TValue?>> { next.task };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
@ -21,8 +18,7 @@ namespace osu.Game.Database
|
||||
/// <param name="userId">The user to lookup.</param>
|
||||
/// <param name="token">An optional cancellation token.</param>
|
||||
/// <returns>The populated user, or null if the user does not exist or the request could not be satisfied.</returns>
|
||||
[ItemCanBeNull]
|
||||
public Task<APIUser> GetUserAsync(int userId, CancellationToken token = default) => LookupAsync(userId, token);
|
||||
public Task<APIUser?> GetUserAsync(int userId, CancellationToken token = default) => LookupAsync(userId, token);
|
||||
|
||||
/// <summary>
|
||||
/// Perform an API lookup on the specified users, populating a <see cref="APIUser"/> model.
|
||||
@ -30,10 +26,10 @@ namespace osu.Game.Database
|
||||
/// <param name="userIds">The users to lookup.</param>
|
||||
/// <param name="token">An optional cancellation token.</param>
|
||||
/// <returns>The populated users. May include null results for failed retrievals.</returns>
|
||||
public Task<APIUser[]> GetUsersAsync(int[] userIds, CancellationToken token = default) => LookupAsync(userIds, token);
|
||||
public Task<APIUser?[]> GetUsersAsync(int[] userIds, CancellationToken token = default) => LookupAsync(userIds, token);
|
||||
|
||||
protected override GetUsersRequest CreateRequest(IEnumerable<int> ids) => new GetUsersRequest(ids.ToArray());
|
||||
|
||||
protected override IEnumerable<APIUser> RetrieveResults(GetUsersRequest request) => request.Response?.Users;
|
||||
protected override IEnumerable<APIUser>? RetrieveResults(GetUsersRequest request) => request.Response?.Users;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Online.Rooms
|
||||
{
|
||||
var beatmap = task.GetResultSafely();
|
||||
|
||||
if (SelectedItem.Value?.Beatmap.OnlineID == beatmap.OnlineID)
|
||||
if (beatmap != null && SelectedItem.Value?.Beatmap.OnlineID == beatmap.OnlineID)
|
||||
{
|
||||
selectedBeatmap = beatmap;
|
||||
beginTracking();
|
||||
|
@ -1,12 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
@ -21,7 +18,7 @@ namespace osu.Game.Scoring
|
||||
public partial class ScorePerformanceCache : MemoryCachingComponent<ScorePerformanceCache.PerformanceCacheLookup, PerformanceAttributes>
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; } = null!;
|
||||
|
||||
protected override bool CacheNullValues => false;
|
||||
|
||||
@ -30,10 +27,10 @@ namespace osu.Game.Scoring
|
||||
/// </summary>
|
||||
/// <param name="score">The score to do the calculation on. </param>
|
||||
/// <param name="token">An optional <see cref="CancellationToken"/> to cancel the operation.</param>
|
||||
public Task<PerformanceAttributes> CalculatePerformanceAsync([NotNull] ScoreInfo score, CancellationToken token = default) =>
|
||||
public Task<PerformanceAttributes?> CalculatePerformanceAsync(ScoreInfo score, CancellationToken token = default) =>
|
||||
GetAsync(new PerformanceCacheLookup(score), token);
|
||||
|
||||
protected override async Task<PerformanceAttributes> ComputeValueAsync(PerformanceCacheLookup lookup, CancellationToken token = default)
|
||||
protected override async Task<PerformanceAttributes?> ComputeValueAsync(PerformanceCacheLookup lookup, CancellationToken token = default)
|
||||
{
|
||||
var score = lookup.ScoreInfo;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Database;
|
||||
@ -18,12 +16,12 @@ namespace osu.Game.Tests.Visual
|
||||
/// </summary>
|
||||
public const int UNRESOLVED_USER_ID = -1;
|
||||
|
||||
protected override Task<APIUser> ComputeValueAsync(int lookup, CancellationToken token = default)
|
||||
protected override Task<APIUser?> ComputeValueAsync(int lookup, CancellationToken token = default)
|
||||
{
|
||||
if (lookup == UNRESOLVED_USER_ID)
|
||||
return Task.FromResult((APIUser)null);
|
||||
return Task.FromResult<APIUser?>(null);
|
||||
|
||||
return Task.FromResult(new APIUser
|
||||
return Task.FromResult<APIUser?>(new APIUser
|
||||
{
|
||||
Id = lookup,
|
||||
Username = $"User {lookup}"
|
||||
|
Loading…
Reference in New Issue
Block a user