Skip to content

Commit

Permalink
Several fixes
Browse files Browse the repository at this point in the history
- Cancelling of engine's main loop
- Update() method on context values
- Scheduling of startup actions moved to IHostApplicationLifetime
- Activity's exception is now breaking execution - dev experience!
  • Loading branch information
mikolaj-milewski committed Aug 27, 2024
1 parent 12fbdf3 commit 9ecc71b
Show file tree
Hide file tree
Showing 24 changed files with 369 additions and 175 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Stateflows\Stateflows.csproj" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions Core/Stateflows/Activities/ActivitiesDependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Stateflows.Activities.EventHandlers;
using Stateflows.Activities.Registration.Builders;
using Stateflows.Activities.Registration.Interfaces;
using Stateflows.Common.Initializer;

namespace Stateflows.Activities
{
Expand All @@ -26,6 +27,11 @@ public static IStateflowsBuilder AddActivities(this IStateflowsBuilder stateflow
return stateflowsBuilder;
}

[DebuggerHidden]
public static IStateflowsBuilder AddDefaultInstance<TActivity>(this IStateflowsBuilder stateflowsBuilder, DefaultInstanceInitializationRequestFactoryAsync initializationRequestFactoryAsync = null)
where TActivity : class, IActivity
=> stateflowsBuilder.AddDefaultInstance(new StateMachineClass(Activity<TActivity>.Name).BehaviorClass, initializationRequestFactoryAsync);

private static ActivitiesRegister EnsureActivitiesServices(this IStateflowsBuilder stateflowsBuilder)
{
if (!Registers.TryGetValue(stateflowsBuilder, out var register))
Expand Down
38 changes: 34 additions & 4 deletions Core/Stateflows/Activities/Engine/Executor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Stateflows.Activities.Streams;
using Stateflows.Activities.Registration;
using Stateflows.Activities.Context.Classes;
using Stateflows.Common.Exceptions;

namespace Stateflows.Activities.Engine
{
Expand Down Expand Up @@ -335,6 +336,21 @@ public async Task DoFinalizeAsync(IEnumerable<object> outputTokens = null)
await Inspector.BeforeActivityFinalizationAsync(context);

await Graph.Finalize.WhenAll(context);
try
{
await Graph.Finalize.WhenAll(context);
}
catch (Exception e)
{
if (!await Inspector.OnActivityFinalizationExceptionAsync(context, e))
{
throw;
}
else
{
throw new ExecutionException(e);
}
}

await Inspector.AfterActivityFinalizationAsync(context);

Expand Down Expand Up @@ -541,7 +557,21 @@ public async Task<InitializationStatus> DoInitializeActivityAsync(ActivityInitia
}
catch (Exception e)
{
await Inspector.OnActivityInitializationExceptionAsync(context, context.InitializationEvent, e);
if (e is StateflowsException)
{
throw;
}
else
{
if (!await Inspector.OnActivityInitializationExceptionAsync(context, context.InitializationEvent, e))
{
throw;
}
else
{
throw new ExecutionException(e);
}
}

result = InitializationStatus.NotInitialized;

Check warning on line 576 in Core/Stateflows/Activities/Engine/Executor.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected
}
Expand All @@ -563,7 +593,7 @@ public async Task<InitializationStatus> DoInitializeActivityAsync(ActivityInitia
return result;
}

public async Task<IEnumerable<TokenHolder>> HandleExceptionAsync(Node node, Exception exception, BaseContext context)
public async Task<bool> HandleExceptionAsync(Node node, Exception exception, BaseContext context)
{
Node handler = null;
var currentNode = node;
Expand Down Expand Up @@ -615,10 +645,10 @@ public async Task<IEnumerable<TokenHolder>> HandleExceptionAsync(Node node, Exce

ReportExceptionHandled(node, exceptionName, exceptionContext.OutputTokens.Where(t => t is TokenHolder<ControlToken>).ToArray(), Context);

return exceptionContext.OutputTokens;
return true;
}

return new TokenHolder[0];
return false;
}

public async Task DoHandleNodeAsync(Node node, Edge upstreamEdge, NodeScope nodeScope, IEnumerable<TokenHolder> input = null, IEnumerable<TokenHolder> selectionTokens = null)
Expand Down
63 changes: 40 additions & 23 deletions Core/Stateflows/Activities/Engine/Inspector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,82 +195,99 @@ public async Task AfterNodeActivateAsync(ActionContext context)
await Plugins.RunSafe(p => p.AfterNodeActivateAsync(context), nameof(AfterNodeActivateAsync), Logger);
}

public async Task OnActivityInitializationExceptionAsync(BaseContext context, Event initializationEvent, Exception exception)
private static bool ShouldPropagateException(Graph graph, bool handled)
=> !handled;

public async Task<bool> OnActivityInitializationExceptionAsync(BaseContext context, Event initializationEvent, Exception exception)
{
var exceptionContext = new ActivityInitializationContext(context, initializationEvent, null);
await ExceptionHandlers.RunSafe(h => h.OnActivityInitializationExceptionAsync(exceptionContext, exception), nameof(OnActivityInitializationExceptionAsync), Logger);
var handled = await ExceptionHandlers.RunSafe(h => h.OnActivityInitializationExceptionAsync(exceptionContext, exception), nameof(OnActivityInitializationExceptionAsync), Logger, false);
await Inspectors.RunSafe(i => i.OnActivityInitializationExceptionAsync(exceptionContext, exception), nameof(OnActivityInitializationExceptionAsync), Logger);

if (!ExceptionHandlers.Any())
if (ShouldPropagateException(context.Context.Executor.Graph, handled))
{
context.Context.Exceptions.Add(exception);
}

return handled;
}

public async Task OnActivityFinalizationExceptionAsync(ActivityActionContext context, Exception exception)
public async Task<bool> OnActivityFinalizationExceptionAsync(ActivityActionContext context, Exception exception)
{
await ExceptionHandlers.RunSafe(h => h.OnActivityFinalizationExceptionAsync(context, exception), nameof(OnActivityFinalizationExceptionAsync), Logger);
var handled = await ExceptionHandlers.RunSafe(h => h.OnActivityFinalizationExceptionAsync(context, exception), nameof(OnActivityFinalizationExceptionAsync), Logger, false);
await Inspectors.RunSafe(i => i.OnActivityFinalizationExceptionAsync(context, exception), nameof(OnActivityFinalizationExceptionAsync), Logger);

if (!ExceptionHandlers.Any())
if (ShouldPropagateException(context.Context.Executor.Graph, handled))
{
context.Context.Exceptions.Add(exception);
}

return handled;
}

public async Task OnNodeInitializationExceptionAsync(ActivityNodeContext context, Exception exception)
public async Task<bool> OnNodeInitializationExceptionAsync(ActivityNodeContext context, Exception exception)
{
await ExceptionHandlers.RunSafe(h => h.OnNodeInitializationExceptionAsync(context, exception), nameof(OnNodeInitializationExceptionAsync), Logger);
var handled = await ExceptionHandlers.RunSafe(h => h.OnNodeInitializationExceptionAsync(context, exception), nameof(OnNodeInitializationExceptionAsync), Logger, false);
await Inspectors.RunSafe(i => i.OnNodeInitializationExceptionAsync(context, exception), nameof(OnNodeInitializationExceptionAsync), Logger);

if (!ExceptionHandlers.Any())
if (ShouldPropagateException(context.Context.Executor.Graph, handled))
{
context.Context.Exceptions.Add(exception);
}

return handled;
}

public async Task OnNodeFinalizationExceptionAsync(ActivityNodeContext context, Exception exception)
public async Task<bool> OnNodeFinalizationExceptionAsync(ActivityNodeContext context, Exception exception)
{
await ExceptionHandlers.RunSafe(h => h.OnNodeFinalizationExceptionAsync(context, exception), nameof(OnNodeFinalizationExceptionAsync), Logger);
var handled = await ExceptionHandlers.RunSafe(h => h.OnNodeFinalizationExceptionAsync(context, exception), nameof(OnNodeFinalizationExceptionAsync), Logger, false);
await Inspectors.RunSafe(i => i.OnNodeFinalizationExceptionAsync(context, exception), nameof(OnNodeFinalizationExceptionAsync), Logger);

if (!ExceptionHandlers.Any())
if (ShouldPropagateException(context.Context.Executor.Graph, handled))
{
context.Context.Exceptions.Add(exception);
}

return handled;
}

public async Task OnNodeExecutionExceptionAsync(ActivityNodeContext context, Exception exception)
public async Task<bool> OnNodeExecutionExceptionAsync(ActivityNodeContext context, Exception exception)
{
await ExceptionHandlers.RunSafe(h => h.OnNodeExecutionExceptionAsync(context, exception), nameof(OnNodeExecutionExceptionAsync), Logger);
var handled = await ExceptionHandlers.RunSafe(h => h.OnNodeExecutionExceptionAsync(context, exception), nameof(OnNodeExecutionExceptionAsync), Logger, false);
await Inspectors.RunSafe(i => i.OnNodeExecutionExceptionAsync(context, exception), nameof(OnNodeExecutionExceptionAsync), Logger);

if (!ExceptionHandlers.Any())
if (ShouldPropagateException(context.Context.Executor.Graph, handled))
{
context.Context.Exceptions.Add(exception);
}

return handled;
}

public async Task OnFlowGuardExceptionAsync(IGuardInspectionContext context, Exception exception)
public async Task<bool> OnFlowGuardExceptionAsync(IGuardInspectionContext context, Exception exception)
{
await ExceptionHandlers.RunSafe(h => h.OnFlowGuardExceptionAsync(context, exception), nameof(OnFlowGuardExceptionAsync), Logger);
var handled = await ExceptionHandlers.RunSafe(h => h.OnFlowGuardExceptionAsync(context, exception), nameof(OnFlowGuardExceptionAsync), Logger, false);
await Inspectors.RunSafe(i => i.OnFlowGuardExceptionAsync(context, exception), nameof(OnFlowGuardExceptionAsync), Logger);

//if (!ExceptionHandlers.Any())
//if (ShouldPropagateException(context.Context.Executor.Graph, handled))
//{
// context.Exceptions.Add(exception);
// context.Context.Exceptions.Add(exception);
//}

return handled;
}

public async Task OnFlowTransformationExceptionAsync(ITransformationInspectionContext context, Exception exception)
public async Task<bool> OnFlowTransformationExceptionAsync(ITransformationInspectionContext context, Exception exception)
{
await ExceptionHandlers.RunSafe(h => h.OnFlowTransformationExceptionAsync(context, exception), nameof(OnFlowTransformationExceptionAsync), Logger);
var handled = await ExceptionHandlers.RunSafe(h => h.OnFlowTransformationExceptionAsync(context, exception), nameof(OnFlowTransformationExceptionAsync), Logger, false);
await Inspectors.RunSafe(i => i.OnFlowTransformationExceptionAsync(context, exception), nameof(OnFlowTransformationExceptionAsync), Logger);

//if (!ExceptionHandlers.Any())
//if (ShouldPropagateException(context.Context.Executor.Graph, handled))
//{
// context.Exceptions.Add(exception);
// context.Context.Exceptions.Add(exception);
//}

return handled;
}
}
}
28 changes: 14 additions & 14 deletions Core/Stateflows/Activities/Interfaces/IActivityExceptionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ namespace Stateflows.Activities
{
public interface IActivityExceptionHandler
{
Task OnActivityInitializationExceptionAsync(IActivityInitializationContext context, Exception exception)
=> Task.CompletedTask;
Task<bool> OnActivityInitializationExceptionAsync(IActivityInitializationContext context, Exception exception)
=> Task.FromResult(false);

Task OnActivityFinalizationExceptionAsync(IActivityFinalizationContext context, Exception exception)
=> Task.CompletedTask;
Task<bool> OnActivityFinalizationExceptionAsync(IActivityFinalizationContext context, Exception exception)
=> Task.FromResult(false);

Task OnNodeInitializationExceptionAsync(IActivityNodeContext context, Exception exception)
=> Task.CompletedTask;
Task<bool> OnNodeInitializationExceptionAsync(IActivityNodeContext context, Exception exception)
=> Task.FromResult(false);

Task OnNodeFinalizationExceptionAsync(IActivityNodeContext context, Exception exception)
=> Task.CompletedTask;
Task<bool> OnNodeFinalizationExceptionAsync(IActivityNodeContext context, Exception exception)
=> Task.FromResult(false);

Task OnNodeExecutionExceptionAsync(IActivityNodeContext context, Exception exception)
=> Task.CompletedTask;
Task<bool> OnNodeExecutionExceptionAsync(IActivityNodeContext context, Exception exception)
=> Task.FromResult(false);

Task OnFlowGuardExceptionAsync<TToken>(IGuardContext<TToken> context, Exception exception)
=> Task.CompletedTask;
Task<bool> OnFlowGuardExceptionAsync<TToken>(IGuardContext<TToken> context, Exception exception)
=> Task.FromResult(false);

Task OnFlowTransformationExceptionAsync<TToken>(ITransformationContext<TToken> context, Exception exception)
=> Task.CompletedTask;
Task<bool> OnFlowTransformationExceptionAsync<TToken>(ITransformationContext<TToken> context, Exception exception)
=> Task.FromResult(false);

}
}
6 changes: 3 additions & 3 deletions Core/Stateflows/Activities/Models/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public IEnumerable<Node> ExceptionHandlers
.Select(e => e.Target)
.Where(n => n.Type == NodeType.ExceptionHandler);

