fix(data matching): corrects cancellation on fast input changes & other things:
corrects getdata for child tasks, syncs current state, extra signalling for SetData phase
This commit is contained in:
@@ -8,6 +8,9 @@ using Timer = System.Timers.Timer;
|
||||
|
||||
namespace GrasshopperAsyncComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Inherit your component from this class to make all the async goodness available.
|
||||
/// </summary>
|
||||
public abstract class GH_AsyncComponent : GH_Component
|
||||
{
|
||||
public override Guid ComponentGuid { get => new Guid("5DBBD498-0326-4E25-83A5-424D8DC493D4"); }
|
||||
@@ -30,12 +33,23 @@ namespace GrasshopperAsyncComponent
|
||||
|
||||
int Iterations = 0;
|
||||
|
||||
public WorkerInstance BaseWorker { get; set; }
|
||||
bool SetData = false;
|
||||
|
||||
List<WorkerInstance> Workers;
|
||||
|
||||
List<Task> Tasks;
|
||||
|
||||
List<CancellationTokenSource> CancelationSources;
|
||||
|
||||
/// <summary>
|
||||
/// Set this property inside the constructor of your derived component.
|
||||
/// </summary>
|
||||
public WorkerInstance BaseWorker { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional: if you have opinions on how the default system task scheduler should treat your workers, set it here.
|
||||
/// </summary>
|
||||
public TaskCreationOptions? TaskCreationOptions { get; set; } = null;
|
||||
|
||||
protected GH_AsyncComponent(string name, string nickname, string description, string category, string subCategory) : base(name, nickname, description, category, subCategory)
|
||||
{
|
||||
@@ -61,8 +75,10 @@ namespace GrasshopperAsyncComponent
|
||||
{
|
||||
State++;
|
||||
|
||||
if (State == Iterations)
|
||||
if (State == Workers.Count)
|
||||
{
|
||||
SetData = true;
|
||||
Workers.Reverse();
|
||||
Rhino.RhinoApp.InvokeOnUiThread((Action)delegate
|
||||
{
|
||||
ExpireSolution(true);
|
||||
@@ -74,28 +90,35 @@ namespace GrasshopperAsyncComponent
|
||||
|
||||
Workers = new List<WorkerInstance>();
|
||||
CancelationSources = new List<CancellationTokenSource>();
|
||||
Tasks = new List<Task>();
|
||||
}
|
||||
|
||||
protected override void BeforeSolveInstance()
|
||||
{
|
||||
if (State != 0) return;
|
||||
if (State != 0 && SetData) return;
|
||||
|
||||
foreach (var source in CancelationSources) source.Cancel();
|
||||
|
||||
CancelationSources.Clear();
|
||||
Workers.Clear();
|
||||
Errors.Clear();
|
||||
Tasks.Clear();
|
||||
|
||||
State = 0;
|
||||
Iterations = 0;
|
||||
base.BeforeSolveInstance();
|
||||
}
|
||||
|
||||
protected override void AfterSolveInstance()
|
||||
{
|
||||
if (State == 0 && Tasks.Count > 0)
|
||||
{
|
||||
foreach (var task in Tasks) task.Start();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess DA)
|
||||
{
|
||||
if (State == 0)
|
||||
{
|
||||
Iterations++;
|
||||
if (BaseWorker == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Worker class not provided.");
|
||||
@@ -116,32 +139,50 @@ namespace GrasshopperAsyncComponent
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
CurrentWorker.CancellationToken = tokenSource.Token;
|
||||
CurrentWorker.Id = DA.Iteration.ToString();
|
||||
var CurrentRun = new Task(() => CurrentWorker.DoWork(ReportProgress, ReportError, Done), tokenSource.Token, TaskCreationOptions.LongRunning);
|
||||
|
||||
Task CurrentRun;
|
||||
if (TaskCreationOptions != null)
|
||||
{
|
||||
CurrentRun = new Task(() => CurrentWorker.DoWork(ReportProgress, ReportError, Done), tokenSource.Token, (TaskCreationOptions)TaskCreationOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentRun = new Task(() => CurrentWorker.DoWork(ReportProgress, ReportError, Done), tokenSource.Token);
|
||||
}
|
||||
// Add cancelation source to our bag
|
||||
CancelationSources.Add(tokenSource);
|
||||
|
||||
// Add the worker to our list
|
||||
Workers.Add(CurrentWorker);
|
||||
|
||||
CurrentRun.Start();
|
||||
Tasks.Add(CurrentRun);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var test = DA.Iteration;
|
||||
|
||||
Workers[DA.Iteration].SetData(DA);
|
||||
|
||||
// Note we're decrementing the state here.
|
||||
if (--State == 0)
|
||||
if (SetData)
|
||||
{
|
||||
foreach (var (message, type) in Errors)
|
||||
{
|
||||
AddRuntimeMessage(type, message);
|
||||
}
|
||||
if (Workers.Count > 0)
|
||||
Workers[--State].SetData(DA);
|
||||
|
||||
Message = "Done";
|
||||
OnDisplayExpired(true);
|
||||
Errors.Clear();
|
||||
if (State == 0)
|
||||
{
|
||||
foreach (var (message, type) in Errors)
|
||||
{
|
||||
AddRuntimeMessage(type, message);
|
||||
}
|
||||
|
||||
CancelationSources.Clear();
|
||||
Workers.Clear();
|
||||
Errors.Clear();
|
||||
Tasks.Clear();
|
||||
|
||||
SetData = false;
|
||||
|
||||
Message = "Done";
|
||||
//OnDisplayExpired(true);
|
||||
OnDisplayExpired(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace GrasshopperAsyncComponent
|
||||
|
||||
/// <summary>
|
||||
/// This method is where the actual calculation/computation/heavy lifting should be done.
|
||||
/// <b>Make sure you always check as frequently as you can if <see cref="WorkerInstance.CancellationToken"/> is cancelled. For an example, see the <see cref="GrasshopperAsyncComponent.SampleImplementations.PrimeCalculator"/>.</b>
|
||||
/// <b>Make sure you always check as frequently as you can if <see cref="WorkerInstance.CancellationToken"/> is cancelled. For an example, see the <see cref="GrasshopperAsyncComponent.SampleImplementations.PrimeCalculatorWorker"/>.</b>
|
||||
/// </summary>
|
||||
/// <param name="ReportProgress">Call this to report progress up to the parent component.</param>
|
||||
/// <param name="ReportError">Call this to report errors up to the parent component.</param>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<Compile Include="Info\GrasshopperAsyncComponentInfo.cs" />
|
||||
<Compile Include="Base\IAsyncComponentWorker.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SampleImplementations\SampleAsyncComponent.cs" />
|
||||
<Compile Include="SampleImplementations\Sample_PrimeCalculatorAsyncComponent.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
+13
-15
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace GrasshopperAsyncComponent.SampleImplementations
|
||||
{
|
||||
public class SampleAsyncComponent : GH_AsyncComponent
|
||||
public class Sample_PrimeCalculatorAsyncComponent : GH_AsyncComponent
|
||||
{
|
||||
public override Guid ComponentGuid { get => new Guid("DF2B93E2-052D-4BE4-BC62-90DC1F169BF6"); }
|
||||
|
||||
@@ -16,19 +16,19 @@ namespace GrasshopperAsyncComponent.SampleImplementations
|
||||
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
public SampleAsyncComponent() : base("Sample Async Component", "CYCLOMAXOTRON", "Meaningless labour.", "Samples", "Async")
|
||||
public Sample_PrimeCalculatorAsyncComponent() : base("Sample Async Component", "PRIME", "Calculates the nth prime number.", "Samples", "Async")
|
||||
{
|
||||
BaseWorker = new PrimeCalculator();
|
||||
BaseWorker = new PrimeCalculatorWorker();
|
||||
}
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddIntegerParameter("Max iterations", "M", "How many useless cycles should we spin. Minimum 10, maximum 1000.", GH_ParamAccess.item);
|
||||
pManager.AddIntegerParameter("N", "N", "Which n-th prime number. Minimum 1, maximum one million. Take care, it can burn your CPU.", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddTextParameter("Output", "O", "Will just say hello world after spinning.", GH_ParamAccess.item);
|
||||
pManager.AddTextParameter("Output", "O", "The n-th prime number.", GH_ParamAccess.item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace GrasshopperAsyncComponent.SampleImplementations
|
||||
}
|
||||
}
|
||||
|
||||
public class PrimeCalculator : WorkerInstance
|
||||
public class PrimeCalculatorWorker : WorkerInstance
|
||||
{
|
||||
int TehNthPrime { get; set; } = 100;
|
||||
long ThePrime { get; set; } = -1;
|
||||
@@ -120,24 +120,22 @@ namespace GrasshopperAsyncComponent.SampleImplementations
|
||||
Done();
|
||||
}
|
||||
|
||||
public override WorkerInstance Duplicate() => new PrimeCalculator();
|
||||
public override WorkerInstance Duplicate() => new PrimeCalculatorWorker();
|
||||
|
||||
public override void GetData(IGH_DataAccess DA, GH_ComponentParamServer Params)
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
int _nthPrime = 100;
|
||||
DA.GetData(0, ref _nthPrime);
|
||||
if (_nthPrime > 1000000) _nthPrime = 1000000;
|
||||
if (_nthPrime < 1) _nthPrime = 1;
|
||||
|
||||
int _maxIterations = 100;
|
||||
DA.GetData(0, ref _maxIterations);
|
||||
if (_maxIterations > 1000000) _maxIterations = 1000000;
|
||||
if (_maxIterations < 10) _maxIterations = 10;
|
||||
|
||||
TehNthPrime = _maxIterations;
|
||||
TehNthPrime = _nthPrime;
|
||||
}
|
||||
|
||||
public override void SetData(IGH_DataAccess DA)
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested) return;
|
||||
DA.SetData(0, $"Hello world. Worker {Id} has found for that the {TehNthPrime}th prime is: {ThePrime}");
|
||||
DA.SetData(0, $"W_ID {Id}: {TehNthPrime}th prime is: {ThePrime}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user