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.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace DesktopMagic.BuiltInPlugins;
@@ -46,7 +47,7 @@ public class WeatherPlugin : AsyncPlugin
private string lastUpdated = "";
private bool isLoading = false;
public override async void Start()
public override async Task StartAsync(CancellationToken cancellationToken)
{
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
if (!isLoading && cachedLat != null && cachedLon != null)
+27 -1
View File
@@ -37,6 +37,8 @@ public partial class PluginWindow : Window, IPluginWindow
private Plugin? pluginClassInstance;
private readonly AssemblyLoadContext assemblyLoadContext;
private CancellationTokenSource? pluginCancellationTokenSource;
public bool IsRunning { get; private set; } = true;
public PluginMetadata PluginMetadata { get; private set; }
public string PluginFolderPath { get; private set; }
@@ -315,6 +317,12 @@ public partial class PluginWindow : Window, IPluginWindow
updateTimer.Elapsed += UpdateTimer_Elapsed;
pluginClassInstance.Start();
if (pluginClassInstance is AsyncPlugin asyncPluginStart)
{
pluginCancellationTokenSource?.Dispose();
pluginCancellationTokenSource = new CancellationTokenSource();
await asyncPluginStart.StartAsync(pluginCancellationTokenSource.Token);
}
UpdatePluginWindow();
await Dispatcher.InvokeAsync(ThemeChanged);
@@ -602,7 +610,8 @@ public partial class PluginWindow : Window, IPluginWindow
if (pluginClassInstance is AsyncPlugin asyncPlugin)
{
result = await asyncPlugin.MainAsync();
CancellationToken token = pluginCancellationTokenSource?.Token ?? CancellationToken.None;
result = await asyncPlugin.MainAsync(token);
}
else
{
@@ -678,6 +687,20 @@ public partial class PluginWindow : Window, IPluginWindow
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)
{
SaveState(pluginClassInstance);
@@ -690,6 +713,9 @@ public partial class PluginWindow : Window, IPluginWindow
}
assemblyLoadContext.Unload();
pluginCancellationTokenSource?.Dispose();
pluginCancellationTokenSource = null;
}
#region Window Events
+14 -1
View File
@@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
namespace DesktopMagic.Api;
@@ -15,11 +16,23 @@ namespace DesktopMagic.Api;
/// intervals, and configuration changes as needed.</remarks>
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>
/// Occurs when the <see cref="Plugin.UpdateInterval"/> elapses.
/// </summary>
/// <returns></returns>
public abstract Task<Bitmap?> MainAsync();
public abstract Task<Bitmap?> MainAsync(CancellationToken cancellationToken);
/// <summary>
/// This method should not be called directly! Override and use <see cref="MainAsync"/> for asynchronous plugin operations.