// 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. using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Graphics; namespace osu.Game.Database { /// <summary> /// A component which performs lookups (or calculations) and caches the results. /// Currently not persisted between game sessions. /// </summary> public abstract class MemoryCachingComponent<TLookup, TValue> : Component { private readonly ConcurrentDictionary<TLookup, TValue> cache = new ConcurrentDictionary<TLookup, TValue>(); protected virtual bool CacheNullValues => true; /// <summary> /// Retrieve the cached value for the given lookup. /// </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) { 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 bool CheckExists([NotNull] TLookup lookup, out TValue value) => cache.TryGetValue(lookup, out value); /// <summary> /// Called on cache miss to compute the value for the specified lookup. /// </summary> /// <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); } }