Add convenience method to start timer without key (#27875)

* Add convenience method to start timer without key

It is probably common that there is no need to allow different timers
that send the same message, and this makes that more convenient to write.

Updated one method to gather feedback, if we like the change I can apply
it to the others as well.

* Add alternative to all typed timer API's

Update java/scaladoc, update tests

Not updated classic actors and FSM API's
This commit is contained in:
Arnout Engelen 2019-11-26 14:26:49 +01:00 committed by Christopher Batey
parent 38d41e11fb
commit 4b632c4537
21 changed files with 253 additions and 116 deletions

View file

@ -30,7 +30,11 @@ public class ManualTimerExampleTest extends JUnitSuite {
private final ManualTime manualTime = ManualTime.get(testKit.system()); private final ManualTime manualTime = ManualTime.get(testKit.system());
static final class Tick {} static final class Tick {
private Tick() {}
static final Tick INSTANCE = new Tick();
}
static final class Tock {} static final class Tock {}
@ -40,7 +44,7 @@ public class ManualTimerExampleTest extends JUnitSuite {
Behavior<Tick> behavior = Behavior<Tick> behavior =
Behaviors.withTimers( Behaviors.withTimers(
timer -> { timer -> {
timer.startSingleTimer("T", new Tick(), Duration.ofMillis(10)); timer.startSingleTimer(Tick.INSTANCE, Duration.ofMillis(10));
return Behaviors.receiveMessage( return Behaviors.receiveMessage(
tick -> { tick -> {
probe.ref().tell(new Tock()); probe.ref().tell(new Tock());

View file

@ -47,7 +47,7 @@ class TestProbeSpec extends ScalaTestWithActorTestKit with WordSpecLike with Log
val probe = TestProbe() val probe = TestProbe()
val ref = spawn(Behaviors.receive[Stop.type]((_, _) => val ref = spawn(Behaviors.receive[Stop.type]((_, _) =>
Behaviors.withTimers { timer => Behaviors.withTimers { timer =>
timer.startSingleTimer("key", Stop, 300.millis) timer.startSingleTimer(Stop, 300.millis)
Behaviors.receive((_, _) => Behaviors.stopped) Behaviors.receive((_, _) => Behaviors.stopped)
})) }))

View file

@ -25,7 +25,7 @@ class ManualTimerExampleSpec extends ScalaTestWithActorTestKit(ManualTime.config
val probe = TestProbe[Tock.type]() val probe = TestProbe[Tock.type]()
val behavior = Behaviors.withTimers[Tick.type] { timer => val behavior = Behaviors.withTimers[Tick.type] { timer =>
timer.startSingleTimer("T", Tick, 10.millis) timer.startSingleTimer(Tick, 10.millis)
Behaviors.receiveMessage { _ => Behaviors.receiveMessage { _ =>
probe.ref ! Tock probe.ref ! Tock
Behaviors.same Behaviors.same
@ -49,7 +49,7 @@ class ManualTimerExampleSpec extends ScalaTestWithActorTestKit(ManualTime.config
val probe = TestProbe[Tock.type]() val probe = TestProbe[Tock.type]()
val behavior = Behaviors.withTimers[Tick.type] { timer => val behavior = Behaviors.withTimers[Tick.type] { timer =>
timer.startTimerWithFixedDelay("T", Tick, 10.millis) timer.startTimerWithFixedDelay(Tick, 10.millis)
Behaviors.receiveMessage { _ => Behaviors.receiveMessage { _ =>
probe.ref ! Tock probe.ref ! Tock
Behaviors.same Behaviors.same

View file

@ -133,6 +133,15 @@ public class ActorCompile {
}); });
} }
{
Behavior<MyMsg> b =
Behaviors.withTimers(
timers -> {
timers.startTimerWithFixedDelay(new MyMsgB("tick"), Duration.ofSeconds(1));
return Behaviors.ignore();
});
}
static class MyBehavior extends ExtensibleBehavior<MyMsg> { static class MyBehavior extends ExtensibleBehavior<MyMsg> {
@Override @Override

View file

@ -211,7 +211,7 @@ interface StyleGuideDocExamples {
name, name,
command.interval, command.interval,
n); n);
timers.startTimerWithFixedDelay("repeat", Increment.INSTANCE, command.interval); timers.startTimerWithFixedDelay(Increment.INSTANCE, command.interval);
return Behaviors.same(); return Behaviors.same();
} }
@ -306,7 +306,7 @@ interface StyleGuideDocExamples {
setup.name, setup.name,
command.interval, command.interval,
n); n);
setup.timers.startTimerWithFixedDelay("repeat", Increment.INSTANCE, command.interval); setup.timers.startTimerWithFixedDelay(Increment.INSTANCE, command.interval);
return Behaviors.same(); return Behaviors.same();
} }
@ -394,7 +394,7 @@ interface StyleGuideDocExamples {
name, name,
command.interval, command.interval,
n); n);
timers.startTimerWithFixedDelay("repeat", Increment.INSTANCE, command.interval); timers.startTimerWithFixedDelay(Increment.INSTANCE, command.interval);
return Behaviors.same(); return Behaviors.same();
} }
@ -552,7 +552,7 @@ interface StyleGuideDocExamples {
context -> context ->
Behaviors.withTimers( Behaviors.withTimers(
timers -> { timers -> {
timers.startTimerWithFixedDelay("tick", Tick.INSTANCE, tickInterval); timers.startTimerWithFixedDelay(Tick.INSTANCE, tickInterval);
return new Counter(name, context); return new Counter(name, context);
})); }));
} }
@ -688,7 +688,7 @@ interface StyleGuideDocExamples {
(ActorContext<Message> context) -> (ActorContext<Message> context) ->
Behaviors.withTimers( Behaviors.withTimers(
timers -> { timers -> {
timers.startTimerWithFixedDelay("tick", Tick.INSTANCE, tickInterval); timers.startTimerWithFixedDelay(Tick.INSTANCE, tickInterval);
return new Counter(name, context); return new Counter(name, context);
})) }))
.narrow(); // note narrow here .narrow(); // note narrow here

View file

@ -88,7 +88,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"schedule non-repeated ticks" taggedAs TimingTest in { "schedule non-repeated ticks" taggedAs TimingTest in {
val probe = TestProbe[Event]("evt") val probe = TestProbe[Event]("evt")
val behv = Behaviors.withTimers[Command] { timer => val behv = Behaviors.withTimers[Command] { timer =>
timer.startSingleTimer("T", Tick(1), 10.millis) timer.startSingleTimer(Tick(1), 10.millis)
target(probe.ref, timer, 1) target(probe.ref, timer, 1)
} }
@ -103,7 +103,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"schedule repeated ticks" taggedAs TimingTest in { "schedule repeated ticks" taggedAs TimingTest in {
val probe = TestProbe[Event]("evt") val probe = TestProbe[Event]("evt")
val behv = Behaviors.withTimers[Command] { timer => val behv = Behaviors.withTimers[Command] { timer =>
timer.startTimerWithFixedDelay("T", Tick(1), interval) timer.startTimerWithFixedDelay(Tick(1), interval)
target(probe.ref, timer, 1) target(probe.ref, timer, 1)
} }
@ -121,7 +121,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"replace timer" taggedAs TimingTest in { "replace timer" taggedAs TimingTest in {
val probe = TestProbe[Event]("evt") val probe = TestProbe[Event]("evt")
val behv = Behaviors.withTimers[Command] { timer => val behv = Behaviors.withTimers[Command] { timer =>
timer.startTimerWithFixedDelay("T", Tick(1), interval) timer.startTimerWithFixedDelay(Tick(1), interval)
target(probe.ref, timer, 1) target(probe.ref, timer, 1)
} }
@ -141,7 +141,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"cancel timer" taggedAs TimingTest in { "cancel timer" taggedAs TimingTest in {
val probe = TestProbe[Event]("evt") val probe = TestProbe[Event]("evt")
val behv = Behaviors.withTimers[Command] { timer => val behv = Behaviors.withTimers[Command] { timer =>
timer.startTimerWithFixedDelay("T", Tick(1), interval) timer.startTimerWithFixedDelay(Tick(1), interval)
target(probe.ref, timer, 1) target(probe.ref, timer, 1)
} }
@ -193,7 +193,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
val probe = TestProbe[Event]("evt") val probe = TestProbe[Event]("evt")
val behv = Behaviors val behv = Behaviors
.supervise(Behaviors.withTimers[Command] { timer => .supervise(Behaviors.withTimers[Command] { timer =>
timer.startTimerWithFixedDelay("T", Tick(1), interval) timer.startTimerWithFixedDelay(Tick(1), interval)
target(probe.ref, timer, 1) target(probe.ref, timer, 1)
}) })
.onFailure[Exception](SupervisorStrategy.restart) .onFailure[Exception](SupervisorStrategy.restart)
@ -222,7 +222,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"cancel timers when stopped from exception" taggedAs TimingTest in { "cancel timers when stopped from exception" taggedAs TimingTest in {
val probe = TestProbe[Event]() val probe = TestProbe[Event]()
val behv = Behaviors.withTimers[Command] { timer => val behv = Behaviors.withTimers[Command] { timer =>
timer.startTimerWithFixedDelay("T", Tick(1), interval) timer.startTimerWithFixedDelay(Tick(1), interval)
target(probe.ref, timer, 1) target(probe.ref, timer, 1)
} }
val ref = spawn(behv) val ref = spawn(behv)
@ -235,7 +235,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"cancel timers when stopped voluntarily" taggedAs TimingTest in { "cancel timers when stopped voluntarily" taggedAs TimingTest in {
val probe = TestProbe[Event]() val probe = TestProbe[Event]()
val behv = Behaviors.withTimers[Command] { timer => val behv = Behaviors.withTimers[Command] { timer =>
timer.startTimerWithFixedDelay("T", Tick(1), interval) timer.startTimerWithFixedDelay(Tick(1), interval)
target(probe.ref, timer, 1) target(probe.ref, timer, 1)
} }
val ref = spawn(behv) val ref = spawn(behv)
@ -246,9 +246,9 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"allow for nested timers" in { "allow for nested timers" in {
val probe = TestProbe[String]() val probe = TestProbe[String]()
val ref = spawn(Behaviors.withTimers[String] { outerTimer => val ref = spawn(Behaviors.withTimers[String] { outerTimer =>
outerTimer.startTimerWithFixedDelay("outer-key", "outer-message", 50.millis) outerTimer.startTimerWithFixedDelay("outer-message", 50.millis)
Behaviors.withTimers { innerTimer => Behaviors.withTimers { innerTimer =>
innerTimer.startTimerWithFixedDelay("inner-key", "inner-message", 50.millis) innerTimer.startTimerWithFixedDelay("inner-message", 50.millis)
Behaviors.receiveMessage { message => Behaviors.receiveMessage { message =>
if (message == "stop") Behaviors.stopped if (message == "stop") Behaviors.stopped
else { else {
@ -273,7 +273,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
"keep timers when behavior changes" in { "keep timers when behavior changes" in {
val probe = TestProbe[String]() val probe = TestProbe[String]()
def newBehavior(n: Int): Behavior[String] = Behaviors.withTimers[String] { timers => def newBehavior(n: Int): Behavior[String] = Behaviors.withTimers[String] { timers =>
timers.startTimerWithFixedDelay(s"key${n}", s"message${n}", 50.milli) timers.startTimerWithFixedDelay(s"message${n}", 50.milli)
Behaviors.receiveMessage { message => Behaviors.receiveMessage { message =>
if (message == "stop") Behaviors.stopped if (message == "stop") Behaviors.stopped
else { else {
@ -299,7 +299,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
val probe = TestProbe[DeadLetter]() val probe = TestProbe[DeadLetter]()
val ref = spawn(Behaviors.withTimers[String] { timers => val ref = spawn(Behaviors.withTimers[String] { timers =>
Behaviors.setup { _ => Behaviors.setup { _ =>
timers.startTimerWithFixedDelay("test", "test", 250.millis) timers.startTimerWithFixedDelay("test", 250.millis)
Behaviors.receive { (context, _) => Behaviors.receive { (context, _) =>
Behaviors.stopped(() => context.log.info(s"stopping")) Behaviors.stopped(() => context.log.info(s"stopping"))
} }
@ -323,11 +323,11 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
case Tick(-1) => case Tick(-1) =>
probe.ref ! Tock(-1) probe.ref ! Tock(-1)
Behaviors.withTimers[Command] { timer => Behaviors.withTimers[Command] { timer =>
timer.startSingleTimer("T0", Tick(0), 5.millis) timer.startSingleTimer(Tick(0), 5.millis)
Behaviors.receiveMessage[Command] { Behaviors.receiveMessage[Command] {
case Tick(0) => case Tick(0) =>
probe.ref ! Tock(0) probe.ref ! Tock(0)
timer.startSingleTimer("T1", Tick(1), 5.millis) timer.startSingleTimer(Tick(1), 5.millis)
// let Tick(0) arrive in mailbox, test will not fail if it arrives later // let Tick(0) arrive in mailbox, test will not fail if it arrives later
Thread.sleep(100) Thread.sleep(100)
throw TestException("boom") throw TestException("boom")
@ -365,7 +365,7 @@ class TimerSpec extends ScalaTestWithActorTestKit with WordSpecLike with LogCapt
case Tick(-1) => case Tick(-1) =>
probe.ref ! Tock(-1) probe.ref ! Tock(-1)
Behaviors.withTimers[Command] { timer => Behaviors.withTimers[Command] { timer =>
timer.startSingleTimer("T0", Tick(0), 5.millis) timer.startSingleTimer(Tick(0), 5.millis)
// let Tick(0) arrive in mailbox, test will not fail if it arrives later // let Tick(0) arrive in mailbox, test will not fail if it arrives later
Thread.sleep(100) Thread.sleep(100)
throw TestException("boom") throw TestException("boom")

View file

@ -148,7 +148,7 @@ class TransformMessagesSpec extends ScalaTestWithActorTestKit with WordSpecLike
val probe = TestProbe[String]() val probe = TestProbe[String]()
val behv = Behaviors val behv = Behaviors
.withTimers[String] { timers => .withTimers[String] { timers =>
timers.startSingleTimer("timer", "a", 10.millis) timers.startSingleTimer("a", 10.millis)
Behaviors.receiveMessage { msg => Behaviors.receiveMessage { msg =>
probe.ref ! msg probe.ref ! msg
Behaviors.same Behaviors.same
@ -169,7 +169,7 @@ class TransformMessagesSpec extends ScalaTestWithActorTestKit with WordSpecLike
"be possible to combine with outer timers" in { "be possible to combine with outer timers" in {
val probe = TestProbe[String]() val probe = TestProbe[String]()
val behv = Behaviors.withTimers[String] { timers => val behv = Behaviors.withTimers[String] { timers =>
timers.startSingleTimer("timer", "a", 10.millis) timers.startSingleTimer("a", 10.millis)
Behaviors Behaviors
.receiveMessage[String] { msg => .receiveMessage[String] { msg =>
probe.ref ! msg probe.ref ! msg

View file

@ -57,7 +57,7 @@ object FSMDocSpec {
private def active(data: Todo): Behavior[Event] = private def active(data: Todo): Behavior[Event] =
Behaviors.withTimers[Event] { timers => Behaviors.withTimers[Event] { timers =>
// instead of FSM state timeout // instead of FSM state timeout
timers.startSingleTimer(Timeout, Timeout, 1.second) timers.startSingleTimer(Timeout, 1.second)
Behaviors.receiveMessagePartial { Behaviors.receiveMessagePartial {
case Flush | Timeout => case Flush | Timeout =>
data.target ! Batch(data.queue) data.target ! Batch(data.queue)

View file

@ -123,7 +123,7 @@ object StyleGuideDocExamples {
name, name,
interval.toString, interval.toString,
n.toString) n.toString)
timers.startTimerWithFixedDelay("repeat", Increment, interval) timers.startTimerWithFixedDelay(Increment, interval)
Behaviors.same Behaviors.same
case Increment => case Increment =>
val newValue = n + 1 val newValue = n + 1
@ -166,7 +166,7 @@ object StyleGuideDocExamples {
setup.name, setup.name,
interval, interval,
n) n)
setup.timers.startTimerWithFixedDelay("repeat", Increment, interval) setup.timers.startTimerWithFixedDelay(Increment, interval)
Behaviors.same Behaviors.same
case Increment => case Increment =>
val newValue = n + 1 val newValue = n + 1
@ -213,7 +213,7 @@ object StyleGuideDocExamples {
name, name,
interval, interval,
n) n)
timers.startTimerWithFixedDelay("repeat", Increment, interval) timers.startTimerWithFixedDelay(Increment, interval)
Behaviors.same Behaviors.same
case Increment => case Increment =>
val newValue = n + 1 val newValue = n + 1
@ -249,7 +249,7 @@ object StyleGuideDocExamples {
name, name,
interval, interval,
n) n)
timers.startTimerWithFixedDelay("repeat", Increment, interval) timers.startTimerWithFixedDelay(Increment, interval)
Behaviors.same Behaviors.same
case Increment => case Increment =>
val newValue = n + 1 val newValue = n + 1
@ -341,7 +341,7 @@ object StyleGuideDocExamples {
def apply(name: String, tickInterval: FiniteDuration): Behavior[Command] = def apply(name: String, tickInterval: FiniteDuration): Behavior[Command] =
Behaviors.setup { context => Behaviors.setup { context =>
Behaviors.withTimers { timers => Behaviors.withTimers { timers =>
timers.startTimerWithFixedDelay("tick", Tick, tickInterval) timers.startTimerWithFixedDelay(Tick, tickInterval)
new Counter(name, context).counter(0) new Counter(name, context).counter(0)
} }
} }
@ -390,7 +390,7 @@ object StyleGuideDocExamples {
Behaviors Behaviors
.setup[Counter.Message] { context => .setup[Counter.Message] { context =>
Behaviors.withTimers { timers => Behaviors.withTimers { timers =>
timers.startTimerWithFixedDelay("tick", Tick, tickInterval) timers.startTimerWithFixedDelay(Tick, tickInterval)
new Counter(name, context).counter(0) new Counter(name, context).counter(0)
} }
} }

View file

@ -46,9 +46,9 @@ object TailChopping {
def sendNextRequest(requestCount: Int): Behavior[Command] = { def sendNextRequest(requestCount: Int): Behavior[Command] = {
if (sendRequest(requestCount, replyAdapter)) { if (sendRequest(requestCount, replyAdapter)) {
timers.startSingleTimer(RequestTimeout, RequestTimeout, nextRequestAfter) timers.startSingleTimer(RequestTimeout, nextRequestAfter)
} else { } else {
timers.startSingleTimer(FinalTimeout, FinalTimeout, finalTimeout) timers.startSingleTimer(FinalTimeout, finalTimeout)
} }
waiting(requestCount) waiting(requestCount)
} }

View file

@ -29,12 +29,32 @@ trait TimerScheduler[T] {
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerWithFixedDelay(key: Any, msg: T, delay: java.time.Duration): Unit def startTimerWithFixedDelay(key: Any, msg: T, delay: java.time.Duration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages.
*
* It will not compensate the delay between messages if scheduling is delayed
* longer than specified for some reason. The delay between sending of subsequent
* messages will always be (at least) the given `delay`.
*
* In the long run, the frequency of messages will generally be slightly lower than
* the reciprocal of the specified `delay`.
*
* When a new timer is started with the same message,
* the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer was started. If you do not want this,
* you can start start them as individual timers by specifying distinct keys.
*/
def startTimerWithFixedDelay(msg: T, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(msg, msg, delay)
/** /**
* Schedules a message to be sent repeatedly to the `self` actor with a * Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * given frequency.
@ -56,12 +76,41 @@ trait TimerScheduler[T] {
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerAtFixedRate(key: Any, msg: T, interval: java.time.Duration): Unit def startTimerAtFixedRate(key: Any, msg: T, interval: java.time.Duration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
*
* It will compensate the delay for a subsequent message if the sending of previous
* message was delayed more than specified. In such cases, the actual message interval
* will differ from the interval passed to the method.
*
* If the execution is delayed longer than the `interval`, the subsequent message will
* be sent immediately after the prior one. This also has the consequence that after
* long garbage collection pauses or other reasons when the JVM was suspended all
* "missed" messages will be sent when the process wakes up again.
*
* In the long run, the frequency of messages will be exactly the reciprocal of the
* specified `interval`.
*
* Warning: `startTimerAtFixedRate` can result in bursts of scheduled messages after long
* garbage collection pauses, which may in worst case cause undesired load on the system.
* Therefore `startTimerWithFixedDelay` is often preferred.
*
* When a new timer is started with the same message,
* the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer was started. If you do not want this,
* you can start start them as individual timers by specifying distinct keys.
*/
def startTimerAtFixedRate(msg: T, interval: java.time.Duration): Unit =
startTimerAtFixedRate(msg, msg, interval)
/** /**
* Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]]. * Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]].
*/ */
@ -72,16 +121,29 @@ trait TimerScheduler[T] {
def startPeriodicTimer(key: Any, msg: T, interval: Duration): Unit def startPeriodicTimer(key: Any, msg: T, interval: Duration): Unit
/** /**
* * Start a timer that will send `msg` once to the `self` actor after * Start a timer that will send `msg` once to the `self` actor after
* the given `delay`. * the given `delay`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startSingleTimer(key: Any, msg: T, delay: Duration): Unit def startSingleTimer(key: Any, msg: T, delay: Duration): Unit
/**
* Start a timer that will send `msg` once to the `self` actor after
* the given `delay`.
*
* When a new timer is started with the same message
* the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer was started. If you do not want this,
* you can start start them as individual timers by specifying distinct keys.
*/
def startSingleTimer(msg: T, delay: Duration): Unit =
startSingleTimer(msg, msg, delay)
/** /**
* Check if a timer with a given `key` is active. * Check if a timer with a given `key` is active.
*/ */

View file

@ -29,12 +29,32 @@ trait TimerScheduler[T] {
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already be enqueued
* in the mailbox when the new timer is started. * in the mailbox before the new timer was started.
*/ */
def startTimerWithFixedDelay(key: Any, msg: T, delay: FiniteDuration): Unit def startTimerWithFixedDelay(key: Any, msg: T, delay: FiniteDuration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages.
*
* It will not compensate the delay between messages if scheduling is delayed
* longer than specified for some reason. The delay between sending of subsequent
* messages will always be (at least) the given `delay`.
*
* In the long run, the frequency of messages will generally be slightly lower than
* the reciprocal of the specified `delay`.
*
* When a new timer is started with the same message,
* the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer was started. If you do not want this,
* you can start start them as individual timers by specifying distinct keys.
*/
def startTimerWithFixedDelay(msg: T, delay: FiniteDuration): Unit =
startTimerWithFixedDelay(msg, msg, delay)
/** /**
* Schedules a message to be sent repeatedly to the `self` actor with a * Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * given frequency.
@ -56,12 +76,41 @@ trait TimerScheduler[T] {
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerAtFixedRate(key: Any, msg: T, interval: FiniteDuration): Unit def startTimerAtFixedRate(key: Any, msg: T, interval: FiniteDuration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
*
* It will compensate the delay for a subsequent message if the sending of previous
* message was delayed more than specified. In such cases, the actual message interval
* will differ from the interval passed to the method.
*
* If the execution is delayed longer than the `interval`, the subsequent message will
* be sent immediately after the prior one. This also has the consequence that after
* long garbage collection pauses or other reasons when the JVM was suspended all
* "missed" messages will be sent when the process wakes up again.
*
* In the long run, the frequency of messages will be exactly the reciprocal of the
* specified `interval`.
*
* Warning: `startTimerAtFixedRate` can result in bursts of scheduled messages after long
* garbage collection pauses, which may in worst case cause undesired load on the system.
* Therefore `startTimerWithFixedDelay` is often preferred.
*
* When a new timer is started with the same message
* the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer was started. If you do not want this,
* you can start start them as individual timers by specifying distinct keys.
*/
def startTimerAtFixedRate(msg: T, interval: FiniteDuration): Unit =
startTimerAtFixedRate(msg, msg, interval)
/** /**
* Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]]. * Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]].
*/ */
@ -76,12 +125,25 @@ trait TimerScheduler[T] {
* the given `delay`. * the given `delay`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startSingleTimer(key: Any, msg: T, delay: FiniteDuration): Unit def startSingleTimer(key: Any, msg: T, delay: FiniteDuration): Unit
/**
* Start a timer that will send `msg` once to the `self` actor after
* the given `delay`.
*
* If a new timer is started with the same message
* the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer was started. If you do not want this,
* you can start start them as individual timers by specifying distinct keys.
*/
def startSingleTimer(msg: T, delay: FiniteDuration): Unit =
startSingleTimer(msg, msg, delay)
/** /**
* Check if a timer with a given `key` is active. * Check if a timer with a given `key` is active.
*/ */

View file

@ -444,9 +444,9 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] {
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerWithFixedDelay(name: String, msg: Any, delay: java.time.Duration): Unit = def startTimerWithFixedDelay(name: String, msg: Any, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(name, msg, delay.asScala) startTimerWithFixedDelay(name, msg, delay.asScala)
@ -472,9 +472,9 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] {
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerAtFixedRate(name: String, msg: Any, interval: java.time.Duration): Unit = def startTimerAtFixedRate(name: String, msg: Any, interval: java.time.Duration): Unit =
startTimerAtFixedRate(name, msg, interval.asScala) startTimerAtFixedRate(name, msg, interval.asScala)
@ -484,9 +484,9 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] {
* the given `delay`. * the given `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startSingleTimer(name: String, msg: Any, delay: java.time.Duration): Unit = def startSingleTimer(name: String, msg: Any, delay: java.time.Duration): Unit =
startSingleTimer(name, msg, delay.asScala) startSingleTimer(name, msg, delay.asScala)

View file

@ -486,9 +486,9 @@ trait FSM[S, D] extends Actor with Listeners with ActorLogging {
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerWithFixedDelay(name: String, msg: Any, delay: FiniteDuration): Unit = def startTimerWithFixedDelay(name: String, msg: Any, delay: FiniteDuration): Unit =
startTimer(name, msg, delay, FixedDelayMode) startTimer(name, msg, delay, FixedDelayMode)
@ -514,9 +514,9 @@ trait FSM[S, D] extends Actor with Listeners with ActorLogging {
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerAtFixedRate(name: String, msg: Any, interval: FiniteDuration): Unit = def startTimerAtFixedRate(name: String, msg: Any, interval: FiniteDuration): Unit =
startTimer(name, msg, interval, FixedRateMode) startTimer(name, msg, interval, FixedRateMode)
@ -526,9 +526,9 @@ trait FSM[S, D] extends Actor with Listeners with ActorLogging {
* the given `delay`. * the given `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startSingleTimer(name: String, msg: Any, delay: FiniteDuration): Unit = def startSingleTimer(name: String, msg: Any, delay: FiniteDuration): Unit =
startTimer(name, msg, delay, SingleMode) startTimer(name, msg, delay, SingleMode)

View file

@ -97,9 +97,9 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerWithFixedDelay(key: Any, msg: Any, delay: FiniteDuration): Unit def startTimerWithFixedDelay(key: Any, msg: Any, delay: FiniteDuration): Unit
@ -115,9 +115,9 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
final def startTimerWithFixedDelay(key: Any, msg: Any, delay: java.time.Duration): Unit = final def startTimerWithFixedDelay(key: Any, msg: Any, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(key, msg, delay.asScala) startTimerWithFixedDelay(key, msg, delay.asScala)
@ -143,9 +143,9 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerAtFixedRate(key: Any, msg: Any, interval: FiniteDuration): Unit def startTimerAtFixedRate(key: Any, msg: Any, interval: FiniteDuration): Unit
@ -170,9 +170,9 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
final def startTimerAtFixedRate(key: Any, msg: Any, interval: java.time.Duration): Unit = final def startTimerAtFixedRate(key: Any, msg: Any, interval: java.time.Duration): Unit =
startTimerAtFixedRate(key, msg, interval.asScala) startTimerAtFixedRate(key, msg, interval.asScala)
@ -201,9 +201,9 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
* the given `timeout`. * the given `timeout`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startSingleTimer(key: Any, msg: Any, timeout: FiniteDuration): Unit def startSingleTimer(key: Any, msg: Any, timeout: FiniteDuration): Unit
@ -212,9 +212,9 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
* the given `timeout`. * the given `timeout`.
* *
* Each timer has a key and if a new timer with same key is started * Each timer has a key and if a new timer with same key is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
final def startSingleTimer(key: Any, msg: Any, timeout: java.time.Duration): Unit = final def startSingleTimer(key: Any, msg: Any, timeout: java.time.Duration): Unit =
startSingleTimer(key, msg, timeout.asScala) startSingleTimer(key, msg, timeout.asScala)

View file

@ -136,11 +136,11 @@ private[typed] object ClusterReceptionist extends ReceptionistBehaviorProvider {
// also periodic cleanup in case removal from ORMultiMap is skipped due to concurrent update, // also periodic cleanup in case removal from ORMultiMap is skipped due to concurrent update,
// which is possible for OR CRDTs - done with an adapter to leverage the existing NodesRemoved message // which is possible for OR CRDTs - done with an adapter to leverage the existing NodesRemoved message
timers.startTimerWithFixedDelay("remove-nodes", RemoveTick, setup.settings.pruningInterval) timers.startTimerWithFixedDelay(RemoveTick, setup.settings.pruningInterval)
// default tomstone keepalive is 24h (based on prune-gossip-tombstones-after) and keeping the actorrefs // default tomstone keepalive is 24h (based on prune-gossip-tombstones-after) and keeping the actorrefs
// around isn't very costly so don't prune often // around isn't very costly so don't prune often
timers.startTimerWithFixedDelay("prune-tombstones", PruneTombstonesTick, setup.keepTombstonesFor / 24) timers.startTimerWithFixedDelay(PruneTombstonesTick, setup.keepTombstonesFor / 24)
behavior(setup, registry, TypedMultiMap.empty[AbstractServiceKey, SubscriptionsKV]) behavior(setup, registry, TypedMultiMap.empty[AbstractServiceKey, SubscriptionsKV])
} }

View file

@ -398,7 +398,7 @@ There are a few things worth noting here:
* To get access to the timers you start with `Behaviors.withTimers` that will pass a `TimerScheduler` instance to the function. * To get access to the timers you start with `Behaviors.withTimers` that will pass a `TimerScheduler` instance to the function.
This can be used with any type of `Behavior`, including `receive`, `receiveMessage`, but also `setup` or any other behavior. This can be used with any type of `Behavior`, including `receive`, `receiveMessage`, but also `setup` or any other behavior.
* Each timer has a key and if a new timer with the same key is started, the previous is cancelled and it's guaranteed that a message from the previous timer is not received, even though it might already be enqueued in the mailbox when the new timer is started. * Each timer has a key and if a new timer with the same key is started, the previous is cancelled. It is guaranteed that a message from the previous timer is not received, even if it was already enqueued in the mailbox when the new timer was started.
* Both periodic and single message timers are supported. * Both periodic and single message timers are supported.
* The `TimerScheduler` is mutable in itself, because it performs and manages the side effects of registering the scheduled tasks. * The `TimerScheduler` is mutable in itself, because it performs and manages the side effects of registering the scheduled tasks.
* The `TimerScheduler` is bound to the lifecycle of the actor that owns it and it's cancelled automatically when the actor is stopped. * The `TimerScheduler` is bound to the lifecycle of the actor that owns it and it's cancelled automatically when the actor is stopped.

View file

@ -78,7 +78,7 @@ public class DeviceGroupQuery extends AbstractBehavior<DeviceGroupQuery.Command>
this.requestId = requestId; this.requestId = requestId;
this.requester = requester; this.requester = requester;
timers.startSingleTimer(CollectionTimeout.class, CollectionTimeout.INSTANCE, timeout); timers.startSingleTimer(CollectionTimeout.INSTANCE, timeout);
ActorRef<Device.RespondTemperature> respondTemperatureAdapter = ActorRef<Device.RespondTemperature> respondTemperatureAdapter =
context.messageAdapter(Device.RespondTemperature.class, WrappedRespondTemperature::new); context.messageAdapter(Device.RespondTemperature.class, WrappedRespondTemperature::new);

View file

@ -205,7 +205,7 @@ object EventSourcedBehaviorSpec {
case IncrementLater => case IncrementLater =>
// purpose is to test signals // purpose is to test signals
val delay = ctx.spawnAnonymous(Behaviors.withTimers[Tick.type] { timers => val delay = ctx.spawnAnonymous(Behaviors.withTimers[Tick.type] { timers =>
timers.startSingleTimer(Tick, Tick, 10.millis) timers.startSingleTimer(Tick, 10.millis)
Behaviors.receive((_, msg) => Behaviors.receive((_, msg) =>
msg match { msg match {
case Tick => Behaviors.stopped case Tick => Behaviors.stopped
@ -467,7 +467,7 @@ class EventSourcedBehaviorSpec
"handle scheduled message arriving before recovery completed " in { "handle scheduled message arriving before recovery completed " in {
val c = spawn(Behaviors.withTimers[Command] { timers => val c = spawn(Behaviors.withTimers[Command] { timers =>
timers.startSingleTimer("tick", Increment, 1.millis) timers.startSingleTimer(Increment, 1.millis)
Thread.sleep(30) // now it's probably already in the mailbox, and will be stashed Thread.sleep(30) // now it's probably already in the mailbox, and will be stashed
counter(nextPid) counter(nextPid)
}) })
@ -483,7 +483,7 @@ class EventSourcedBehaviorSpec
"handle scheduled message arriving after recovery completed " in { "handle scheduled message arriving after recovery completed " in {
val c = spawn(Behaviors.withTimers[Command] { timers => val c = spawn(Behaviors.withTimers[Command] { timers =>
// probably arrives after recovery completed // probably arrives after recovery completed
timers.startSingleTimer("tick", Increment, 200.millis) timers.startSingleTimer(Increment, 200.millis)
counter(nextPid) counter(nextPid)
}) })

View file

@ -145,7 +145,7 @@ object PersistentActorCompileOnlyTest {
}) })
Behaviors.withTimers((timers: TimerScheduler[Command]) => { Behaviors.withTimers((timers: TimerScheduler[Command]) => {
timers.startTimerWithFixedDelay("swing", MoodSwing, 10.seconds) timers.startTimerWithFixedDelay(MoodSwing, 10.seconds)
b b
}) })
} }

View file

@ -213,9 +213,9 @@ trait PersistentFSMBase[S, D, E] extends Actor with Listeners with ActorLogging
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerWithFixedDelay(name: String, msg: Any, delay: FiniteDuration): Unit = def startTimerWithFixedDelay(name: String, msg: Any, delay: FiniteDuration): Unit =
startTimer(name, msg, delay, FixedDelayMode) startTimer(name, msg, delay, FixedDelayMode)
@ -241,9 +241,9 @@ trait PersistentFSMBase[S, D, E] extends Actor with Listeners with ActorLogging
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerAtFixedRate(name: String, msg: Any, interval: FiniteDuration): Unit = def startTimerAtFixedRate(name: String, msg: Any, interval: FiniteDuration): Unit =
startTimer(name, msg, interval, FixedRateMode) startTimer(name, msg, interval, FixedRateMode)
@ -253,9 +253,9 @@ trait PersistentFSMBase[S, D, E] extends Actor with Listeners with ActorLogging
* the given `delay`. * the given `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startSingleTimer(name: String, msg: Any, delay: FiniteDuration): Unit = def startSingleTimer(name: String, msg: Any, delay: FiniteDuration): Unit =
startTimer(name, msg, delay, SingleMode) startTimer(name, msg, delay, SingleMode)
@ -1103,9 +1103,9 @@ abstract class AbstractPersistentFSMBase[S, D, E] extends PersistentFSMBase[S, D
* the reciprocal of the specified `delay`. * the reciprocal of the specified `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerWithFixedDelay(name: String, msg: Any, delay: java.time.Duration): Unit = def startTimerWithFixedDelay(name: String, msg: Any, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(name, msg, delay.asScala) startTimerWithFixedDelay(name, msg, delay.asScala)
@ -1131,9 +1131,9 @@ abstract class AbstractPersistentFSMBase[S, D, E] extends PersistentFSMBase[S, D
* Therefore `startTimerWithFixedDelay` is often preferred. * Therefore `startTimerWithFixedDelay` is often preferred.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startTimerAtFixedRate(name: String, msg: Any, interval: java.time.Duration): Unit = def startTimerAtFixedRate(name: String, msg: Any, interval: java.time.Duration): Unit =
startTimerAtFixedRate(name, msg, interval.asScala) startTimerAtFixedRate(name, msg, interval.asScala)
@ -1143,9 +1143,9 @@ abstract class AbstractPersistentFSMBase[S, D, E] extends PersistentFSMBase[S, D
* the given `delay`. * the given `delay`.
* *
* Each timer has a `name` and if a new timer with same `name` is started * Each timer has a `name` and if a new timer with same `name` is started
* the previous is cancelled and it's guaranteed that a message from the * the previous is cancelled. It is guaranteed that a message from the
* previous timer is not received, even though it might already be enqueued * previous timer is not received, even if it was already enqueued
* in the mailbox when the new timer is started. * in the mailbox when the new timer was started.
*/ */
def startSingleTimer(name: String, msg: Any, delay: java.time.Duration): Unit = def startSingleTimer(name: String, msg: Any, delay: java.time.Duration): Unit =
startSingleTimer(name, msg, delay.asScala) startSingleTimer(name, msg, delay.asScala)