From e64d2e08a69229c4a5fcae8fe8da440d2dab53f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vondra?= Date: Wed, 17 Jul 2024 16:09:18 +0200 Subject: [PATCH 1/3] convert Failure to exception when using Ask instead of returning the Failure object itself as a result --- src/core/Akka.Tests/Actor/AskSpec.cs | 17 +++++++++++++++++ src/core/Akka/Actor/ActorRef.cs | 12 ++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/core/Akka.Tests/Actor/AskSpec.cs b/src/core/Akka.Tests/Actor/AskSpec.cs index d6389f71c4d..7935fbf461d 100644 --- a/src/core/Akka.Tests/Actor/AskSpec.cs +++ b/src/core/Akka.Tests/Actor/AskSpec.cs @@ -282,6 +282,23 @@ public async Task Bugfix5204_should_allow_null_response_without_error() resp.Should().BeNullOrEmpty(); } + /// + /// Reproduction for https://github.com/akkadotnet/akka.net/issues/7254 + /// + [Fact] + public async Task Bugfix7254_should_throw_error_when_expecting_object_type() + { + const string textExceptionMessage = "THIS IS TEST"; + + var actor = Sys.ActorOf(act => act.ReceiveAny((_, context) => + { + context.Sender.Tell(new Status.Failure(new Exception(textExceptionMessage))); + })); + + var ex = await Assert.ThrowsAsync(async () => await actor.Ask("answer")); + ex.Message.ShouldBe(textExceptionMessage); + } + [Fact] public void AskDoesNotDeadlockWhenWaitForResultInGuiApplication() { diff --git a/src/core/Akka/Actor/ActorRef.cs b/src/core/Akka/Actor/ActorRef.cs index 8be973a515d..104b9b179de 100644 --- a/src/core/Akka/Actor/ActorRef.cs +++ b/src/core/Akka/Actor/ActorRef.cs @@ -113,12 +113,6 @@ protected override void TellInternal(object message, IActorRef sender) case ISystemMessage msg: handled = _result.TrySetException(new InvalidOperationException($"system message of type '{msg.GetType().Name}' is invalid for {nameof(FutureActorRef)}")); break; - case T t: - handled = _result.TrySetResult(t); - break; - case null: - handled = _result.TrySetResult(default); - break; case Status.Failure f: handled = _result.TrySetException(f.Cause ?? new TaskCanceledException("Task cancelled by actor via Failure message.")); @@ -130,6 +124,12 @@ protected override void TellInternal(object message, IActorRef sender) ?? new TaskCanceledException("Task cancelled by actor via Failure message.")); #pragma warning restore CS0618 break; + case T t: + handled = _result.TrySetResult(t); + break; + case null: + handled = _result.TrySetResult(default); + break; default: _ = _result.TrySetException(new ArgumentException( $"Received message of type [{message.GetType()}] - Ask expected message of type [{typeof(T)}]")); From 9f7f91b439f273e959549483b5e05d18230a69c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vondra?= Date: Mon, 11 Nov 2024 10:40:44 +0100 Subject: [PATCH 2/3] support Ask-ing for failure because it promothes hypertrophy --- src/core/Akka/Actor/ActorRef.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Akka/Actor/ActorRef.cs b/src/core/Akka/Actor/ActorRef.cs index 104b9b179de..fda9af4a400 100644 --- a/src/core/Akka/Actor/ActorRef.cs +++ b/src/core/Akka/Actor/ActorRef.cs @@ -113,13 +113,13 @@ protected override void TellInternal(object message, IActorRef sender) case ISystemMessage msg: handled = _result.TrySetException(new InvalidOperationException($"system message of type '{msg.GetType().Name}' is invalid for {nameof(FutureActorRef)}")); break; - case Status.Failure f: + case Status.Failure f when typeof(T) != typeof(Status.Failure): handled = _result.TrySetException(f.Cause ?? new TaskCanceledException("Task cancelled by actor via Failure message.")); break; #pragma warning disable CS0618 // for backwards compatibility - case Failure f: + case Failure f when !typeof(Failure).IsAssignableFrom(typeof(T)): handled = _result.TrySetException(f.Exception ?? new TaskCanceledException("Task cancelled by actor via Failure message.")); #pragma warning restore CS0618 From cc256aa73f8ea3910544040a7764d724815324d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vondra?= Date: Mon, 11 Nov 2024 15:17:49 +0100 Subject: [PATCH 3/3] fixed test that expects Status.Failure to expect an exception instead --- src/core/Akka.Tests/Actor/InboxSpec.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/Akka.Tests/Actor/InboxSpec.cs b/src/core/Akka.Tests/Actor/InboxSpec.cs index b1a19321f1d..7abbc4c94dd 100644 --- a/src/core/Akka.Tests/Actor/InboxSpec.cs +++ b/src/core/Akka.Tests/Actor/InboxSpec.cs @@ -157,9 +157,7 @@ public void Select_WithClient_should_update_Client_and_copy_the_rest_of_the_prop public async Task Inbox_Receive_will_timeout_gracefully_if_timeout_is_already_expired() { var task = _inbox.ReceiveAsync(TimeSpan.FromSeconds(-1)); - Assert.True(await task.AwaitWithTimeout(TimeSpan.FromMilliseconds(1000)), "Receive did not complete in time."); - Assert.IsType(task.Result); + await Assert.ThrowsAnyAsync(() => task.AwaitWithTimeout(TimeSpan.FromMilliseconds(1000))); } } } -