// Copyright (c) ppy Pty Ltd . 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.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Graphics; namespace osu.Game.Database { /// /// A component which performs lookups (or calculations) and caches the results. /// Currently not persisted between game sessions. /// public abstract class MemoryCachingComponent : Component { private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); protected virtual bool CacheNullValues => true; /// /// Retrieve the cached value for the given lookup. /// /// The lookup to retrieve. /// An optional to cancel the operation. protected async Task GetAsync([NotNull] TLookup lookup, CancellationToken token = default) { if (CheckExists(lookup, out TValue performance)) return performance; var computed = await ComputeValueAsync(lookup, token).ConfigureAwait(false); if (computed != null || CacheNullValues) cache[lookup] = computed; return computed; } protected void Invalidate(Func invalidationFunction) { foreach (var kvp in cache) { if (invalidationFunction(kvp.Key)) cache.TryRemove(kvp.Key, out _); } } protected bool CheckExists([NotNull] TLookup lookup, out TValue value) => cache.TryGetValue(lookup, out value); /// /// Called on cache miss to compute the value for the specified lookup. /// /// The lookup to retrieve. /// An optional to cancel the operation. /// The computed value. protected abstract Task ComputeValueAsync(TLookup lookup, CancellationToken token = default); } }