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 { object TimerScheduled {
import akka.util.JavaDurationConverters._
sealed trait TimerMode sealed trait TimerMode
case object FixedRateMode extends TimerMode case object FixedRateMode extends TimerMode
case class FixedRateModeWithInitialDelay(initialDelay: FiniteDuration) extends TimerMode
case object FixedDelayMode extends TimerMode case object FixedDelayMode extends TimerMode
case class FixedDelayModeWithInitialDelay(initialDelay: FiniteDuration) extends TimerMode
case object SingleMode extends TimerMode case object SingleMode extends TimerMode
/*Java API*/ /*Java API*/
def fixedRateMode = FixedRateMode def fixedRateMode = FixedRateMode
def fixedRateMode(initialDelay: java.time.Duration) = FixedRateModeWithInitialDelay(initialDelay.asScala)
def fixedDelayMode = FixedDelayMode def fixedDelayMode = FixedDelayMode
def fixedDelayMode(initialDelay: java.time.Duration) = FixedDelayModeWithInitialDelay(initialDelay.asScala)
def singleMode = SingleMode def singleMode = SingleMode
} }

View file

@ -91,9 +91,15 @@ import scala.reflect.ClassTag
override def startTimerWithFixedDelay(key: Any, msg: T, delay: FiniteDuration): Unit = override def startTimerWithFixedDelay(key: Any, msg: T, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, Effect.TimerScheduled.FixedDelayMode) 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 = override def startTimerAtFixedRate(key: Any, msg: T, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, Effect.TimerScheduled.FixedRateMode) 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 = override def startPeriodicTimer(key: Any, msg: T, interval: FiniteDuration): Unit =
startTimer(key, msg, interval, Effect.TimerScheduled.FixedRateMode) startTimer(key, msg, interval, Effect.TimerScheduled.FixedRateMode)

View file

