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

View file

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Flow.Launcher.Plugin.Program.Views.Models; using Flow.Launcher.Plugin.Program.Views.Models;
namespace Flow.Launcher.Plugin.Program.Views.Commands namespace Flow.Launcher.Plugin.Program.Views.Commands
@ -15,21 +16,24 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
.ToList(); .ToList();
} }
internal static void DisplayAllPrograms() internal static async Task DisplayAllProgramsAsync()
{ {
await Main._win32sLock.WaitAsync();
var win32 = Main._win32s var win32 = Main._win32s
.Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
.Select(x => new ProgramSource(x)); .Select(x => new ProgramSource(x));
ProgramSetting.ProgramSettingDisplayList.AddRange(win32);
Main._win32sLock.Release();
await Main._uwpsLock.WaitAsync();
var uwp = Main._uwps var uwp = Main._uwps
.Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) .Where(t1 => !ProgramSetting.ProgramSettingDisplayList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
.Select(x => new ProgramSource(x)); .Select(x => new ProgramSource(x));
ProgramSetting.ProgramSettingDisplayList.AddRange(win32);
ProgramSetting.ProgramSettingDisplayList.AddRange(uwp); 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) 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)) if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status))
{ {
program.Enabled = status; program.Enabled = status;
} }
} }
Main._win32sLock.Release();
await Main._uwpsLock.WaitAsync();
foreach (var program in Main._uwps) foreach (var program in Main._uwps)
{ {
if (selectedProgramSourcesToDisable.Any(x => x.UniqueIdentifier == program.UniqueIdentifier && program.Enabled != status)) 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; program.Enabled = status;
} }
} }
Main._uwpsLock.Release();
} }
internal static void StoreDisabledInSettings() internal static void StoreDisabledInSettings()
@ -72,12 +80,22 @@ namespace Flow.Launcher.Plugin.Program.Views.Commands
Main._settings.DisabledProgramSources.RemoveAll(t1 => t1.Enabled); 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 // 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))) && 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, // ProgramSources holds list of user added directories,
// so when we enable/disable we need to reindex to show/not show the programs // 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); EditProgramSource(selectedProgramSource);
} }
private void EditProgramSource(ProgramSource selectedProgramSource) private async void EditProgramSource(ProgramSource selectedProgramSource)
{ {
if (selectedProgramSource == null) if (selectedProgramSource == null)
{ {
@ -202,13 +202,13 @@ namespace Flow.Launcher.Plugin.Program.Views
{ {
if (selectedProgramSource.Enabled) if (selectedProgramSource.Enabled)
{ {
ProgramSettingDisplay.SetProgramSourcesStatus(new List<ProgramSource> { selectedProgramSource }, await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List<ProgramSource> { selectedProgramSource },
true); // sync status in win32, uwp and disabled true); // sync status in win32, uwp and disabled
ProgramSettingDisplay.RemoveDisabledFromSettings(); ProgramSettingDisplay.RemoveDisabledFromSettings();
} }
else else
{ {
ProgramSettingDisplay.SetProgramSourcesStatus(new List<ProgramSource> { selectedProgramSource }, await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List<ProgramSource> { selectedProgramSource },
false); false);
ProgramSettingDisplay.StoreDisabledInSettings(); 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(); ViewRefresh();
} }
private void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e) private async void btnProgramSourceStatus_OnClick(object sender, RoutedEventArgs e)
{ {
var selectedItems = programSourceView var selectedItems = programSourceView
.SelectedItems.Cast<ProgramSource>() .SelectedItems.Cast<ProgramSource>()
@ -311,18 +311,18 @@ namespace Flow.Launcher.Plugin.Program.Views
} }
else if (HasMoreOrEqualEnabledItems(selectedItems)) else if (HasMoreOrEqualEnabledItems(selectedItems))
{ {
ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, false); await ProgramSettingDisplay.SetProgramSourcesStatusAsync(selectedItems, false);
ProgramSettingDisplay.StoreDisabledInSettings(); ProgramSettingDisplay.StoreDisabledInSettings();
} }
else else
{ {
ProgramSettingDisplay.SetProgramSourcesStatus(selectedItems, true); await ProgramSettingDisplay.SetProgramSourcesStatusAsync(selectedItems, true);
ProgramSettingDisplay.RemoveDisabledFromSettings(); ProgramSettingDisplay.RemoveDisabledFromSettings();
} }
if (selectedItems.IsReindexRequired()) if (await selectedItems.IsReindexRequiredAsync())
ReIndexing(); ReIndexing();
programSourceView.SelectedItems.Clear(); programSourceView.SelectedItems.Clear();