Adds initial delay to Timers #30065 (#30066)

This commit is contained in:
Nicolas Vollmar 2021-03-05 08:41:13 +01:00 committed by GitHub
parent b208265e5c
commit 4081638fd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 371 additions and 22 deletions

View file

@ -214,14 +214,20 @@ object Effect {
}
object TimerScheduled {
import akka.util.JavaDurationConverters._
sealed trait TimerMode
case object FixedRateMode extends TimerMode
case class FixedRateModeWithInitialDelay(initialDelay: FiniteDuration) extends TimerMode
case object FixedDelayMode extends TimerMode
case class FixedDelayModeWithInitialDelay(initialDelay: FiniteDuration) extends TimerMode
case object SingleMode extends TimerMode
/*Java API*/
def fixedRateMode = FixedRateMode
def fixedRateMode(initialDelay: java.time.Duration) = FixedRateModeWithInitialDelay(initialDelay.asScala)
def fixedDelayMode = FixedDelayMode
def fixedDelayMode(initialDelay: java.time.Duration) = FixedDelayModeWithInitialDelay(initialDelay.asScala)
def singleMode = SingleMode
}

View file

@ -91,9 +91,15 @@ import scala.reflect.ClassTag
override def startTimerWithFixedDelay(key: Any, msg: T, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, Effect.TimerScheduled.FixedDelayMode)
override def startTimerWithFixedDelay(key: Any, msg: T, initialDelay: FiniteDuration, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, Effect.TimerScheduled.FixedDelayModeWithInitialDelay(initialDelay))
override def startTimerAtFixedRate(key: Any, msg: T, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, Effect.TimerScheduled.FixedRateMode)
override def startTimerAtFixedRate(key: Any, msg: T, initialDelay: FiniteDuration, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, Effect.TimerScheduled.FixedRateModeWithInitialDelay(initialDelay))
override def startPeriodicTimer(key: Any, msg: T, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, Effect.TimerScheduled.FixedRateMode)

View file

