using Nessos.Effects.Handlers;
using System.Diagnostics;
using System.Threading.Tasks;
async Eff ResumableWorkflow()
for (int i = 0; i < 20; i++)
Console.WriteLine($"Iterate {i}");
await SuspendEffect.Value;
var handler = new ResumableEffectHandler(ResumableWorkflow());
Console.WriteLine("Starting");
while (! await handler.TryResume())
Console.WriteLine("Workflow suspended");
Console.WriteLine("Done");
public class SuspendEffect : Effect
public static SuspendEffect Value { get; } = new();
public class ResumableEffectHandler : IEffectHandler
private readonly EffStateMachine<Unit> _stateMachine;
private EffectAwaiter? _suspendedAwaiter;
public ResumableEffectHandler(Eff suspendableWorkflow)
_stateMachine = Eff.FromUntypedEff(suspendableWorkflow).GetStateMachine();
public bool IsSuspended => _suspendedAwaiter != null;
public async ValueTask<bool> TryResume()
if (_suspendedAwaiter != null)
_suspendedAwaiter.SetResult();
_suspendedAwaiter = null;
await ((IEffectHandler)this).Handle(_stateMachine).ConfigureAwait(false);
return _suspendedAwaiter == null;
ValueTask IEffectHandler.Handle<TResult>(EffectAwaiter<TResult> awaiter)
Debug.Assert(_suspendedAwaiter == null);
if (awaiter is EffectAwaiter { Effect: SuspendEffect } suspendedAwaiter)
_suspendedAwaiter = suspendedAwaiter;
async ValueTask IEffectHandler.Handle<TResult>(EffStateMachine<TResult> stateMachine)
Debug.Assert(_suspendedAwaiter == null);
while (_suspendedAwaiter == null)
switch (stateMachine.Position)
case StateMachinePosition.Result:
case StateMachinePosition.Exception:
case StateMachinePosition.TaskAwaiter:
await stateMachine.TaskAwaiter!.Value.ConfigureAwait(false);
case StateMachinePosition.EffAwaiter:
await stateMachine.EffAwaiter!.Accept(this).ConfigureAwait(false);
Debug.Fail($"Invalid state machine position {stateMachine.Position}.");