This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new bafe8a944d Fix timer current message for supervision #3265 (#3294)
bafe8a944d is described below

commit bafe8a944d5bf1793159cc96799f7b8f4413a92c
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Sun Jul 5 21:38:48 2026 +0800

    Fix timer current message for supervision #3265 (#3294)
    
    Motivation:
    Timer-triggered failures in non-stash actors exposed the internal TimerMsg 
wrapper to preRestart instead of the user message delivered to receive.
    
    Modification:
    Rewrite ActorCell.currentMessage to the unwrapped timer message for all 
Timers actors and add a regression test for non-stash timer supervision.
    
    Result:
    preRestart observes the same unwrapped user message that receive processed, 
while existing timer and stash behavior remains covered.
    
    Tests:
    - sbt "actor-tests / Test / testOnly 
org.apache.pekko.actor.TimersAndSupervisionSpec 
org.apache.pekko.actor.TimersAndStashSpec org.apache.pekko.actor.TimerSpec" - 
passed
    - sbt "actor-tests / Test / testOnly org.apache.pekko.actor.FsmTimerSpec" - 
passed
    - sbt "actor-tests / Test / testOnly 
org.apache.pekko.actor.TimersAndSupervisionSpec 
org.apache.pekko.actor.TimersAndStashSpec" - passed
    - scalafmt --mode diff-ref=origin/main - passed with JDK Unsafe deprecation 
warnings
    - scalafmt --list --mode diff-ref=origin/main - passed with JDK Unsafe 
deprecation warnings
    - git diff --check - passed
    - qodercli final diff review - No must-fix findings
    
    References:
    Fixes #3265
---
 .../scala/org/apache/pekko/actor/TimerSpec.scala   | 40 ++++++++++++++++++++++
 .../main/scala/org/apache/pekko/actor/Timers.scala | 11 ++----
 2 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/actor-tests/src/test/scala/org/apache/pekko/actor/TimerSpec.scala 
b/actor-tests/src/test/scala/org/apache/pekko/actor/TimerSpec.scala
index 85e67b77ec..ec02dd80f2 100644
--- a/actor-tests/src/test/scala/org/apache/pekko/actor/TimerSpec.scala
+++ b/actor-tests/src/test/scala/org/apache/pekko/actor/TimerSpec.scala
@@ -321,6 +321,46 @@ object TimersAndStashSpec {
   case object StopStashing
 
 }
+
+object TimersAndSupervisionSpec {
+
+  case object StartTimer
+  case object Scheduled
+
+  class FailingTimerActor(probe: ActorRef) extends Actor with Timers {
+    override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
+      probe ! message
+      super.preRestart(reason, message)
+    }
+
+    def receive: Receive = {
+      case StartTimer => timers.startSingleTimer("key", Scheduled, 10.millis)
+      case Scheduled  => throw new TimerSpec.Exc
+    }
+  }
+
+}
+
+class TimersAndSupervisionSpec extends PekkoSpec {
+  import TimersAndSupervisionSpec._
+
+  "Timers combined with supervision" should {
+
+    "pass the unwrapped timer message to preRestart for non-stash actors" 
taggedAs TimingTest in {
+      val probe = TestProbe()
+      val actor = system.actorOf(Props(new FailingTimerActor(probe.ref)))
+
+      actor ! StartTimer
+
+      probe.expectMsg(Some(Scheduled))
+      watch(actor)
+      system.stop(actor)
+      expectTerminated(actor)
+    }
+  }
+
+}
+
 class TimersAndStashSpec extends PekkoSpec {
   import TimersAndStashSpec._
 
diff --git a/actor/src/main/scala/org/apache/pekko/actor/Timers.scala 
b/actor/src/main/scala/org/apache/pekko/actor/Timers.scala
index 9c57c6100b..c75574be4d 100644
--- a/actor/src/main/scala/org/apache/pekko/actor/Timers.scala
+++ b/actor/src/main/scala/org/apache/pekko/actor/Timers.scala
@@ -55,14 +55,9 @@ trait Timers extends Actor {
           case OptionVal.Some(m: AutoReceivedMessage) =>
             context.asInstanceOf[ActorCell].autoReceiveMessage(Envelope(m, 
self, context.system))
           case OptionVal.Some(m) =>
-            if (this.isInstanceOf[StashSupport]) {
-              // This is important for stash interaction, as stash reads the 
message directly from
-              // currentMessage (StashSupport) #24557. We match StashSupport 
rather than Stash so that
-              // actors mixing in UnboundedStash or UnrestrictedStash directly 
- which are siblings of
-              // Stash, not subtypes - also rewrite the unwrapped timer 
message; otherwise stash()
-              // would re-stash the TimerMsg wrapper and the message would be 
lost on unstash (#3258).
-              actorCell.currentMessage = actorCell.currentMessage.copy(message 
= m)
-            }
+            // Keep the cell-wide current message consistent with the message 
delivered to receive.
+            // Stash and failure handling both read currentMessage directly.
+            actorCell.currentMessage = actorCell.currentMessage.copy(message = 
m)
             super.aroundReceive(receive, m)
           case _ => // discard
         }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to