mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 17:56:44 +02:00
[CmdPal] Refactor ActionRuntime initialization to avoid repeated delays on failure (#40229)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request This PR addresses an issue where ActionRuntime initialization may repeatedly incur delays and fail during runtime usage, especially when the OS supports the API but action creation fails. ### Fix: Introduced ActionRuntimeManager to: - Manage a shared static ActionRuntime instance - Perform initialization once, at extension loading time - Store the result (success or failure) to avoid repeated creation attempts Consumers now: - ActionRuntimeManager.InstanceAsync.GetAwaiter().GetResult() Initialization will only be attempted up to 3 times. After that, runtime is marked as unavailable and no further attempts are made. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #40228 - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Windows.AI.Actions;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
|
||||
public static class ActionRuntimeManager
|
||||
{
|
||||
private static readonly Lazy<Task<ActionRuntime>> _lazyRuntime = new(InitializeAsync);
|
||||
|
||||
public static Task<ActionRuntime> InstanceAsync => _lazyRuntime.Value;
|
||||
|
||||
private static async Task<ActionRuntime> InitializeAsync()
|
||||
{
|
||||
// If we tried 3 times and failed, should we think the action runtime is not working?
|
||||
// then we should not use it anymore.
|
||||
const int maxAttempts = 3;
|
||||
|
||||
for (var attempt = 1; attempt <= maxAttempts; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var runtime = ActionRuntimeFactory.CreateActionRuntime();
|
||||
await Task.Delay(500);
|
||||
|
||||
return runtime;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Attempt {attempt} to initialize ActionRuntime failed: {ex.Message}");
|
||||
|
||||
if (attempt == maxAttempts)
|
||||
{
|
||||
Logger.LogError($"Failed to initialize ActionRuntime: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation.Metadata;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer;
|
||||
|
||||
@@ -17,6 +19,10 @@ public partial class IndexerCommandsProvider : CommandProvider
|
||||
Id = "Files";
|
||||
DisplayName = Resources.IndexerCommandsProvider_DisplayName;
|
||||
Icon = Icons.FileExplorer;
|
||||
if (ApiInformation.IsApiContractPresent("Windows.AI.Actions.ActionsContract", 4))
|
||||
{
|
||||
_ = ActionRuntimeManager.InstanceAsync;
|
||||
}
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands()
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
@@ -15,7 +17,7 @@ using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Pages;
|
||||
|
||||
internal sealed partial class ActionsListContextItem : CommandContextItem
|
||||
internal sealed partial class ActionsListContextItem : CommandContextItem, IDisposable
|
||||
{
|
||||
private readonly string fullPath;
|
||||
private readonly List<CommandContextItem> actions = [];
|
||||
@@ -41,20 +43,24 @@ internal sealed partial class ActionsListContextItem : CommandContextItem
|
||||
|
||||
private void UpdateMoreCommands()
|
||||
{
|
||||
try
|
||||
lock (UpdateMoreCommandsLock)
|
||||
{
|
||||
lock (UpdateMoreCommandsLock)
|
||||
if (actionRuntime == null)
|
||||
{
|
||||
if (actionRuntime == null)
|
||||
{
|
||||
actionRuntime = ActionRuntimeFactory.CreateActionRuntime();
|
||||
Task.Delay(500).Wait();
|
||||
}
|
||||
|
||||
actionRuntime.ActionCatalog.Changed -= ActionCatalog_Changed;
|
||||
actionRuntime.ActionCatalog.Changed += ActionCatalog_Changed;
|
||||
actionRuntime = ActionRuntimeManager.InstanceAsync.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
if (actionRuntime == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
actionRuntime.ActionCatalog.Changed -= ActionCatalog_Changed;
|
||||
actionRuntime.ActionCatalog.Changed += ActionCatalog_Changed;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var extension = System.IO.Path.GetExtension(fullPath).ToLower(CultureInfo.InvariantCulture);
|
||||
ActionEntity entity = null;
|
||||
if (extension != null)
|
||||
@@ -85,9 +91,20 @@ internal sealed partial class ActionsListContextItem : CommandContextItem
|
||||
MoreCommands = [.. actions];
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
actionRuntime = null;
|
||||
Logger.LogError($"Error updating commands: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (UpdateMoreCommandsLock)
|
||||
{
|
||||
if (actionRuntime != null)
|
||||
{
|
||||
actionRuntime.ActionCatalog.Changed -= ActionCatalog_Changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user