From 8b45372f660066a1fd90ffd8b0f372ed59de15f2 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 22 Jan 2024 12:06:14 -0600 Subject: [PATCH 1/2] Use FastCache.Cached as new image cache instead of implement it ourselves --- .../Flow.Launcher.Infrastructure.csproj | 1 + .../Image/ImageCache.cs | 78 +++++++------------ .../Image/ImageLoader.cs | 11 ++- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index b24f069c1ba..574f3686a6d 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,6 +49,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 7a2b5763756..55545b9a732 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -4,13 +4,13 @@ using System.Linq; using System.Threading; using System.Windows.Media; +using FastCache; +using FastCache.Services; namespace Flow.Launcher.Infrastructure.Image { - [Serializable] public class ImageUsage { - public int usage; public ImageSource imageSource; @@ -23,16 +23,13 @@ public ImageUsage(int usage, ImageSource image) public class ImageCache { - private const int MaxCached = 50; - public ConcurrentDictionary<(string, bool), ImageUsage> Data { get; } = new(); - private const int permissibleFactor = 2; - private SemaphoreSlim semaphore = new(1, 1); + private const int MaxCached = 150; public void Initialize(Dictionary<(string, bool), int> usage) { foreach (var key in usage.Keys) { - Data[key] = new ImageUsage(usage[key], null); + Cached.Save(key, new ImageUsage(usage[key], null), TimeSpan.MaxValue, MaxCached); } } @@ -40,70 +37,48 @@ public void Initialize(Dictionary<(string, bool), int> usage) { get { - if (!Data.TryGetValue((path, isFullImage), out var value)) + if (!Cached.TryGet((path, isFullImage), out var value)) { return null; } - value.usage++; - return value.imageSource; + value.Value.usage++; + return value.Value.imageSource; } set { - Data.AddOrUpdate( - (path, isFullImage), - new ImageUsage(0, value), - (k, v) => - { - v.imageSource = value; - v.usage++; - return v; - } - ); - - SliceExtra(); - - async void SliceExtra() + if (Cached.TryGet((path, isFullImage), out var cached)) { - // To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size - // This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time - if (Data.Count > permissibleFactor * MaxCached) - { - await semaphore.WaitAsync().ConfigureAwait(false); - // To delete the images from the data dictionary based on the resizing of the Usage Dictionary - // Double Check to avoid concurrent remove - if (Data.Count > permissibleFactor * MaxCached) - foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key)) - Data.TryRemove(key, out _); - semaphore.Release(); - } + cached.Value.imageSource = value; + cached.Value.usage++; } + + Cached.Save((path, isFullImage), new ImageUsage(0, value), TimeSpan.MaxValue, + MaxCached); } } public bool ContainsKey(string key, bool isFullImage) { - return key is not null && Data.ContainsKey((key, isFullImage)) && Data[(key, isFullImage)].imageSource != null; + return Cached.TryGet((key, isFullImage), out _); } public bool TryGetValue(string key, bool isFullImage, out ImageSource image) { - if (key is not null) - { - bool hasKey = Data.TryGetValue((key, isFullImage), out var imageUsage); - image = hasKey ? imageUsage.imageSource : null; - return hasKey; - } - else + if (Cached.TryGet((key, isFullImage), out var value)) { - image = null; - return false; + image = value.Value.imageSource; + value.Value.usage++; + return image != null; } + + image = null; + return false; } public int CacheSize() { - return Data.Count; + return CacheManager.TotalCount<(string, bool), ImageUsage>(); } /// @@ -111,7 +86,14 @@ public int CacheSize() /// public int UniqueImagesInCache() { - return Data.Values.Select(x => x.imageSource).Distinct().Count(); + return CacheManager.EnumerateEntries<(string, bool), ImageUsage>().Select(x => x.Value.imageSource) + .Distinct() + .Count(); + } + + public IEnumerable> EnumerateEntries() + { + return CacheManager.EnumerateEntries<(string, bool), ImageUsage>(); } } } diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index add6d4e9237..75c2a4ec989 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -49,7 +49,7 @@ public static async Task InitializeAsync() { await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () => { - foreach (var ((path, isFullImage), _) in ImageCache.Data) + foreach (var ((path, isFullImage), _) in usage) { await LoadAsync(path, isFullImage); } @@ -65,7 +65,7 @@ public static async Task Save() try { - _storage.SaveAsync(ImageCache.Data + await _storage.SaveAsync(ImageCache.EnumerateEntries() .ToDictionary( x => x.Key, x => x.Value.usage)); @@ -125,9 +125,12 @@ private static async ValueTask LoadInternalAsync(string path, bool return new ImageResult(MissingImage, ImageType.Error); } - if (ImageCache.ContainsKey(path, loadFullImage)) + // extra scope for use of same variable name { - return new ImageResult(ImageCache[path, loadFullImage], ImageType.Cache); + if (ImageCache.TryGetValue(path, loadFullImage, out var imageSource)) + { + return new ImageResult(imageSource, ImageType.Cache); + } } if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var uriResult) From 267163c8adc61aa339223e046205b00a79694949 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 22 Mar 2024 22:08:18 +0800 Subject: [PATCH 2/2] Update terms --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 2d6fdb7f0aa..c4b1ee8494d 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -106,3 +106,4 @@ alreadyexists JsonRPC JsonRPCV2 Softpedia +img