Skip to content

Commit

Permalink
After an actor is stopped, block continuations from running (asynkron…
Browse files Browse the repository at this point in the history
…#2146)

* After an actor is stopped, block continuations from running

* Additionally check the actors cancellation token before sending the Continuation
  • Loading branch information
benbenwilde authored Jan 23, 2025
1 parent 2a2d747 commit 54eef66
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/Proto.Actor/Context/ActorContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ static async Task Continue(Task target, Continuation cont, IContext ctx)
// ignored
}

if (!ctx.System.Shutdown.IsCancellationRequested)
if (!ctx.CancellationToken.IsCancellationRequested && !ctx.System.Shutdown.IsCancellationRequested)
{
ctx.Self.SendSystemMessage(ctx.System, cont);
}
Expand All @@ -621,7 +621,7 @@ private async Task HandleContinuation(Continuation cont)
// Don't execute the continuation if the actor instance changed.
// Without this, Continuation's Action closure would execute with
// an older Actor instance.
if (cont.Actor != Actor && cont is not { Actor: null })
if (_state == ContextState.Stopped || System.Shutdown.IsCancellationRequested || (cont.Actor != Actor && cont is not { Actor: null }))
{
Logger.DroppingContinuation(Self, MessageEnvelope.UnwrapMessage(cont.Message));

Expand Down
43 changes: 43 additions & 0 deletions tests/Proto.Actor.Tests/ReenterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,49 @@ public async Task DropReenterContinuationAfterRestart()
var res = await Context.RequestAsync<bool>(pid, "waitstate", TimeSpan.FromSeconds(5));
Assert.True(res);
}

[Fact]
public async Task DropReenterContinuationAfterStop()
{
var stopped = false;
var completionExecuted = false;
CancellationTokenSource cts = new();

var props = Props.FromFunc(async ctx =>
{
switch (ctx.Message)
{
case "start":

ctx.ReenterAfter(
Task.Delay(-1, cts.Token),
() => { completionExecuted = true; });

ctx.Stop(ctx.Self);

// Release the cancellation token after stop gets processed.
cts.Cancel();

ctx.Respond(true);

break;
case Stopped:
stopped = true;

break;
}
}
);

var pid = Context.Spawn(props);

await Context.RequestAsync<bool>(pid, "start", TimeSpan.FromSeconds(5));

await Task.Delay(500);
await Task.Yield();

Assert.True(!completionExecuted);
}

private class ReenterAfterCancellationActor : IActor
{
Expand Down

0 comments on commit 54eef66

Please sign in to comment.