diff --git a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs index eab9a8c43ca..4fed10d25ff 100644 --- a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs +++ b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs @@ -26,9 +26,11 @@ public static class PluginsManifest public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default) { + bool lockAcquired = false; try { await manifestUpdateLock.WaitAsync(token).ConfigureAwait(false); + lockAcquired = true; if (UserPlugins == null || usePrimaryUrlOnly || DateTime.Now.Subtract(lastFetchedAt) >= fetchTimeout) { @@ -54,13 +56,18 @@ public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = fals return true; } } + catch (OperationCanceledException) + { + // Ignored + } catch (Exception e) { PublicApi.Instance.LogException(ClassName, "Http request failed", e); } finally { - manifestUpdateLock.Release(); + // Only release the lock if it was acquired + if (lockAcquired) manifestUpdateLock.Release(); } return false; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index fd62566d54e..a4e959dd9c4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs @@ -48,11 +48,18 @@ public static bool IsFastSortOption(EverythingSortOption sortOption) public static async ValueTask IsEverythingRunningAsync(CancellationToken token = default) { - await _semaphore.WaitAsync(token); + try + { + await _semaphore.WaitAsync(token); + } + catch (OperationCanceledException) + { + return false; + } try { - EverythingApiDllImport.Everything_GetMajorVersion(); + _ = EverythingApiDllImport.Everything_GetMajorVersion(); var result = EverythingApiDllImport.Everything_GetLastError() != StateCode.IPCError; return result; } @@ -77,8 +84,14 @@ public static async IAsyncEnumerable SearchAsync(EverythingSearchO if (option.MaxCount < 0) throw new ArgumentOutOfRangeException(nameof(option.MaxCount), option.MaxCount, "MaxCount must be greater than or equal to 0"); - await _semaphore.WaitAsync(token); - + try + { + await _semaphore.WaitAsync(token); + } + catch (OperationCanceledException) + { + yield break; + } try { @@ -120,8 +133,6 @@ public static async IAsyncEnumerable SearchAsync(EverythingSearchO EverythingApiDllImport.Everything_SetRequestFlags(EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME); } - - if (token.IsCancellationRequested) yield break; if (!EverythingApiDllImport.Everything_QueryW(true)) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 0258a10d2c7..456085fcaf2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -31,6 +31,8 @@ public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, I internal static PluginInitContext Context { get; private set; } + private static readonly Lock _lastIndexTimeLock = new(); + private static readonly List emptyResults = []; private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 }; @@ -82,8 +84,45 @@ public async Task> QueryAsync(Query query, CancellationToken token) { var resultList = await Task.Run(async () => { - await _win32sLock.WaitAsync(token); - await _uwpsLock.WaitAsync(token); + // Preparing win32 programs + List win32s; + bool win32LockAcquired = false; + try + { + await _win32sLock.WaitAsync(token); + win32LockAcquired = true; + win32s = [.. _win32s]; + } + catch (OperationCanceledException) + { + return emptyResults; + } + finally + { + // Only release the lock if it was acquired + if (win32LockAcquired) _win32sLock.Release(); + } + + // Preparing UWP programs + List uwps; + bool uwpsLockAcquired = false; + try + { + await _uwpsLock.WaitAsync(token); + uwpsLockAcquired = true; + uwps = [.. _uwps]; + } + catch (OperationCanceledException) + { + return emptyResults; + } + finally + { + // Only release the lock if it was acquired + if (uwpsLockAcquired) _uwpsLock.Release(); + } + + // Start querying programs try { // Collect all UWP Windows app directories @@ -94,8 +133,8 @@ public async Task> QueryAsync(Query query, CancellationToken token) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray() : null; - return _win32s.Cast() - .Concat(_uwps) + return win32s.Cast() + .Concat(uwps) .AsParallel() .WithCancellation(token) .Where(HideUninstallersFilter) @@ -109,11 +148,6 @@ public async Task> QueryAsync(Query query, CancellationToken token) { return emptyResults; } - finally - { - _uwpsLock.Release(); - _win32sLock.Release(); - } }, token); resultList = resultList.Count != 0 ? resultList : emptyResults; @@ -275,7 +309,12 @@ static void MoveFile(string sourcePath, string destinationPath) var cacheEmpty = _win32sCount == 0 || _uwpsCount == 0; - if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now) + bool needReindex; + lock (_lastIndexTimeLock) + { + needReindex = _settings.LastIndexTime.AddHours(30) < DateTime.Now; + } + if (cacheEmpty || needReindex) { _ = Task.Run(async () => { @@ -308,7 +347,10 @@ public static async Task IndexWin32ProgramsAsync() } ResetCache(); await Context.API.SaveCacheBinaryStorageAsync>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - _settings.LastIndexTime = DateTime.Now; + lock (_lastIndexTimeLock) + { + _settings.LastIndexTime = DateTime.Now; + } } catch (Exception e) { @@ -333,7 +375,10 @@ public static async Task IndexUwpProgramsAsync() } ResetCache(); await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - _settings.LastIndexTime = DateTime.Now; + lock (_lastIndexTimeLock) + { + _settings.LastIndexTime = DateTime.Now; + } } catch (Exception e) {