@ -123,8 +123,12 @@ object BehaviorTestKitSpec {
case ScheduleCommand(key, delay, mode, cmd) => case ScheduleCommand(key, delay, mode, cmd) =>
mode match { mode match {
case Effect.TimerScheduled.SingleMode => timers.startSingleTimer(key, cmd, delay) case Effect.TimerScheduled.SingleMode => timers.startSingleTimer(key, cmd, delay)
case Effect.TimerScheduled.FixedDelayMode => timers.startTimerWithFixedDelay(key, cmd, delay) case Effect.TimerScheduled.FixedDelayMode => timers.startTimerWithFixedDelay(key, cmd, delay, delay)
case Effect.TimerScheduled.FixedRateMode => timers.startTimerAtFixedRate(key, cmd, 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 Behaviors.same
case CancelScheduleCommand(key) => case CancelScheduleCommand(key) =>
@ -445,16 +449,17 @@ class BehaviorTestKitSpec extends AnyWordSpec with Matchers with LogCapturing {
} }
"schedule and fire timers multiple times" in { "schedule and fire timers multiple times" in {
val delay = 42.seconds
val testkit = BehaviorTestKit[Parent.Command](Parent.init) 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 { val send = testkit.expectEffectPF {
case e @ Effect.TimerScheduled( case e @ Effect.TimerScheduled(
"abc", "abc",
SpawnChild, SpawnChild,
finiteDuration, finiteDuration,
Effect.TimerScheduled.FixedRateMode, Effect.TimerScheduled.FixedRateModeWithInitialDelay(`delay`),
false /*not overriding*/ ) => false /*not overriding*/ ) =>
finiteDuration should equal(42.seconds) finiteDuration should equal(delay)
e.send e.send
} }
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 { private sealed trait TimerMode {
def repeat: Boolean def repeat: Boolean
} }
private case object FixedRateMode extends TimerMode { private case class FixedRateMode(initialDelay: FiniteDuration) extends TimerMode {
override def repeat: Boolean = true override def repeat: Boolean = true
} }
private case object FixedDelayMode extends TimerMode { private case class FixedDelayMode(initialDelay: FiniteDuration) extends TimerMode {
override def repeat: Boolean = true override def repeat: Boolean = true
} }
private case object SingleMode extends TimerMode { 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 = override final def startTimerWithFixedDelay(key: Any, msg: T, delay: Duration): Unit =
startTimerWithFixedDelay(key, msg, delay.asScala) 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 = override final def startTimerAtFixedRate(key: Any, msg: T, interval: Duration): Unit =
startTimerAtFixedRate(key, msg, interval.asScala) 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 = { override final def startPeriodicTimer(key: Any, msg: T, interval: Duration): Unit = {
//this follows the deprecation note in the super class //this follows the deprecation note in the super class
startTimerWithFixedDelay(key, msg, interval.asScala) startTimerWithFixedDelay(key, msg, interval.asScala)
@ -83,13 +89,19 @@ import scala.concurrent.duration.FiniteDuration
private val timerGen = Iterator.from(1) private val timerGen = Iterator.from(1)
override def startTimerAtFixedRate(key: Any, msg: T, interval: FiniteDuration): Unit = 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 = 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 = 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 = override def startSingleTimer(key: Any, msg: T, delay: FiniteDuration): Unit =
startTimer(key, msg, delay, SingleMode) startTimer(key, msg, delay, SingleMode)
@ -110,11 +122,11 @@ import scala.concurrent.duration.FiniteDuration
val task = mode match { val task = mode match {
case SingleMode => case SingleMode =>
ctx.system.scheduler.scheduleOnce(delay, () => ctx.self.unsafeUpcast ! timerMsg)(ExecutionContexts.parasitic) ctx.system.scheduler.scheduleOnce(delay, () => ctx.self.unsafeUpcast ! timerMsg)(ExecutionContexts.parasitic)
case FixedDelayMode => case m: FixedDelayMode =>
ctx.system.scheduler.scheduleWithFixedDelay(delay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)( ctx.system.scheduler.scheduleWithFixedDelay(m.initialDelay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)(
ExecutionContexts.parasitic) ExecutionContexts.parasitic)
case FixedRateMode => case m: FixedRateMode =>
ctx.system.scheduler.scheduleAtFixedRate(delay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)( ctx.system.scheduler.scheduleAtFixedRate(m.initialDelay, delay)(() => ctx.self.unsafeUpcast ! timerMsg)(
ExecutionContexts.parasitic) ExecutionContexts.parasitic)
} }

View file

@ -40,6 +40,24 @@ trait TimerScheduler[T] {
*/ */
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 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 * Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages. * fixed `delay` between messages.
@ -60,6 +78,26 @@ trait TimerScheduler[T] {
def startTimerWithFixedDelay(msg: T, delay: java.time.Duration): Unit = def startTimerWithFixedDelay(msg: T, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(msg, msg, delay) 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 * Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * given frequency.
@ -87,6 +125,33 @@ trait TimerScheduler[T] {
*/ */
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` 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 * Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * given frequency.
@ -116,6 +181,35 @@ trait TimerScheduler[T] {
def startTimerAtFixedRate(msg: T, interval: java.time.Duration): Unit = def startTimerAtFixedRate(msg: T, interval: java.time.Duration): Unit =
startTimerAtFixedRate(msg, msg, interval) 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]]. * 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 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 * Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages. * fixed `delay` between messages.
@ -60,6 +78,26 @@ trait TimerScheduler[T] {
def startTimerWithFixedDelay(msg: T, delay: FiniteDuration): Unit = def startTimerWithFixedDelay(msg: T, delay: FiniteDuration): Unit =
startTimerWithFixedDelay(msg, msg, delay) 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 * Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * given frequency.
@ -87,6 +125,33 @@ trait TimerScheduler[T] {
*/ */
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` 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 * Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * given frequency.
@ -116,6 +181,35 @@ trait TimerScheduler[T] {
def startTimerAtFixedRate(msg: T, interval: FiniteDuration): Unit = def startTimerAtFixedRate(msg: T, interval: FiniteDuration): Unit =
startTimerAtFixedRate(msg, msg, interval) 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]]. * 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 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 * Java API: Schedules a message to be sent repeatedly to the `self` actor with a
* fixed `delay` between messages. * 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 = final def startTimerWithFixedDelay(key: Any, msg: Any, delay: java.time.Duration): Unit =
startTimerWithFixedDelay(key, msg, delay.asScala) 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 * Scala API: Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * given frequency.
@ -150,6 +191,33 @@ abstract class AbstractActorWithTimers extends AbstractActor with Timers {
*/ */
def startTimerAtFixedRate(key: Any, msg: Any, interval: FiniteDuration): Unit 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 * Java API: Schedules a message to be sent repeatedly to the `self` actor with a
* given frequency. * 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 = final def startTimerAtFixedRate(key: Any, msg: Any, interval: java.time.Duration): Unit =
startTimerAtFixedRate(key, msg, interval.asScala) 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]]. * Deprecated API: See [[TimerScheduler#startTimerWithFixedDelay]] or [[TimerScheduler#startTimerAtFixedRate]].
*/ */

View file

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