public async Task<IEnumerable<object>> HandleExceptionAsync(Exception exception, BaseContext context)
public async Task<bool> HandleExceptionAsync(Exception exception, BaseContext context)
{
Node handler = null;
var currentNode = this;
Expand Down Expand Up @@ -205,10 +205,10 @@ public async Task<IEnumerable<object>> HandleExceptionAsync(Exception exception,

await handler.Action.WhenAll(exceptionContext);

return exceptionContext.OutputTokens;
return true;
}

return new TokenHolder[0];
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Stateflows.Activities.Registration.Extensions;
using Stateflows.Activities.Registration.Interfaces;
using Stateflows.Activities.Registration.Interfaces.Base;
using Stateflows.Common.Exceptions;

namespace Stateflows.Activities.Registration.Builders
{
Expand Down Expand Up @@ -91,7 +92,22 @@ public IActivityBuilder AddInitializer<TInitializationEvent>(Func<IActivityIniti
}
catch (Exception e)
{
await c.Context.Executor.Inspector.OnActivityInitializationExceptionAsync(context, context.InitializationEvent, e);
if (e is StateflowsException)
{
throw;
}
else
{
if (!await c.Context.Executor.Inspector.OnActivityInitializationExceptionAsync(context, context.InitializationEvent, e))
{
throw;
}
else
{
throw new ExecutionException(e);
}
}

result = false;

Check warning on line 111 in Core/Stateflows/Activities/Registration/Builders/ActivityBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Unreachable code detected
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Stateflows.Activities.Context.Interfaces;
using Stateflows.Activities.Registration.Builders;
using Stateflows.Activities.Registration.Interfaces;
using Stateflows.Common.Exceptions;

namespace Stateflows.Activities.Registration
{
Expand Down Expand Up @@ -128,7 +129,17 @@ public BaseActivityBuilder AddNode(NodeType type, string nodeName, ActionDelegat
}
catch (Exception e)
{
await (c as BaseContext).Context.Executor.HandleExceptionAsync(node, e, c as BaseContext);
if (e is StateflowsException)
{
throw;
}
else
{
if (!await (c as BaseContext).Context.Executor.HandleExceptionAsync(node, e, c as BaseContext))
{
throw;
}
}
}
}
);
Expand Down Expand Up @@ -317,7 +328,21 @@ public BaseActivityBuilder AddOnFinalize(Func<IActivityNodeContext, Task> action
}
catch (Exception e)
{
await context.Context.Executor.Inspector.OnNodeFinalizationExceptionAsync(context, e);
if (e is StateflowsException)
{
throw;
}
else
{
if (!await context.Context.Executor.Inspector.OnNodeFinalizationExceptionAsync(context, e))
{
throw;
}
else
{
throw new ExecutionException(e);
}
}
}
});

Expand All @@ -337,7 +362,21 @@ public BaseActivityBuilder AddOnInitialize(Func<IActivityNodeContext, Task> acti
}
catch (Exception e)
{
await context.Context.Executor.Inspector.OnNodeInitializationExceptionAsync(context, e);
if (e is StateflowsException)
{
throw;
}
else
{
if (!await context.Context.Executor.Inspector.OnNodeInitializationExceptionAsync(context, e))
{
throw;
}
else
{
throw new ExecutionException(e);
}
}
}
});

Expand Down
Loading

0 comments on commit 9ecc71b

Please sign in to comment.