mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Add non-async version load in BinaryStorage & Use non-async version for ImageLoader
This commit is contained in:
parent
7b08c965ec
commit
3afc52187f
4 changed files with 89 additions and 63 deletions
|
|
@ -19,7 +19,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
private static readonly string ClassName = nameof(ImageLoader);
|
||||
|
||||
private static readonly ImageCache ImageCache = new();
|
||||
private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1);
|
||||
private static Lock storageLock { get; } = new();
|
||||
private static BinaryStorage<List<(string, bool)>> _storage;
|
||||
private static readonly ConcurrentDictionary<string, string> GuidToKey = new();
|
||||
private static IImageHashGenerator _hashGenerator;
|
||||
|
|
@ -36,20 +36,25 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
|
||||
public static async Task InitializeAsync()
|
||||
{
|
||||
_storage = new BinaryStorage<List<(string, bool)>>("Image");
|
||||
_hashGenerator = new ImageHashGenerator();
|
||||
|
||||
var usage = await LoadStorageToConcurrentDictionaryAsync();
|
||||
_storage.ClearData();
|
||||
|
||||
ImageCache.Initialize(usage);
|
||||
|
||||
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
|
||||
var usage = await Task.Run(() =>
|
||||
{
|
||||
ImageSource img = new BitmapImage(new Uri(icon));
|
||||
img.Freeze();
|
||||
ImageCache[icon, false] = img;
|
||||
}
|
||||
_storage = new BinaryStorage<List<(string, bool)>>("Image");
|
||||
_hashGenerator = new ImageHashGenerator();
|
||||
|
||||
var usage = LoadStorageToConcurrentDictionary();
|
||||
_storage.ClearData();
|
||||
|
||||
ImageCache.Initialize(usage);
|
||||
|
||||
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
|
||||
{
|
||||
ImageSource img = new BitmapImage(new Uri(icon));
|
||||
img.Freeze();
|
||||
ImageCache[icon, false] = img;
|
||||
}
|
||||
|
||||
return usage;
|
||||
});
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
|
|
@ -64,42 +69,26 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
});
|
||||
}
|
||||
|
||||
public static async Task SaveAsync()
|
||||
public static void Save()
|
||||
{
|
||||
await storageLock.WaitAsync();
|
||||
|
||||
try
|
||||
lock (storageLock)
|
||||
{
|
||||
await _storage.SaveAsync(ImageCache.EnumerateEntries()
|
||||
.Select(x => x.Key)
|
||||
.ToList());
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Log.Exception(ClassName, "Failed to save image cache to file", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
storageLock.Release();
|
||||
try
|
||||
{
|
||||
_storage.Save([.. ImageCache.EnumerateEntries().Select(x => x.Key)]);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Log.Exception(ClassName, "Failed to save image cache to file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task WaitSaveAsync()
|
||||
private static List<(string, bool)> LoadStorageToConcurrentDictionary()
|
||||
{
|
||||
await storageLock.WaitAsync();
|
||||
storageLock.Release();
|
||||
}
|
||||
|
||||
private static async Task<List<(string, bool)>> LoadStorageToConcurrentDictionaryAsync()
|
||||
{
|
||||
await storageLock.WaitAsync();
|
||||
try
|
||||
lock (storageLock)
|
||||
{
|
||||
return await _storage.TryLoadAsync(new List<(string, bool)>());
|
||||
}
|
||||
finally
|
||||
{
|
||||
storageLock.Release();
|
||||
return _storage.TryLoad([]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,45 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
|
||||
}
|
||||
|
||||
public T TryLoad(T defaultData)
|
||||
{
|
||||
if (Data != null) return Data;
|
||||
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
if (new FileInfo(FilePath).Length == 0)
|
||||
{
|
||||
Log.Error(ClassName, $"Zero length cache file <{FilePath}>");
|
||||
Data = defaultData;
|
||||
Save();
|
||||
}
|
||||
|
||||
var bytes = File.ReadAllBytes(FilePath);
|
||||
Data = Deserialize(bytes, defaultData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Info(ClassName, "Cache file not exist, load default data");
|
||||
Data = defaultData;
|
||||
Save();
|
||||
}
|
||||
return Data;
|
||||
}
|
||||
|
||||
private T Deserialize(ReadOnlySpan<byte> bytes, T defaultData)
|
||||
{
|
||||
try
|
||||
{
|
||||
var t = MemoryPackSerializer.Deserialize<T>(bytes);
|
||||
return t ?? defaultData;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e);
|
||||
return defaultData;
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<T> TryLoadAsync(T defaultData)
|
||||
{
|
||||
if (Data != null) return Data;
|
||||
|
|
@ -79,26 +118,31 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
return Data;
|
||||
}
|
||||
|
||||
private static async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
|
||||
private async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
|
||||
{
|
||||
try
|
||||
{
|
||||
var t = await MemoryPackSerializer.DeserializeAsync<T>(stream);
|
||||
return t ?? defaultData;
|
||||
}
|
||||
catch (System.Exception)
|
||||
catch (System.Exception e)
|
||||
{
|
||||
// Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e);
|
||||
Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e);
|
||||
return defaultData;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
Save(Data.NonNull());
|
||||
}
|
||||
|
||||
public void Save(T data)
|
||||
{
|
||||
// User may delete the directory, so we need to check it
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
var serialized = MemoryPackSerializer.Serialize(Data);
|
||||
var serialized = MemoryPackSerializer.Serialize(data);
|
||||
File.WriteAllBytes(FilePath, serialized);
|
||||
}
|
||||
|
||||
|
|
@ -107,15 +151,6 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
await SaveAsync(Data.NonNull());
|
||||
}
|
||||
|
||||
// ImageCache need to convert data into concurrent dictionary for usage,
|
||||
// so we would better to clear the data
|
||||
public void ClearData()
|
||||
{
|
||||
Data = default;
|
||||
}
|
||||
|
||||
// ImageCache storages data in its class,
|
||||
// so we need to pass it to SaveAsync
|
||||
public async ValueTask SaveAsync(T data)
|
||||
{
|
||||
// User may delete the directory, so we need to check it
|
||||
|
|
@ -124,5 +159,12 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
await using var stream = new FileStream(FilePath, FileMode.Create);
|
||||
await MemoryPackSerializer.SerializeAsync(stream, data);
|
||||
}
|
||||
|
||||
// ImageCache need to convert data into concurrent dictionary for usage,
|
||||
// so we would better to clear the data
|
||||
public void ClearData()
|
||||
{
|
||||
Data = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ using Flow.Launcher.Core.Plugin;
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.DialogJump;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
|
@ -358,7 +357,6 @@ namespace Flow.Launcher
|
|||
_notifyIcon.Visible = false;
|
||||
App.API.SaveAppAllSettings();
|
||||
e.Cancel = true;
|
||||
await ImageLoader.WaitSaveAsync();
|
||||
await PluginManager.DisposePluginsAsync();
|
||||
Notification.Uninstall();
|
||||
// After plugins are all disposed, we shutdown application to close app
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ namespace Flow.Launcher
|
|||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
|
||||
public async void RestartApp()
|
||||
public void RestartApp()
|
||||
{
|
||||
_mainVM.Hide();
|
||||
|
||||
|
|
@ -83,9 +83,6 @@ namespace Flow.Launcher
|
|||
// which will cause ungraceful exit
|
||||
SaveAppAllSettings();
|
||||
|
||||
// Wait for all image caches to be saved before restarting
|
||||
await ImageLoader.WaitSaveAsync();
|
||||
|
||||
// Restart requires Squirrel's Update.exe to be present in the parent folder,
|
||||
// it is only published from the project's release pipeline. When debugging without it,
|
||||
// the project may not restart or just terminates. This is expected.
|
||||
|
|
@ -115,8 +112,8 @@ namespace Flow.Launcher
|
|||
_settings.Save();
|
||||
PluginManager.Save();
|
||||
_mainVM.Save();
|
||||
ImageLoader.Save();
|
||||
}
|
||||
_ = ImageLoader.SaveAsync();
|
||||
}
|
||||
|
||||
public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync();
|
||||
|
|
|
|||
Loading…
Reference in a new issue