Add cancellation support to AsyncPlugin methods

This commit is contained in:
Stone_Red
2026-01-13 22:42:15 +01:00
parent 056c42d4df
commit 92ecd2e93d
3 changed files with 44 additions and 4 deletions
@@ -7,6 +7,7 @@ using System.Drawing;
using System.Globalization; using System.Globalization;
using System.Net.Http; using System.Net.Http;
using System.Text.Json; using System.Text.Json;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DesktopMagic.BuiltInPlugins; namespace DesktopMagic.BuiltInPlugins;
@@ -46,7 +47,7 @@ public class WeatherPlugin : AsyncPlugin
private string lastUpdated = ""; private string lastUpdated = "";
private bool isLoading = false; private bool isLoading = false;
public override async void Start() public override async Task StartAsync(CancellationToken cancellationToken)
{ {
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (DesktopMagic)"); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (DesktopMagic)");
@@ -74,7 +75,7 @@ public class WeatherPlugin : AsyncPlugin
}; };
} }
public override async Task<Bitmap?> MainAsync() public override async Task<Bitmap?> MainAsync(CancellationToken cancellationToken)
{ {
// Periodic background update // Periodic background update
if (!isLoading && cachedLat != null && cachedLon != null) if (!isLoading && cachedLat != null && cachedLon != null)
+27 -1
View File
@@ -37,6 +37,8 @@ public partial class PluginWindow : Window, IPluginWindow
private Plugin? pluginClassInstance; private Plugin? pluginClassInstance;
private readonly AssemblyLoadContext assemblyLoadContext; private readonly AssemblyLoadContext assemblyLoadContext;
private CancellationTokenSource? pluginCancellationTokenSource;
public bool IsRunning { get; private set; } = true; public bool IsRunning { get; private set; } = true;
public PluginMetadata PluginMetadata { get; private set; } public PluginMetadata PluginMetadata { get; private set; }
public string PluginFolderPath { get; private set; } public string PluginFolderPath { get; private set; }
@@ -315,6 +317,12 @@ public partial class PluginWindow : Window, IPluginWindow
updateTimer.Elapsed += UpdateTimer_Elapsed; updateTimer.Elapsed += UpdateTimer_Elapsed;
pluginClassInstance.Start(); pluginClassInstance.Start();
if (pluginClassInstance is AsyncPlugin asyncPluginStart)
{
pluginCancellationTokenSource?.Dispose();
pluginCancellationTokenSource = new CancellationTokenSource();
await asyncPluginStart.StartAsync(pluginCancellationTokenSource.Token);
}
UpdatePluginWindow(); UpdatePluginWindow();
await Dispatcher.InvokeAsync(ThemeChanged); await Dispatcher.InvokeAsync(ThemeChanged);
@@ -602,7 +610,8 @@ public partial class PluginWindow : Window, IPluginWindow
if (pluginClassInstance is AsyncPlugin asyncPlugin) if (pluginClassInstance is AsyncPlugin asyncPlugin)
{ {
result = await asyncPlugin.MainAsync(); CancellationToken token = pluginCancellationTokenSource?.Token ?? CancellationToken.None;
result = await asyncPlugin.MainAsync(token);
} }
else else
{ {
@@ -678,6 +687,20 @@ public partial class PluginWindow : Window, IPluginWindow
try try
{ {
pluginCancellationTokenSource?.Cancel();
if (pluginClassInstance is AsyncPlugin asyncPluginStop)
{
try
{
_ = asyncPluginStop.StopAsync(pluginCancellationTokenSource?.Token ?? CancellationToken.None);
}
catch (Exception ex)
{
App.Logger.LogError($"\"{PluginMetadata.Name}\" - {ex}", source: "Plugin");
}
}
if (pluginClassInstance is not null) if (pluginClassInstance is not null)
{ {
SaveState(pluginClassInstance); SaveState(pluginClassInstance);
@@ -690,6 +713,9 @@ public partial class PluginWindow : Window, IPluginWindow
} }
assemblyLoadContext.Unload(); assemblyLoadContext.Unload();
pluginCancellationTokenSource?.Dispose();
pluginCancellationTokenSource = null;
} }
#region Window Events #region Window Events
+14 -1
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DesktopMagic.Api; namespace DesktopMagic.Api;
@@ -15,11 +16,23 @@ namespace DesktopMagic.Api;
/// intervals, and configuration changes as needed.</remarks> /// intervals, and configuration changes as needed.</remarks>
public abstract class AsyncPlugin : Plugin public abstract class AsyncPlugin : Plugin
{ {
/// <summary>
/// Occurs once when the plugin gets activated. Override for async initialization.
/// </summary>
/// <param name="cancellationToken">Token signaled when the host requests cancellation.</param>
public virtual Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
/// <summary>
/// Occurs once when the plugin gets deactivated. Override for async cleanup.
/// </summary>
/// <param name="cancellationToken">Token signaled when the host requests cancellation.</param>
public virtual Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
/// <summary> /// <summary>
/// Occurs when the <see cref="Plugin.UpdateInterval"/> elapses. /// Occurs when the <see cref="Plugin.UpdateInterval"/> elapses.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public abstract Task<Bitmap?> MainAsync(); public abstract Task<Bitmap?> MainAsync(CancellationToken cancellationToken);
/// <summary> /// <summary>
/// This method should not be called directly! Override and use <see cref="MainAsync"/> for asynchronous plugin operations. /// This method should not be called directly! Override and use <see cref="MainAsync"/> for asynchronous plugin operations.