diff --git a/akka-actor-tests/src/test/scala/akka/actor/ReceiveTimeoutSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ReceiveTimeoutSpec.scala index bb3a786e6c..eed25cb532 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ReceiveTimeoutSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ReceiveTimeoutSpec.scala @@ -6,17 +6,55 @@ package akka.actor import language.postfixOps import akka.testkit._ + import scala.concurrent.duration._ import java.util.concurrent.atomic.AtomicInteger + import scala.concurrent.Await import java.util.concurrent.TimeoutException +import java.util.concurrent.atomic.AtomicBoolean object ReceiveTimeoutSpec { case object Tick case object TransparentTick extends NotInfluenceReceiveTimeout + + class RestartingParent(probe: ActorRef) extends Actor { + val restarting = new AtomicBoolean(false) + val child = context.actorOf(Props(new RestartingChild(probe, restarting))) + def receive = { + case msg => + child.forward(msg) + } + } + class RestartingChild(probe: ActorRef, restarting: AtomicBoolean) extends Actor { + + override def preStart(): Unit = { + if (restarting.get) { + probe ! "restarting" + context.setReceiveTimeout(500.millis) + } else { + probe ! "starting" + } + } + + override def postStop(): Unit = { + probe ! "stopping" + } + + def receive = { + case "crash" => + restarting.set(true) + probe ! "crashing" + throw TestException("boom bang") + case ReceiveTimeout => + probe ! ReceiveTimeout + case other => + probe ! other + } + } } -class ReceiveTimeoutSpec extends AkkaSpec { +class ReceiveTimeoutSpec extends AkkaSpec() { import ReceiveTimeoutSpec._ "An actor with receive timeout" must { @@ -237,5 +275,19 @@ class ReceiveTimeoutSpec extends AkkaSpec { probe.expectMsgType[ReceiveTimeout] system.stop(timeoutActor) } + + // #28266 reproducer + "get the timeout when scheduled immedately on restart" in { + val probe = TestProbe() + val ref = system.actorOf(Props(new RestartingParent(probe.ref))) + probe.expectMsg("starting") + EventFilter.error("boom bang", occurrences = 1).intercept { + ref ! "crash" + } + probe.expectMsg("crashing") + probe.expectMsg("stopping") + probe.expectMsg("restarting") + probe.expectMsg(ReceiveTimeout) + } } } diff --git a/akka-actor/src/main/scala/akka/actor/dungeon/FaultHandling.scala b/akka-actor/src/main/scala/akka/actor/dungeon/FaultHandling.scala index 6f59293a64..1c49869a81 100644 --- a/akka-actor/src/main/scala/akka/actor/dungeon/FaultHandling.scala +++ b/akka-actor/src/main/scala/akka/actor/dungeon/FaultHandling.scala @@ -244,6 +244,7 @@ private[akka] trait FaultHandling { this: ActorCell => if (freshActor eq failedActor) setActorFields(freshActor, this, self) // If the creator returns the same instance, we need to restore our nulled out fields. freshActor.aroundPostRestart(cause) + checkReceiveTimeout() // user may have set a receive timeout in preStart which is called from postRestart if (system.settings.DebugLifecycle) publish(Debug(self.path.toString, clazz(freshActor), "restarted")) // only after parent is up and running again do restart the children which were not stopped