diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 957f836bb..416c75a9d 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -54,28 +54,28 @@ jobs: shell: powershell run: .\Scripts\post_build.ps1 - name: Upload Plugin Nupkg - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: Plugin nupkg path: | Output\Release\Flow.Launcher.Plugin.*.nupkg compression-level: 0 - name: Upload Setup - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: Flow Installer path: | Output\Packages\Flow-Launcher-*.exe compression-level: 0 - name: Upload Portable Version - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: Portable Version path: | Output\Packages\Flow-Launcher-Portable.zip compression-level: 0 - name: Upload Full Nupkg - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: Full nupkg path: | @@ -83,7 +83,7 @@ jobs: compression-level: 0 - name: Upload Release Information - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: RELEASES path: | diff --git a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs index eab9a8c43..4fed10d25 100644 --- a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs +++ b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs @@ -26,9 +26,11 @@ namespace Flow.Launcher.Core.ExternalPlugins 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 @@ namespace Flow.Launcher.Core.ExternalPlugins 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/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index fc3ab0a9c..420c07a8d 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -454,7 +454,7 @@ Icons You have activated Flow Launcher {0} times Check for Updates - Become A Sponsor + Become a Sponsor New version {0} is available, would you like to restart Flow Launcher to use the update? Check updates failed, please check your connection and proxy settings to api.github.com. diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index fd62566d5..a4e959dd9 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 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything 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 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything 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 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything 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.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index f9d8963e6..d7b069082 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -57,19 +57,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search { if (string.IsNullOrEmpty(query.Search) && ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword)) return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks); - - var quickAccessLinks = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); - - results.UnionWith(quickAccessLinks); } else { + // No action keyword matched- plugin should not handle this query, return empty results. return new List(); } IAsyncEnumerable searchResults; - bool isPathSearch = query.Search.IsLocationPathString() + bool isPathSearch = query.Search.IsLocationPathString() || EnvironmentVariables.IsEnvironmentVariableSearch(query.Search) || EnvironmentVariables.HasEnvironmentVar(query.Search); @@ -103,10 +100,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search searchResults = Settings.IndexProvider.SearchAsync(query.Search, token); engineName = Enum.GetName(Settings.IndexSearchEngine); break; + + case true or false + when ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword): + return QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); + default: return results.ToList(); } + // Merge Quick Access Link results for non-path searches. + results.UnionWith(QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks)); + try { await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false)) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 0258a10d2..456085fca 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -31,6 +31,8 @@ namespace Flow.Launcher.Plugin.Program 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 @@ namespace Flow.Launcher.Plugin.Program { 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 @@ namespace Flow.Launcher.Plugin.Program .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray() : null; - return _win32s.Cast() - .Concat(_uwps) + return win32s.Cast() + .Concat(uwps) .AsParallel() .WithCancellation(token) .Where(HideUninstallersFilter) @@ -109,11 +148,6 @@ namespace Flow.Launcher.Plugin.Program { return emptyResults; } - finally - { - _uwpsLock.Release(); - _win32sLock.Release(); - } }, token); resultList = resultList.Count != 0 ? resultList : emptyResults; @@ -275,7 +309,12 @@ namespace Flow.Launcher.Plugin.Program 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 @@ namespace Flow.Launcher.Plugin.Program } 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 @@ namespace Flow.Launcher.Plugin.Program } ResetCache(); await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - _settings.LastIndexTime = DateTime.Now; + lock (_lastIndexTimeLock) + { + _settings.LastIndexTime = DateTime.Now; + } } catch (Exception e) {