2018-10-29 17:19:37 +08:00
|
|
|
/*
|
2020-01-02 07:24:59 -05:00
|
|
|
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
2011-03-31 20:32:04 +13:00
|
|
|
*/
|
|
|
|
|
|
2010-10-26 12:49:25 +02:00
|
|
|
package akka.actor
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2012-06-21 16:09:14 +02:00
|
|
|
import language.postfixOps
|
2011-06-17 22:19:17 +02:00
|
|
|
import akka.testkit._
|
2019-12-18 01:28:39 +01:00
|
|
|
|
2012-09-21 14:50:06 +02:00
|
|
|
import scala.concurrent.duration._
|
2010-10-04 20:45:33 +02:00
|
|
|
import java.util.concurrent.atomic.AtomicInteger
|
2019-12-18 01:28:39 +01:00
|
|
|
|
2012-06-29 16:06:26 +02:00
|
|
|
import scala.concurrent.Await
|
2011-12-19 15:05:33 +01:00
|
|
|
import java.util.concurrent.TimeoutException
|
2019-12-18 01:28:39 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2013-08-23 14:39:21 +02:00
|
|
|
object ReceiveTimeoutSpec {
|
|
|
|
|
case object Tick
|
2019-05-14 06:33:42 -07:00
|
|
|
case object TransparentTick extends NotInfluenceReceiveTimeout
|
2019-12-18 01:28:39 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-23 14:39:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-18 01:28:39 +01:00
|
|
|
class ReceiveTimeoutSpec extends AkkaSpec() {
|
2013-08-23 14:39:21 +02:00
|
|
|
import ReceiveTimeoutSpec._
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-03-31 20:34:36 +13:00
|
|
|
"An actor with receive timeout" must {
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2016-11-29 08:33:36 +01:00
|
|
|
"get timeout" taggedAs TimingTest in {
|
2011-03-31 20:32:04 +13:00
|
|
|
val timeoutLatch = TestLatch()
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-12-13 14:09:40 +01:00
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
2011-12-13 11:11:21 +01:00
|
|
|
context.setReceiveTimeout(500 milliseconds)
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2012-05-21 13:47:48 +02:00
|
|
|
def receive = {
|
2020-04-27 17:31:16 +07:00
|
|
|
case ReceiveTimeout => timeoutLatch.open()
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-12-19 15:05:33 +01:00
|
|
|
Await.ready(timeoutLatch, TestLatch.DefaultTimeout)
|
2011-12-14 00:06:36 +01:00
|
|
|
system.stop(timeoutActor)
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2016-11-29 08:33:36 +01:00
|
|
|
"reschedule timeout after regular receive" taggedAs TimingTest in {
|
2011-03-31 20:32:04 +13:00
|
|
|
val timeoutLatch = TestLatch()
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-12-13 14:09:40 +01:00
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
2011-12-13 11:11:21 +01:00
|
|
|
context.setReceiveTimeout(500 milliseconds)
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2012-05-21 13:47:48 +02:00
|
|
|
def receive = {
|
2019-02-09 15:25:39 +01:00
|
|
|
case Tick => ()
|
2020-04-27 17:31:16 +07:00
|
|
|
case ReceiveTimeout => timeoutLatch.open()
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2010-10-04 20:45:33 +02:00
|
|
|
|
2011-03-31 20:32:04 +13:00
|
|
|
timeoutActor ! Tick
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-12-19 15:05:33 +01:00
|
|
|
Await.ready(timeoutLatch, TestLatch.DefaultTimeout)
|
2011-12-14 00:06:36 +01:00
|
|
|
system.stop(timeoutActor)
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2016-11-29 08:33:36 +01:00
|
|
|
"be able to turn off timeout if desired" taggedAs TimingTest in {
|
2011-03-31 20:32:04 +13:00
|
|
|
val count = new AtomicInteger(0)
|
|
|
|
|
val timeoutLatch = TestLatch()
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-12-13 14:09:40 +01:00
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
2011-12-13 11:11:21 +01:00
|
|
|
context.setReceiveTimeout(500 milliseconds)
|
2011-03-31 20:32:04 +13:00
|
|
|
|
2012-05-21 13:47:48 +02:00
|
|
|
def receive = {
|
2019-02-09 15:25:39 +01:00
|
|
|
case Tick => ()
|
|
|
|
|
case ReceiveTimeout =>
|
2011-03-31 20:32:04 +13:00
|
|
|
count.incrementAndGet
|
2020-04-27 17:31:16 +07:00
|
|
|
timeoutLatch.open()
|
2012-09-18 18:17:44 +02:00
|
|
|
context.setReceiveTimeout(Duration.Undefined)
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2011-03-31 20:32:04 +13:00
|
|
|
|
|
|
|
|
timeoutActor ! Tick
|
|
|
|
|
|
2011-12-19 15:05:33 +01:00
|
|
|
Await.ready(timeoutLatch, TestLatch.DefaultTimeout)
|
2015-01-16 11:09:59 +01:00
|
|
|
count.get should ===(1)
|
2011-12-14 00:06:36 +01:00
|
|
|
system.stop(timeoutActor)
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
|
|
|
|
|
2016-11-29 08:33:36 +01:00
|
|
|
"not receive timeout message when not specified" taggedAs TimingTest in {
|
2011-03-31 20:32:04 +13:00
|
|
|
val timeoutLatch = TestLatch()
|
|
|
|
|
|
2011-12-13 14:09:40 +01:00
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
2012-05-21 13:47:48 +02:00
|
|
|
def receive = {
|
2020-04-27 17:31:16 +07:00
|
|
|
case ReceiveTimeout => timeoutLatch.open()
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2011-03-31 20:32:04 +13:00
|
|
|
|
2011-12-19 15:05:33 +01:00
|
|
|
intercept[TimeoutException] { Await.ready(timeoutLatch, 1 second) }
|
2011-12-14 00:06:36 +01:00
|
|
|
system.stop(timeoutActor)
|
2011-03-31 20:32:04 +13:00
|
|
|
}
|
2015-08-18 11:12:56 +02:00
|
|
|
|
2016-11-29 08:33:36 +01:00
|
|
|
"get timeout while receiving NotInfluenceReceiveTimeout messages" taggedAs TimingTest in {
|
2015-08-18 11:12:56 +02:00
|
|
|
val timeoutLatch = TestLatch()
|
|
|
|
|
|
|
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
|
|
|
|
context.setReceiveTimeout(1 second)
|
|
|
|
|
|
|
|
|
|
def receive = {
|
2020-04-27 17:31:16 +07:00
|
|
|
case ReceiveTimeout => timeoutLatch.open()
|
2019-05-14 06:33:42 -07:00
|
|
|
case TransparentTick =>
|
2015-08-18 11:12:56 +02:00
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
2019-05-27 11:53:26 +02:00
|
|
|
val ticks = system.scheduler.scheduleWithFixedDelay(100.millis, 100.millis) { () =>
|
|
|
|
|
timeoutActor ! TransparentTick
|
|
|
|
|
timeoutActor ! Identify(None)
|
|
|
|
|
}(system.dispatcher)
|
2015-08-18 11:12:56 +02:00
|
|
|
|
|
|
|
|
Await.ready(timeoutLatch, TestLatch.DefaultTimeout)
|
|
|
|
|
ticks.cancel()
|
|
|
|
|
system.stop(timeoutActor)
|
|
|
|
|
}
|
2018-05-22 10:42:35 +02:00
|
|
|
|
|
|
|
|
"get timeout while receiving only NotInfluenceReceiveTimeout messages" taggedAs TimingTest in {
|
|
|
|
|
val timeoutLatch = TestLatch(2)
|
|
|
|
|
|
|
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
|
|
|
|
context.setReceiveTimeout(1 second)
|
|
|
|
|
|
|
|
|
|
def receive = {
|
2019-02-09 15:25:39 +01:00
|
|
|
case ReceiveTimeout =>
|
2019-05-14 06:33:42 -07:00
|
|
|
self ! TransparentTick
|
2018-05-22 10:42:35 +02:00
|
|
|
timeoutLatch.countDown()
|
2019-05-14 06:33:42 -07:00
|
|
|
case TransparentTick =>
|
2018-05-22 10:42:35 +02:00
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
Await.ready(timeoutLatch, TestLatch.DefaultTimeout)
|
|
|
|
|
system.stop(timeoutActor)
|
|
|
|
|
}
|
2018-04-26 19:08:50 -07:00
|
|
|
|
|
|
|
|
"get timeout while receiving NotInfluenceReceiveTimeout messages scheduled with Timers" taggedAs TimingTest in {
|
|
|
|
|
val timeoutLatch = TestLatch()
|
|
|
|
|
val count = new AtomicInteger(0)
|
|
|
|
|
|
|
|
|
|
class ActorWithTimer() extends Actor with Timers {
|
2019-05-27 11:53:26 +02:00
|
|
|
timers.startTimerWithFixedDelay("transparentTick", TransparentTick, 100.millis)
|
|
|
|
|
timers.startTimerWithFixedDelay("identifyTick", Identify(None), 100.millis)
|
2018-04-26 19:08:50 -07:00
|
|
|
|
|
|
|
|
context.setReceiveTimeout(1 second)
|
|
|
|
|
def receive: Receive = {
|
2019-02-09 15:25:39 +01:00
|
|
|
case ReceiveTimeout =>
|
2020-04-27 17:31:16 +07:00
|
|
|
timeoutLatch.open()
|
2019-05-14 06:33:42 -07:00
|
|
|
case TransparentTick =>
|
2018-04-26 19:08:50 -07:00
|
|
|
count.incrementAndGet()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val timeoutActor = system.actorOf(Props(new ActorWithTimer()))
|
|
|
|
|
|
|
|
|
|
Await.ready(timeoutLatch, TestLatch.DefaultTimeout)
|
|
|
|
|
count.get() should be > 0
|
|
|
|
|
system.stop(timeoutActor)
|
|
|
|
|
}
|
2018-07-12 23:41:02 +07:00
|
|
|
|
|
|
|
|
"be able to turn on timeout in NotInfluenceReceiveTimeout message handler" taggedAs TimingTest in {
|
|
|
|
|
val timeoutLatch = TestLatch()
|
|
|
|
|
|
|
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
|
|
|
|
def receive = {
|
2019-05-14 06:33:42 -07:00
|
|
|
case TransparentTick => context.setReceiveTimeout(500 milliseconds)
|
2020-04-27 17:31:16 +07:00
|
|
|
case ReceiveTimeout => timeoutLatch.open()
|
2018-07-12 23:41:02 +07:00
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
2019-05-14 06:33:42 -07:00
|
|
|
timeoutActor ! TransparentTick
|
2018-07-12 23:41:02 +07:00
|
|
|
|
|
|
|
|
Await.ready(timeoutLatch, TestLatch.DefaultTimeout)
|
|
|
|
|
system.stop(timeoutActor)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be able to turn off timeout in NotInfluenceReceiveTimeout message handler" taggedAs TimingTest in {
|
|
|
|
|
val timeoutLatch = TestLatch()
|
|
|
|
|
|
|
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
|
|
|
|
context.setReceiveTimeout(500 milliseconds)
|
|
|
|
|
|
|
|
|
|
def receive = {
|
2019-05-14 06:33:42 -07:00
|
|
|
case TransparentTick => context.setReceiveTimeout(Duration.Inf)
|
2020-04-27 17:31:16 +07:00
|
|
|
case ReceiveTimeout => timeoutLatch.open()
|
2018-07-12 23:41:02 +07:00
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
2019-05-14 06:33:42 -07:00
|
|
|
timeoutActor ! TransparentTick
|
2018-07-12 23:41:02 +07:00
|
|
|
|
|
|
|
|
intercept[TimeoutException] { Await.ready(timeoutLatch, 1 second) }
|
|
|
|
|
system.stop(timeoutActor)
|
|
|
|
|
}
|
2019-05-14 06:33:42 -07:00
|
|
|
|
|
|
|
|
"remove / change existing timeout while handling a NotInfluenceReceiveTimeout message" taggedAs TimingTest in {
|
|
|
|
|
val timeoutLatch = TestLatch()
|
|
|
|
|
val initialTimeout = 500.millis
|
|
|
|
|
|
|
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
|
|
|
|
context.setReceiveTimeout(initialTimeout)
|
|
|
|
|
|
|
|
|
|
def receive: Receive = {
|
|
|
|
|
case TransparentTick => context.setReceiveTimeout(Duration.Undefined)
|
2020-04-27 17:31:16 +07:00
|
|
|
case ReceiveTimeout => timeoutLatch.open()
|
2019-05-14 06:33:42 -07:00
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
timeoutActor ! TransparentTick
|
|
|
|
|
|
|
|
|
|
intercept[TimeoutException] { Await.ready(timeoutLatch, initialTimeout * 2) }
|
|
|
|
|
system.stop(timeoutActor)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"work correctly if the timeout is changed multiple times while handling a NotInfluenceReceiveTimeout message" taggedAs TimingTest in {
|
|
|
|
|
val probe = TestProbe()
|
|
|
|
|
val initialTimeout = 500.millis
|
|
|
|
|
|
|
|
|
|
val timeoutActor = system.actorOf(Props(new Actor {
|
|
|
|
|
var count = 0
|
|
|
|
|
context.setReceiveTimeout(initialTimeout)
|
|
|
|
|
|
|
|
|
|
def receive: Receive = {
|
|
|
|
|
case TransparentTick =>
|
|
|
|
|
context.setReceiveTimeout(initialTimeout * 2)
|
|
|
|
|
count += 1 // do some work then
|
|
|
|
|
context.setReceiveTimeout(initialTimeout)
|
|
|
|
|
|
|
|
|
|
case ReceiveTimeout => probe.ref ! ReceiveTimeout
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
probe.expectNoMessage(initialTimeout / 2)
|
|
|
|
|
|
|
|
|
|
timeoutActor ! TransparentTick
|
|
|
|
|
timeoutActor ! TransparentTick
|
|
|
|
|
|
|
|
|
|
// not triggered by initialTimeout because it was changed by the ticks
|
|
|
|
|
// wait shorter than last set value, but longer than initialTimeout
|
|
|
|
|
probe.expectNoMessage(initialTimeout / 2 + 50.millis)
|
|
|
|
|
// now should happen
|
|
|
|
|
probe.expectMsgType[ReceiveTimeout]
|
|
|
|
|
system.stop(timeoutActor)
|
|
|
|
|
}
|
2019-12-18 01:28:39 +01:00
|
|
|
|
|
|
|
|
// #28266 reproducer
|
2019-12-19 01:57:55 -05:00
|
|
|
"get the timeout when scheduled immediately on restart" in {
|
2019-12-18 01:28:39 +01:00
|
|
|
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)
|
|
|
|
|
}
|
2011-02-03 14:37:08 +01:00
|
|
|
}
|
2010-08-24 23:21:28 +02:00
|
|
|
}
|