@ -123,8 +123,12 @@ object BehaviorTestKitSpec {
case ScheduleCommand(key, delay, mode, cmd) =>
mode match {
case Effect.TimerScheduled.SingleMode => timers.startSingleTimer(key, cmd, delay)
case Effect.TimerScheduled.FixedDelayMode => timers.startTimerWithFixedDelay(key, cmd, delay)
case Effect.TimerScheduled.FixedRateMode => timers.startTimerAtFixedRate(key, cmd, delay)
case Effect.TimerScheduled.FixedDelayMode => timers.startTimerWithFixedDelay(key, cmd, delay, delay)
case m: Effect.TimerScheduled.FixedDelayModeWithInitialDelay =>
timers.startTimerWithFixedDelay(key, cmd, m.initialDelay, delay)
case Effect.TimerScheduled.FixedRateMode => timers.startTimerAtFixedRate(key, cmd, delay, delay)
case m: Effect.TimerScheduled.FixedRateModeWithInitialDelay =>
timers.startTimerAtFixedRate(key, cmd, m.initialDelay, delay)
}
Behaviors.same
case CancelScheduleCommand(key) =>
@ -445,16 +449,17 @@ class BehaviorTestKitSpec extends AnyWordSpec with Matchers with LogCapturing {
}
"schedule and fire timers multiple times" in {
val delay = 42.seconds
val testkit = BehaviorTestKit[Parent.Command](Parent.init)
testkit.run(ScheduleCommand("abc", 42.seconds, Effect.TimerScheduled.FixedRateMode, SpawnChild))
testkit.run(ScheduleCommand("abc", delay, Effect.TimerScheduled.FixedRateMode, SpawnChild))
val send = testkit.expectEffectPF {
case e @ Effect.TimerScheduled(
"abc",
SpawnChild,
finiteDuration,
Effect.TimerScheduled.FixedRateMode,
Effect.TimerScheduled.FixedRateModeWithInitialDelay(`delay`),
false /*not overriding*/ ) =>
finiteDuration should equal(42.seconds)
finiteDuration should equal(delay)
e.send
}
send()

View file

@ -0,0 +1,12 @@
# marked with @DoNotInherit
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.typed.javadsl.TimerScheduler.startTimerWithFixedDelay")
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.typed.javadsl.TimerScheduler.startTimerAtFixedRate")
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.typed.scaladsl.TimerScheduler.startTimerWithFixedDelay")
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.typed.scaladsl.TimerScheduler.startTimerAtFixedRate")
# marked with @InternalApi
ProblemFilters.exclude[MissingTypesProblem]("akka.actor.typed.internal.TimerSchedulerImpl$FixedRateMode$")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.typed.internal.TimerSchedulerImpl#FixedRateMode.*")
ProblemFilters.exclude[FinalMethodProblem]("akka.actor.typed.internal.TimerSchedulerImpl#FixedRateMode.toString")
ProblemFilters.exclude[MissingTypesProblem]("akka.actor.typed.internal.TimerSchedulerImpl$FixedDelayMode$")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.typed.internal.TimerSchedulerImpl#FixedDelayMode.*")
ProblemFilters.exclude[FinalMethodProblem]("akka.actor.typed.internal.TimerSchedulerImpl#FixedDelayMode.toString")

View file

@ -40,10 +40,10 @@ import scala.concurrent.duration.FiniteDuration
private sealed trait TimerMode {
def repeat: Boolean
}
private case object FixedRateMode extends TimerMode {
private case class FixedRateMode(initialDelay: FiniteDuration) extends TimerMode {
override def repeat: Boolean = true
}
private case object FixedDelayMode extends TimerMode {
private case class FixedDelayMode(initialDelay: FiniteDuration) extends TimerMode {
override def repeat: Boolean = true
}
private case object SingleMode extends TimerMode {
@ -59,9 +59,15 @@ import scala.concurrent.duration.FiniteDuration
override final def startTimerWithFixedDelay(key: Any, msg: T, delay: Duration): Unit =
startTimerWithFixedDelay(key, msg, delay.asScala)
override final def startTimerWithFixedDelay(key: Any, msg: T, initialDelay: Duration, delay: Duration): Unit =
startTimerWithFixedDelay(key, msg, initialDelay.asScala, delay.asScala)
override final def startTimerAtFixedRate(key: Any, msg: T, interval: Duration): Unit =
startTimerAtFixedRate(key, msg, interval.asScala)
override final def startTimerAtFixedRate(key: Any, msg: T, initialDelay: Duration, interval: Duration): Unit =
startTimerAtFixedRate(key, msg, initialDelay.asScala, interval.asScala)
override final def startPeriodicTimer(key: Any, msg: T, interval: Duration): Unit = {
//this follows the deprecation note in the super class
startTimerWithFixedDelay(key, msg, interval.asScala)
@ -83,13 +89,19 @@ import scala.concurrent.duration.FiniteDuration
private val timerGen = Iterator.from(1)
override def startTimerAtFixedRate(key: Any, msg: T, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, FixedRateMode)
startTimer(key, msg, interval, FixedRateMode(interval))
override def startTimerAtFixedRate(key: Any, msg: T, initialDelay: FiniteDuration, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, FixedRateMode(initialDelay))
override def startTimerWithFixedDelay(key: Any, msg: T, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, FixedDelayMode)
startTimer(key, msg, delay, FixedDelayMode(delay))
override def startTimerWithFixedDelay(key: Any, msg: T, initialDelay: FiniteDuration, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, FixedDelayMode(initialDelay))
override def startPeriodicTimer(key: Any, msg: T, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, FixedRateMode)
startTimer(key, msg, interval, FixedRateMode(interval))
override def startSingleTimer(key: Any, msg: T, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, SingleMode)
@ -110,11 +122,11 @@ import scala.concurrent.duration.FiniteDuration
val task = mode match {
case SingleMode =>
ctx.system.scheduler.scheduleOnce(delay, () => ctx.self.unsafeUpcast ! timerMsg)(ExecutionContexts.parasitic)
case FixedDelayMode =>
ctx.system.scheduler.scheduleWithFixedDelay(delay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)(
case m: FixedDelayMode =>
ctx.system.scheduler.scheduleWithFixedDelay(m.initialDelay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)(
ExecutionContexts.parasitic)
case FixedRateMode =>
ctx.system.scheduler.scheduleAtFixedRate(delay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)(
case m: FixedRateMode =>
ctx.system.scheduler.scheduleAtFixedRate(m.initialDelay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)(
ExecutionContexts.parasitic)
}

View file

@ -40,6 +40,24 @@ trait TimerScheduler[T] {
*/
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 after `initialDelay`.
*
* 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`.
*
* Each timer has a key and if a new timer with 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.
*/
def startTimerWithFixedDelay(key: Any, msg: T, initialDelay: java.time.Duration, delay: java.time.Duration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages.
@ -60,6 +78,26 @@ trait TimerScheduler[T] {
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
* fixed `delay` between messages after `initialDelay`.
*
* 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, initialDelay: java.time.Duration, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(msg, msg, initialDelay, delay)
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
@ -87,6 +125,33 @@ trait TimerScheduler[T] {
*/
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` after `initialDelay`.
*
* 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.
*
* Each timer has a key and if a new timer with 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.
*/
def startTimerAtFixedRate(key: Any, msg: T, initialDelay: java.time.Duration, interval: java.time.Duration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
@ -116,6 +181,35 @@ trait TimerScheduler[T] {
def startTimerAtFixedRate(msg: T, interval: java.time.Duration): Unit =
startTimerAtFixedRate(msg, msg, interval)
/**
* 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` after `initialDelay`.
*
* 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, initialDelay: java.time.Duration, interval: java.time.Duration): Unit =
startTimerAtFixedRate(msg, msg, initialDelay, interval)
/**
* Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]].
*/

View file

@ -40,6 +40,24 @@ trait TimerScheduler[T] {
*/
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 after the `initialDelay`.
*
* 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`.
*
* Each timer has a key and if a new timer with 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 be enqueued
* in the mailbox before the new timer was started.
*/
def startTimerWithFixedDelay(key: Any, msg: T, initialDelay: FiniteDuration, delay: FiniteDuration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages.
@ -60,6 +78,26 @@ trait TimerScheduler[T] {
def startTimerWithFixedDelay(msg: T, delay: FiniteDuration): Unit =
startTimerWithFixedDelay(msg, msg, delay)
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages after the `initialDelay`.
*
* 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, initialDelay: FiniteDuration, delay: FiniteDuration): Unit =
startTimerWithFixedDelay(msg, msg, initialDelay, delay)
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
@ -87,6 +125,33 @@ trait TimerScheduler[T] {
*/
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` after `initialDelay`.
*
* 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.
*
* Each timer has a key and if a new timer with 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.
*/
def startTimerAtFixedRate(key: Any, msg: T, initialDelay: FiniteDuration, interval: FiniteDuration): Unit
/**
* Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
@ -116,6 +181,35 @@ trait TimerScheduler[T] {
def startTimerAtFixedRate(msg: T, interval: FiniteDuration): Unit =
startTimerAtFixedRate(msg, msg, interval)
/**
* 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` after `initialDelay`.
*
* 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, initialDelay: FiniteDuration, interval: FiniteDuration): Unit =
startTimerAtFixedRate(msg, msg, initialDelay, interval)
/**
* Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]].
*/

View file

@ -0,0 +1,14 @@
# marked with @DoNotInherit
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.TimerScheduler.startTimerWithFixedDelay")
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.actor.TimerScheduler.startTimerAtFixedRate")
# marked with @InternalApi
ProblemFilters.exclude[MissingTypesProblem]("akka.actor.TimerSchedulerImpl$FixedRateMode$")
ProblemFilters.exclude[MissingTypesProblem]("akka.actor.TimerSchedulerImpl$FixedDelayMode$")
ProblemFilters.exclude[FinalMethodProblem]("akka.actor.TimerSchedulerImpl#FixedRateMode.toString")
ProblemFilters.exclude[FinalMethodProblem]("akka.actor.TimerSchedulerImpl#FixedDelayMode.toString")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.TimerSchedulerImpl#FixedRateMode.*")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.TimerSchedulerImpl#FixedDelayMode.*")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.TimerSchedulerImpl#FixedRateMode.productElementName")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.TimerSchedulerImpl#FixedRateMode.productElementNames")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.TimerSchedulerImpl#FixedDelayMode.productElementName")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.TimerSchedulerImpl#FixedDelayMode.productElementNames")

View file

@ -104,6 +104,24 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
*/
def startTimerWithFixedDelay(key: Any, msg: Any, delay: FiniteDuration): Unit
/**
* Scala API: Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages after the `initialDelay`.
*
* 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`.
*
* Each timer has a key and if a new timer with 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.
*/
def startTimerWithFixedDelay(key: Any, msg: Any, initialDelay: FiniteDuration, delay: FiniteDuration): Unit
/**
* Java API: Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages.
@ -123,6 +141,29 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
final def startTimerWithFixedDelay(key: Any, msg: Any, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(key, msg, delay.asScala)
/**
* Java API: Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages after the `initialDelay`.
*
* 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`.
*
* Each timer has a key and if a new timer with 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.
*/
final def startTimerWithFixedDelay(
key: Any,
msg: Any,
initialDelay: java.time.Duration,
delay: java.time.Duration): Unit =
startTimerWithFixedDelay(key, msg, initialDelay.asScala, delay.asScala)
/**
* Scala API: Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
@ -150,6 +191,33 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
*/
def startTimerAtFixedRate(key: Any, msg: Any, interval: FiniteDuration): Unit
/**
* Scala API: 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` after `initialDelay`.
*
* 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.
*
* Each timer has a key and if a new timer with 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.
*/
def startTimerAtFixedRate(key: Any, msg: Any, initialDelay: FiniteDuration, interval: FiniteDuration): Unit
/**
* Java API: Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency.
@ -178,6 +246,38 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
final def startTimerAtFixedRate(key: Any, msg: Any, interval: java.time.Duration): Unit =
startTimerAtFixedRate(key, msg, interval.asScala)
/**
* Java API: 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.
*
* Each timer has a key and if a new timer with 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.
*/
final def startTimerAtFixedRate(
key: Any,
msg: Any,
initialDelay: java.time.Duration,
interval: java.time.Duration): Unit =
startTimerAtFixedRate(key, msg, initialDelay.asScala, interval.asScala)
/**
* Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]].
*/

View file

@ -32,10 +32,10 @@ import akka.util.OptionVal
private sealed trait TimerMode {
def repeat: Boolean
}
private case object FixedRateMode extends TimerMode {
private case class FixedRateMode(initialDelay: FiniteDuration) extends TimerMode {
override def repeat: Boolean = true
}
private case object FixedDelayMode extends TimerMode {
private case class FixedDelayMode(initialDelay: FiniteDuration) extends TimerMode {
override def repeat: Boolean = true
}
private case object SingleMode extends TimerMode {
@ -58,10 +58,16 @@ import akka.util.OptionVal
}
override def startTimerAtFixedRate(key: Any, msg: Any, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, FixedRateMode)
startTimer(key, msg, interval, FixedRateMode(interval))
override def startTimerAtFixedRate(key: Any, msg: Any, initialDelay: FiniteDuration, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, FixedRateMode(initialDelay))
override def startTimerWithFixedDelay(key: Any, msg: Any, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, FixedDelayMode)
startTimer(key, msg, delay, FixedDelayMode(delay))
override def startTimerWithFixedDelay(key: Any, msg: Any, initialDelay: FiniteDuration, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, FixedDelayMode(initialDelay))
override def startPeriodicTimer(key: Any, msg: Any, interval: FiniteDuration): Unit =
startTimerAtFixedRate(key, msg, interval)
@ -85,10 +91,10 @@ import akka.util.OptionVal
val task = mode match {
case SingleMode =>
ctx.system.scheduler.scheduleOnce(timeout, ctx.self, timerMsg)(ctx.dispatcher)
case FixedDelayMode =>
ctx.system.scheduler.scheduleWithFixedDelay(timeout, timeout, ctx.self, timerMsg)(ctx.dispatcher)
case FixedRateMode =>
ctx.system.scheduler.scheduleAtFixedRate(timeout, timeout, ctx.self, timerMsg)(ctx.dispatcher)
case m: FixedDelayMode =>
ctx.system.scheduler.scheduleWithFixedDelay(m.initialDelay, timeout, ctx.self, timerMsg)(ctx.dispatcher)
case m: FixedRateMode =>
ctx.system.scheduler.scheduleAtFixedRate(m.initialDelay, timeout, ctx.self, timerMsg)(ctx.dispatcher)
}
val nextTimer = Timer(key, msg, mode.repeat, nextGen, task)