Skip to content

Commit 3afc521

Browse files
committed
Add non-async version load in BinaryStorage & Use non-async version for ImageLoader
1 parent 7b08c96 commit 3afc521

File tree

4 files changed

+87
-61
lines changed

4 files changed

+87
-61
lines changed

Flow.Launcher.Infrastructure/Image/ImageLoader.cs

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static class ImageLoader
1919
private static readonly string ClassName = nameof(ImageLoader);
2020

2121
private static readonly ImageCache ImageCache = new();
22-
private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1);
22+
private static Lock storageLock { get; } = new();
2323
private static BinaryStorage<List<(string, bool)>> _storage;
2424
private static readonly ConcurrentDictionary<string, string> GuidToKey = new();
2525
private static IImageHashGenerator _hashGenerator;
@@ -36,20 +36,25 @@ public static class ImageLoader
3636

3737
public static async Task InitializeAsync()
3838
{
39-
_storage = new BinaryStorage<List<(string, bool)>>("Image");
40-
_hashGenerator = new ImageHashGenerator();
39+
var usage = await Task.Run(() =>
40+
{
41+
_storage = new BinaryStorage<List<(string, bool)>>("Image");
42+
_hashGenerator = new ImageHashGenerator();
4143

42-
var usage = await LoadStorageToConcurrentDictionaryAsync();
43-
_storage.ClearData();
44+
var usage = LoadStorageToConcurrentDictionary();
45+
_storage.ClearData();
4446

45-
ImageCache.Initialize(usage);
47+
ImageCache.Initialize(usage);
4648

47-
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
48-
{
49-
ImageSource img = new BitmapImage(new Uri(icon));
50-
img.Freeze();
51-
ImageCache[icon, false] = img;
52-
}
49+
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
50+
{
51+
ImageSource img = new BitmapImage(new Uri(icon));
52+
img.Freeze();
53+
ImageCache[icon, false] = img;
54+
}
55+
56+
return usage;
57+
});
5358

5459
_ = Task.Run(async () =>
5560
{
@@ -64,42 +69,26 @@ await Stopwatch.InfoAsync(ClassName, "Preload images cost", async () =>
6469
});
6570
}
6671

67-
public static async Task SaveAsync()
72+
public static void Save()
6873
{
69-
await storageLock.WaitAsync();
70-
71-
try
72-
{
73-
await _storage.SaveAsync(ImageCache.EnumerateEntries()
74-
.Select(x => x.Key)
75-
.ToList());
76-
}
77-
catch (System.Exception e)
74+
lock (storageLock)
7875
{
79-
Log.Exception(ClassName, "Failed to save image cache to file", e);
80-
}
81-
finally
82-
{
83-
storageLock.Release();
76+
try
77+
{
78+
_storage.Save([.. ImageCache.EnumerateEntries().Select(x => x.Key)]);
79+
}
80+
catch (System.Exception e)
81+
{
82+
Log.Exception(ClassName, "Failed to save image cache to file", e);
83+
}
8484
}
8585
}
8686

87-
public static async Task WaitSaveAsync()
87+
private static List<(string, bool)> LoadStorageToConcurrentDictionary()
8888
{
89-
await storageLock.WaitAsync();
90-
storageLock.Release();
91-
}
92-
93-
private static async Task<List<(string, bool)>> LoadStorageToConcurrentDictionaryAsync()
94-
{
95-
await storageLock.WaitAsync();
96-
try
97-
{
98-
return await _storage.TryLoadAsync(new List<(string, bool)>());
99-
}
100-
finally
89+
lock (storageLock)
10190
{
102-
storageLock.Release();
91+
return _storage.TryLoad([]);
10392
}
10493
}
10594

Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,45 @@ public BinaryStorage(string filename, string directoryPath = null!)
5353
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
5454
}
5555

56+
public T TryLoad(T defaultData)
57+
{
58+
if (Data != null) return Data;
59+
60+
if (File.Exists(FilePath))
61+
{
62+
if (new FileInfo(FilePath).Length == 0)
63+
{
64+
Log.Error(ClassName, $"Zero length cache file <{FilePath}>");
65+
Data = defaultData;
66+
Save();
67+
}
68+
69+
var bytes = File.ReadAllBytes(FilePath);
70+
Data = Deserialize(bytes, defaultData);
71+
}
72+
else
73+
{
74+
Log.Info(ClassName, "Cache file not exist, load default data");
75+
Data = defaultData;
76+
Save();
77+
}
78+
return Data;
79+
}
80+
81+
private T Deserialize(ReadOnlySpan<byte> bytes, T defaultData)
82+
{
83+
try
84+
{
85+
var t = MemoryPackSerializer.Deserialize<T>(bytes);
86+
return t ?? defaultData;
87+
}
88+
catch (System.Exception e)
89+
{
90+
Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e);
91+
return defaultData;
92+
}
93+
}
94+
5695
public async ValueTask<T> TryLoadAsync(T defaultData)
5796
{
5897
if (Data != null) return Data;
@@ -79,26 +118,31 @@ public async ValueTask<T> TryLoadAsync(T defaultData)
79118
return Data;
80119
}
81120

