Add locks for win32s & uwps

This commit is contained in:
Jack251970 2025-04-08 16:29:03 +08:00
parent 9f84a2d50a
commit 7da2884e84
3 changed files with 83 additions and 30 deletions

View file

@ -27,6 +27,9 @@ namespace Flow.Launcher.Plugin.Program
internal static List<UWPApp> _uwps { get; private set; }
internal static Settings _settings { get; private set; }
internal static SemaphoreSlim _win32sLock = new(1, 1);
internal static SemaphoreSlim _uwpsLock = new(1, 1);
internal static PluginInitContext Context { get; private set; }
private static readonly List<Result> emptyResults = new();
@ -82,8 +85,11 @@ namespace Flow.Launcher.Plugin.Program
{
var result = await cache.GetOrCreateAsync(query.Search, async entry =>
{
var resultList = await Task.Run(() =>
var resultList = await Task.Run(async () =>
{
await _win32sLock.WaitAsync(token);
await _uwpsLock.WaitAsync(token);
try
{
// Collect all UWP Windows app directories
@ -95,22 +101,26 @@ namespace Flow.Launcher.Plugin.Program
.ToArray() : null;
return _win32s.Cast<IProgram>()
.Concat(_uwps)
.AsParallel()
.WithCancellation(token)
.Where(HideUninstallersFilter)
.Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories))
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, Context.API))
.Where(r => r?.Score > 0)
.ToList();
.Concat(_uwps)
.AsParallel()
.WithCancellation(token)
.Where(HideUninstallersFilter)
.Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories))
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, Context.API))
.Where(r => r?.Score > 0)
.ToList();
}
catch (OperationCanceledException)
{
Log.Debug("|Flow.Launcher.Plugin.Program.Main|Query operation cancelled");
return emptyResults;
}
finally
{
_uwpsLock.Release();
_win32sLock.Release();
}
}, token);
resultList = resultList.Any() ? resultList : emptyResults;
@ -236,14 +246,25 @@ namespace Flow.Launcher.Plugin.Program
var newUWPCacheFile = Path.Combine(pluginCachePath, $"{UwpCacheName}.cache");
MoveFile(oldUWPCacheFile, newUWPCacheFile);
await _win32sLock.WaitAsync();
_win32s = await context.API.LoadCacheBinaryStorageAsync(Win32CacheName, pluginCachePath, new List<Win32>());
_win32sLock.Release();
await _uwpsLock.WaitAsync();
_uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCachePath, new List<UWPApp>());
_uwpsLock.Release();
});
await _win32sLock.WaitAsync();
await _uwpsLock.WaitAsync();
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Count}>");
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Count}>");
bool cacheEmpty = !_win32s.Any() || !_uwps.Any();
_win32sLock.Release();
_uwpsLock.Release();
if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now)
{
_ = Task.Run(async () =>
@ -267,11 +288,13 @@ namespace Flow.Launcher.Plugin.Program
public static async Task IndexWin32ProgramsAsync()
{
var win32S = Win32.All(_settings);
await _win32sLock.WaitAsync();
_win32s.Clear();
foreach (var win32 in win32S)
{
_win32s.Add(win32);
}
_win32sLock.Release();
ResetCache();
await Context.API.SaveCacheBinaryStorageAsync<List<Win32>>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
_settings.LastIndexTime = DateTime.Now;
@ -280,11 +303,13 @@ namespace Flow.Launcher.Plugin.Program
public static async Task IndexUwpProgramsAsync()
{
var uwps = UWPPackage.All(_settings);
await _uwpsLock.WaitAsync();
_uwps.Clear();
foreach (var uwp in uwps)
{
_uwps.Add(uwp);
}
_uwpsLock.Release();
ResetCache();
await Context.API.SaveCacheBinaryStorageAsync<List<UWPApp>>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
_settings.LastIndexTime = DateTime.Now;
@ -358,26 +383,36 @@ namespace Flow.Launcher.Plugin.Program
return menuOptions;
}
private static void DisableProgram(IProgram programToDelete)
private static async Task DisableProgram(IProgram programToDelete)
{
if (_settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
return;
await _uwpsLock.WaitAsync();
if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
{
var program = _uwps.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
program.Enabled = false;
_settings.DisabledProgramSources.Add(new ProgramSource(program));
_uwpsLock.Release();
// Reindex UWP programs
_ = Task.Run(() =>
{
_ = IndexUwpProgramsAsync();
});
return;
}
else if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
await _win32sLock.WaitAsync();
if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier))
{
var program = _win32s.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier);
program.Enabled = false;
_settings.DisabledProgramSources.Add(new ProgramSource(program));
_win32sLock.Release();
// Reindex Win32 programs
_ = Task.Run(() =>
{
_ = IndexWin32ProgramsAsync();

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Flow.Launcher.Plugin.Program.Views.Models;
namespace Flow.Launcher.Plugin.Program.Views.Commands
@ -15,21 +16,24 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
.ToList();
}
internal static void DisplayAllPrograms()
internal static async Task DisplayAllProgramsAsync()
{
await Main._win32sLock.WaitAsync();
var win32 = Main._win32s
.Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
.Select(x => new ProgramSource(x));
ProgramSetting.ProgramSettingDisplayList.AddRange(win32);
Main._win32sLock.Release();
await Main._uwpsLock.WaitAsync();
var uwp = Main._uwps
.Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
.Select(x => new ProgramSource(x));
ProgramSetting.ProgramSettingDisplayList.AddRange(win32);
ProgramSetting.ProgramSettingDisplayList.AddRange(uwp);
Main._uwpsLock.Release();
}
internal static void SetProgramSourcesStatus(List<ProgramSource> selectedProgramSourcesToDisable, bool status)
internal static async Task SetProgramSourcesStatusAsync(List<ProgramSource> selectedProgramSourcesToDisable, bool status)
{
foreach(var program in ProgramSetting.ProgramSettingDisplayList)
{
@ -39,14 +43,17 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
}
}
foreach(var program in Main._win32s)
await Main._win32sLock.WaitAsync();
foreach (var program in Main._win32s)
{
if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
{
program.Enabled = status;
}
}
Main._win32sLock.Release();
await Main._uwpsLock.WaitAsync();
foreach (var program in Main._uwps)
{
if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
@ -54,6 +61,7 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
program.Enabled = status;
}
}
Main._uwpsLock.Release();
}
internal static void StoreDisabledInSettings()
@ -72,12 +80,22 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
Main._settings.DisabledProgramSources.RemoveAll(t1 => t1.Enabled);
}
internal static bool IsReindexRequired(this List<ProgramSource> selectedItems)
internal static async Task<bool> IsReindexRequiredAsync(this List<ProgramSource> selectedItems)
{
// Not in cache
if (selectedItems.Any(t1 => t1.Enabled && !Main._uwps.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
await Main._win32sLock.WaitAsync();
await Main._uwpsLock.WaitAsync();
try
{
if (selectedItems.Any(t1 => t1.Enabled && !Main._uwps.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier))
&& selectedItems.Any(t1 => t1.Enabled && !Main._win32s.Any(x => t1.UniqueIdentifier == x.UniqueIdentifier)))
return true;
return true;
}
finally
{
Main._win32sLock.Release();
Main._uwpsLock.Release();
}
// ProgramSources holds list of user added directories,
// so when we enable/disable we need to reindex to show/not show the programs

View file

@ -183,7 +183,7 @@ namespace Flow.Launcher.Plugin.Program.Views
EditProgramSource(selectedProgramSource);
}
private void EditProgramSource(ProgramSource selectedProgramSource)
private async void EditProgramSource(ProgramSource selectedProgramSource)
{
if (selectedProgramSource == null)
{
@ -202,13 +202,13 @@ namespace Flow.Launcher.Plugin.Program.Views
{
if (selectedProgramSource.Enabled)
{
ProgramSettingDisplay.SetProgramSourcesStatus(new List<ProgramSource> { selectedProgramSource },
await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List<ProgramSource> { selectedProgramSource },
true); // sync status in win32, uwp and disabled
ProgramSettingDisplay.RemoveDisabledFromSettings();
}
else
{
ProgramSettingDisplay.SetProgramSourcesStatus(new List<ProgramSource> { selectedProgramSource },
await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List<ProgramSource> { selectedProgramSource },
false);
ProgramSettingDisplay.StoreDisabledInSettings();
}
@ -277,14 +277,14 @@ namespace Flow.Launcher.Plugin.Program.Views
}
}
private void btnLoadAllProgramSource_OnClick(object sender, RoutedEventArgs e)
private async void btnLoadAllProgramSource_OnClick(object sender, RoutedEventArgs e)
{
ProgramSettingDisplay.DisplayAllPrograms();
await ProgramSettingDisplay.DisplayAllProgramsAsync();
ViewRefresh();
}
private void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e)
private async void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e)
{
var selectedItems = programSourceView
.SelectedItems.Cast<ProgramSource>()
@ -311,18 +311,18 @@ namespace Flow.Launcher.Plugin.Program.Views
}
else if (HasMoreOrEqualEnabledItems(selectedItems))
{
ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, false);
await ProgramSettingDisplay.SetProgramSourcesStatusAsync(selectedItems, false);
ProgramSettingDisplay.StoreDisabledInSettings();
}
else
{
ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, true);
await ProgramSettingDisplay.SetProgramSourcesStatusAsync(selectedItems, true);
ProgramSettingDisplay.RemoveDisabledFromSettings();
}
if (selectedItems.IsReindexRequired())
if (await selectedItems.IsReindexRequiredAsync())
ReIndexing();
programSourceView.SelectedItems.Clear();