Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66a1ccb8ba | |||
| 4bb2759be9 | |||
| 0a1a3e1627 | |||
| 57c7dd0bd0 | |||
| 330a55b058 | |||
| 1b92138ce9 | |||
| 2f2be53bff | |||
| af6b381578 | |||
| a53cef31a8 | |||
| 8dc8c9a0fb | |||
| 65fab58006 | |||
| b93aa1a76a | |||
| bdc105fa95 | |||
| 59f87b2a7d | |||
| c314fc5834 |
@@ -88,7 +88,7 @@ namespace GrasshopperAsyncComponent
|
||||
|
||||
public virtual void DisplayProgress(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
if (Workers.Count == 0)
|
||||
if (Workers.Count == 0 || ProgressReports.Values.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -226,5 +226,24 @@ namespace GrasshopperAsyncComponent
|
||||
Message = "Done";
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
|
||||
public void RequestCancellation()
|
||||
{
|
||||
foreach (var source in CancellationSources)
|
||||
{
|
||||
source.Cancel();
|
||||
}
|
||||
|
||||
CancellationSources.Clear();
|
||||
Workers.Clear();
|
||||
ProgressReports.Clear();
|
||||
Tasks.Clear();
|
||||
|
||||
Interlocked.Exchange(ref State, 0);
|
||||
Interlocked.Exchange(ref SetData, 0);
|
||||
Message = "Cancelled";
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{695D2B91-DDB6-416E-8A99-DDE6253DA7AA}</ProjectGuid>
|
||||
<ProjectGuid>{114D5E49-AC13-47F7-A70E-B4289579F4E3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GrasshopperAsyncComponent</RootNamespace>
|
||||
@@ -49,11 +49,11 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grasshopper">
|
||||
<Version>6.33.20343.16431</Version>
|
||||
<Version>6.28.20199.17141</Version>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="RhinoCommon">
|
||||
<Version>6.33.20343.16431</Version>
|
||||
<Version>6.28.20199.17141</Version>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<package >
|
||||
<metadata>
|
||||
<id>GrasshopperAsyncComponent</id>
|
||||
<version>0.2.1.0</version>
|
||||
<version>1.2.3.0</version>
|
||||
<title>GrasshopperAsyncComponent</title>
|
||||
<authors>Speckle Systems</authors>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">Apache-2.0</license>
|
||||
<projectUrl>https://github.com/specklesystems/GrasshopperAsyncComponent</projectUrl>
|
||||
<description>Jankless Grasshopper Component</description>
|
||||
<releaseNotes>Hotfix for race condition preventing full component cycle.</releaseNotes>
|
||||
<releaseNotes>Adds cancellation methods.</releaseNotes>
|
||||
<copyright>Speckle Systems</copyright>
|
||||
<tags>grasshopper rhino mcneel gh_component</tags>
|
||||
</metadata>
|
||||
|
||||
@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Speckle Systems")]
|
||||
[assembly: AssemblyProduct("GrasshopperAsyncComponent")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyCopyright("Copyright AEC Systems © 2020, 2021")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyVersion("1.2.3.0")]
|
||||
[assembly: AssemblyFileVersion("1.2.3.0")]
|
||||
@@ -55,8 +55,8 @@
|
||||
<Compile Include="SampleImplementations\Sample_UslessCyclesComponent.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grasshopper" Version="6.33.20343.16431" />
|
||||
<PackageReference Include="RhinoCommon" Version="6.33.20343.16431" />
|
||||
<PackageReference Include="Grasshopper" Version="6.28.20199.17141" />
|
||||
<PackageReference Include="RhinoCommon" Version="6.28.20199.17141" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GrasshopperAsyncComponent\GrasshopperAsyncComponent.csproj">
|
||||
|
||||
+13
-4
@@ -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);
|
||||
}
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
base.AppendAdditionalMenuItems(menu);
|
||||
Menu_AppendItem(menu, "Cancel", (s, e) =>
|
||||
{
|
||||
RequestCancellation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +53,7 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations
|
||||
public override void DoWork(Action<string, double> ReportProgress, Action Done)
|
||||
{
|
||||
// 👉 Checking for cancellation!
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
if (CancellationToken.IsCancellationRequested) { 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) { 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) {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);
|
||||
}
|
||||
|
||||
+12
-2
@@ -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);
|
||||
}
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
base.AppendAdditionalMenuItems(menu);
|
||||
Menu_AppendItem(menu, "Cancel", (s, e) =>
|
||||
{
|
||||
RequestCancellation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class UselessCyclesWorker : WorkerInstance
|
||||
@@ -42,7 +52,7 @@ namespace GrasshopperAsyncComponentDemo.SampleImplementations
|
||||
public override void DoWork(Action<string, double> ReportProgress, Action Done)
|
||||
{
|
||||
// Checking for cancellation
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
if (CancellationToken.IsCancellationRequested) { 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) { return; }
|
||||
}
|
||||
|
||||
Done();
|
||||
|
||||
@@ -15,14 +15,15 @@ Looks nice, doesn't it? Notice that the solution runs "eagerly" - every time the
|
||||
|
||||
- **Grasshopper and Rhino are still responsive!**
|
||||
- **There's progress reporting!** (personally I hate waiting for Gh to unfreeze...).
|
||||
- **Thread safe**: 99% of the times this won't explode in your face. It still might though!
|
||||
|
||||
### Approach
|
||||
|
||||
Provides an abstract `GH_AsyncComponent` which you can inherit from to scaffold your own async component. There's more info in the [blogpost](https://speckle.systems/blog/async-gh/) on how to go about it.
|
||||
|
||||
Even better, there's a [sample component that shows how an implementation could look like](https://github.com/specklesystems/GrasshopperAsyncComponent/tree/main/GrasshopperAsyncComponent/SampleImplementations)! There's two components in that folder:
|
||||
- The Useless Spinner: does no meaningfull CPU work, just keeps a thread busy with SpinWait()
|
||||
- The N-th Prime Calculator: can actually spin your computer's fans quite a bit (for numbers > 100.000)
|
||||
> #### Checkout the sample implementation!
|
||||
> - [Prime number calculator](https://github.com/specklesystems/GrasshopperAsyncComponent/blob/a53cef31a8750a18d06fad0f41b2dc452fdc253b/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_PrimeCalculatorAsyncComponent.cs#L11-L53) Calculates the n'th prime. Can actually spin your computer's fans quite a bit for numbers > 100.000!
|
||||
> - [Usless spinner](https://github.com/specklesystems/GrasshopperAsyncComponent/blob/2f2be53bffd2402337ba40d65bb5b619d1161b3e/GrasshopperAsyncComponentDemo/SampleImplementations/Sample_UslessCyclesComponent.cs#L13-L91) does no meaningfull CPU work, just keeps a thread busy with SpinWait().
|
||||
|
||||
### Current limitations
|
||||
|
||||
@@ -46,6 +47,25 @@ Q: Does this component use all my cores? A: OH YES. It goes WROOOM.
|
||||
|
||||

|
||||
|
||||
Q: Can I enable cancellation of a longer running task?
|
||||
|
||||
A: Yes, now you can! In your component, just add a right click menu action like so:
|
||||
|
||||
```cs
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
base.AppendAdditionalMenuItems(menu);
|
||||
Menu_AppendItem(menu, "Cancel", (s, e) =>
|
||||
{
|
||||
RequestCancellation();
|
||||
});
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Debugging
|
||||
|
||||
Quite easy:
|
||||
|
||||
Reference in New Issue
Block a user