82-
private static async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
121+
private async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
83122
{
84123
try
85124
{
86125
var t = await MemoryPackSerializer.DeserializeAsync<T>(stream);
87126
return t ?? defaultData;
88127
}
89-
catch (System.Exception)
128+
catch (System.Exception e)
90129
{
91-
// Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e);
130+
Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e);
92131
return defaultData;
93132
}
94133
}
95134

96135
public void Save()
136+
{
137+
Save(Data.NonNull());
138+
}
139+
140+
public void Save(T data)
97141
{
98142
// User may delete the directory, so we need to check it
99143
FilesFolders.ValidateDirectory(DirectoryPath);
100144

101-
var serialized = MemoryPackSerializer.Serialize(Data);
145+
var serialized = MemoryPackSerializer.Serialize(data);
102146
File.WriteAllBytes(FilePath, serialized);
103147
}
104148

@@ -107,15 +151,6 @@ public async ValueTask SaveAsync()
107151
await SaveAsync(Data.NonNull());
108152
}
109153

110-
// ImageCache need to convert data into concurrent dictionary for usage,
111-
// so we would better to clear the data
112-
public void ClearData()
113-
{
114-
Data = default;
115-
}
116-
117-
// ImageCache storages data in its class,
118-
// so we need to pass it to SaveAsync
119154
public async ValueTask SaveAsync(T data)
120155
{
121156
// User may delete the directory, so we need to check it
@@ -124,5 +159,12 @@ public async ValueTask SaveAsync(T data)
124159
await using var stream = new FileStream(FilePath, FileMode.Create);
125160
await MemoryPackSerializer.SerializeAsync(stream, data);
126161
}
162+
163+
// ImageCache need to convert data into concurrent dictionary for usage,
164+
// so we would better to clear the data
165+
public void ClearData()
166+
{
167+
Data = default;
168+
}
127169
}
128170
}

Flow.Launcher/MainWindow.xaml.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
using Flow.Launcher.Core.Resource;
1919
using Flow.Launcher.Infrastructure;
2020
using Flow.Launcher.Infrastructure.Hotkey;
21-
using Flow.Launcher.Infrastructure.Image;
2221
using Flow.Launcher.Infrastructure.DialogJump;
2322
using Flow.Launcher.Infrastructure.UserSettings;
2423
using Flow.Launcher.Plugin;
@@ -358,7 +357,6 @@ private async void OnClosing(object sender, CancelEventArgs e)
358357
_notifyIcon.Visible = false;
359358
App.API.SaveAppAllSettings();
360359
e.Cancel = true;
361-
await ImageLoader.WaitSaveAsync();
362360
await PluginManager.DisposePluginsAsync();
363361
Notification.Uninstall();
364362
// After plugins are all disposed, we shutdown application to close app

Flow.Launcher/PublicAPIInstance.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public void ChangeQuery(string query, bool requery = false)
7474
}
7575

7676
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
77-
public async void RestartApp()
77+
public void RestartApp()
7878
{
7979
_mainVM.Hide();
8080

@@ -83,9 +83,6 @@ public async void RestartApp()
8383
// which will cause ungraceful exit
8484
SaveAppAllSettings();
8585

86-
// Wait for all image caches to be saved before restarting
87-
await ImageLoader.WaitSaveAsync();
88-
8986
// Restart requires Squirrel's Update.exe to be present in the parent folder,
9087
// it is only published from the project's release pipeline. When debugging without it,
9188
// the project may not restart or just terminates. This is expected.
@@ -115,8 +112,8 @@ public void SaveAppAllSettings()
115112
_settings.Save();
116113
PluginManager.Save();
117114
_mainVM.Save();
115+
ImageLoader.Save();
118116
}
119-
_ = ImageLoader.SaveAsync();
120117
}
121118

122119
public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync();

0 commit comments

Comments
 (0)