From 7da2884e84ebd45dc70c16cd3dde6f6ef1e2b4af Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 8 Apr 2025 16:29:03 +0800 Subject: [PATCH] Add locks for win32s & uwps --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 61 +++++++++++++++---- .../Views/Commands/ProgramSettingDisplay.cs | 34 ++++++++--- .../Views/ProgramSetting.xaml.cs | 18 +++--- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index d3c50b406..6d2ae70fc 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -27,6 +27,9 @@ namespace Flow.Launcher.Plugin.Program internal static List _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 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() - .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()); + _win32sLock.Release(); + + await _uwpsLock.WaitAsync(); _uwps = await context.API.LoadCacheBinaryStorageAsync(UwpCacheName, pluginCachePath, new List()); + _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>(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>(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(); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs index e4d7c323a..b89a2a6ba 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/Commands/ProgramSettingDisplay.cs @@ -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 selectedProgramSourcesToDisable, bool status) + internal static async Task SetProgramSourcesStatusAsync(List 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 selectedItems) + internal static async Task IsReindexRequiredAsync(this List 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 diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs index 5ad7fcea3..c42bd4f30 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml.cs @@ -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 { selectedProgramSource }, + await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List { selectedProgramSource }, true); // sync status in win32, uwp and disabled ProgramSettingDisplay.RemoveDisabledFromSettings(); } else { - ProgramSettingDisplay.SetProgramSourcesStatus(new List { selectedProgramSource }, + await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List { 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() @@ -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();