From c314fc58344d7ae84f854ad1f439ff78d578bb59 Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Sat, 3 Apr 2021 17:10:36 +0100 Subject: [PATCH] feat(cancellation): exposes task cancellation method --- GrasshopperAsyncComponent/GH_AsyncComponent.cs | 18 ++++++++++++++++++ .../GrasshopperAsyncComponent.csproj | 2 +- .../Sample_PrimeCalculatorAsyncComponent.cs | 17 +++++++++++++---- .../Sample_UslessCyclesComponent.cs | 14 ++++++++++++-- README.md | 17 +++++++++++++++++ 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/GrasshopperAsyncComponent/GH_AsyncComponent.cs b/GrasshopperAsyncComponent/GH_AsyncComponent.cs index 33179f9..6caf97e 100755 --- a/GrasshopperAsyncComponent/GH_AsyncComponent.cs +++ b/GrasshopperAsyncComponent/GH_AsyncComponent.cs @@ -86,6 +86,24 @@ namespace GrasshopperAsyncComponent Tasks = new List(); } + public void RequestCancellation() + { + foreach(var token in CancellationSources) + { + token.Cancel(); + } + + CancellationSources.Clear(); + Workers.Clear(); + ProgressReports.Clear(); + Tasks.Clear(); + + Interlocked.Exchange(ref SetData, 0); + + Message = "Done"; + OnDisplayExpired(true); + } + public virtual void DisplayProgress(object sender, System.Timers.ElapsedEventArgs e) { if (Workers.Count == 0) diff --git a/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj b/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj index 8a8db8d..c48c592 100755 --- a/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj +++ b/GrasshopperAsyncComponent/GrasshopperAsyncComponent.csproj @@ -4,7 +4,7 @@ Debug AnyCPU - {695D2B91-DDB6-416E-8A99-DDE6253DA7AA} + {114D5E49-AC13-47F7-A70E-B4289579F4E3} Library Properties GrasshopperAsyncComponent diff --git a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs index 0cd09e1..b916f96 100755 --- a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs +++ b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using GrasshopperAsyncComponent; +using System.Windows.Forms; namespace GrasshopperAsyncComponentDemo.SampleImplementations { @@ -30,7 +31,15 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations protected override void RegisterOutputParams(GH_OutputParamManager pManager) { pManager.AddNumberParameter("Output", "O", "The n-th prime number.", GH_ParamAccess.item); + } + protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu) + { + base.AppendAdditionalComponentMenuItems(menu); + Menu_AppendItem(menu, "Cancel", (s, e) => + { + this.RequestCancellation(); + }); } } @@ -44,7 +53,7 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations public override void DoWork(Action ReportProgress, Action Done) { // 👉 Checking for cancellation! - if (CancellationToken.IsCancellationRequested) return; + if (CancellationToken.IsCancellationRequested) { Done(); return; } int count = 0; long a = 2; @@ -53,14 +62,14 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations while (count < TheNthPrime) { // 👉 Checking for cancellation! - if (CancellationToken.IsCancellationRequested) return; + if (CancellationToken.IsCancellationRequested) { Done(); return; } long b = 2; int prime = 1;// to check if found a prime while (b * b <= a) { // 👉 Checking for cancellation! - if (CancellationToken.IsCancellationRequested) return; + if (CancellationToken.IsCancellationRequested) { Done(); return; } if (a % b == 0) { @@ -98,7 +107,7 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations public override void SetData(IGH_DataAccess DA) { // 👉 Checking for cancellation! - if (CancellationToken.IsCancellationRequested) return; + if (CancellationToken.IsCancellationRequested) { return; } DA.SetData(0, ThePrime); } diff --git a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs index b204825..2f63da4 100755 --- a/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs +++ b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using GrasshopperAsyncComponent; +using System.Windows.Forms; namespace GrasshopperAsyncComponentDemo.SampleImplementations { @@ -31,6 +32,15 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations { pManager.AddTextParameter("Output", "O", "Nothing really interesting.", GH_ParamAccess.item); } + + protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu) + { + base.AppendAdditionalComponentMenuItems(menu); + Menu_AppendItem(menu, "Cancel", (s, e) => + { + this.RequestCancellation(); + }); + } } public class UselessCyclesWorker : WorkerInstance @@ -42,7 +52,7 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations public override void DoWork(Action ReportProgress, Action Done) { // Checking for cancellation - if (CancellationToken.IsCancellationRequested) return; + if (CancellationToken.IsCancellationRequested) { Done(); return; } for (int i = 0; i <= MaxIterations; i++) { @@ -53,7 +63,7 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations ReportProgress(Id, ((double)(i + 1) / (double)MaxIterations)); // Checking for cancellation - if (CancellationToken.IsCancellationRequested) return; + if (CancellationToken.IsCancellationRequested) { Done(); return; } } Done(); diff --git a/README.md b/README.md index 28a42c8..664f5a1 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,23 @@ Q: Does this component use all my cores? A: OH YES. It goes WROOOM. ![image](https://user-images.githubusercontent.com/7696515/95597125-29310900-0a46-11eb-99ce-663b34506a7a.png) +Q: Can I cancel a stuff that's in progress? + +A: Yes. The `GH_AsyncComponent` class now exposes the `RequestCancellation()` method, which you can invoke from a custom menu action, or however you want. Note, to properly handle this and ensure the component's inner flow state is properly reset, when respecting the cancellation in your component, you should call the `Done()` function before returning from `DoWork()`. E.g.: + +```cs + +public override void DoWork(Action ReportProgress, Action Done) +{ + // note: call done from inside DoWork(), then return abruptly. + if (CancellationToken.IsCancellationRequested) { Done(); return; } + // more code +} + +``` + + + ### Debugging Quite easy: