Merge pull request #4072 from Flow-Launcher/program_plugin_index_issue

Fix Possible OperationCancelException for WaitAsync Operations
This commit is contained in:
Jack Ye 2025-11-08 11:28:58 +08:00 committed by GitHub
commit fc93d0904e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 82 additions and 19 deletions

View file

@ -26,9 +26,11 @@ namespace Flow.Launcher.Core.ExternalPlugins
public static async Task<bool> 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;

View file

@ -48,11 +48,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
public static async ValueTask<bool> 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))

View file

@ -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<Result> 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<Win32> 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<UWPApp> 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<IProgram>()
.Concat(_uwps)
return win32s.Cast<IProgram>()
.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<List<Win32>>(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<List<UWPApp>>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
_settings.LastIndexTime = DateTime.Now;
lock (_lastIndexTimeLock)
{
_settings.LastIndexTime = DateTime.Now;
}
}
catch (Exception e)
{