Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: reject zero and negative periodic tasks schedule (#887) #978

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,45 @@ class LightArrayRevolverSchedulerSpec extends PekkoSpec(SchedulerSpec.testConfRe
}
}

"using scheduleAtFixedRate" must {
"fixed rate reject periodic tasks scheduled with zero interval" taggedAs TimingTest in {
val zeroInterval = 0.seconds
import system.dispatcher
intercept[IllegalArgumentException] {
system.scheduler.scheduleAtFixedRate(100.millis, zeroInterval, testActor, "Not Allowed")
}
expectNoMessage(1.second)
}

"fixed rate reject periodic tasks scheduled with negative interval" taggedAs TimingTest in {
val negativeDelay = -1.seconds
import system.dispatcher
intercept[IllegalArgumentException] {
system.scheduler.scheduleAtFixedRate(100.millis, negativeDelay, testActor, "Not Allowed")
}
expectNoMessage(1.second)
}
}

"using scheduleWithFixedDelay" must {
"fixed delay reject periodic tasks scheduled with zero interval" taggedAs TimingTest in {
val zeroInterval = 0.seconds
import system.dispatcher
intercept[IllegalArgumentException] {
system.scheduler.scheduleWithFixedDelay(100.millis, zeroInterval, testActor, "Not Allowed")
}
expectNoMessage(1.second)
}

"fixed delay reject periodic tasks scheduled with negative interval" taggedAs TimingTest in {
val negativeDelay = -1.seconds
import system.dispatcher
intercept[IllegalArgumentException] {
system.scheduler.scheduleWithFixedDelay(100.millis, negativeDelay, testActor, "Not Allowed")
}
expectNoMessage(1.second)
}
}
// same tests for fixedDelay and fixedRate
List(new ScheduleWithFixedDelayAdapter, new ScheduleAtFixedRateAdapter).foreach { scheduleAdapter =>
s"using $scheduleAdapter" must {
Expand All @@ -684,6 +723,24 @@ class LightArrayRevolverSchedulerSpec extends PekkoSpec(SchedulerSpec.testConfRe
}
expectNoMessage(1.second)
}

"reject periodic tasks scheduled with zero interval" taggedAs TimingTest in {
val zeroInterval = 0.seconds
import system.dispatcher
intercept[IllegalArgumentException] {
scheduleAdapter.schedule(100.millis, zeroInterval, testActor, "Not Allowed")
}
expectNoMessage(1.second)
}

"reject periodic tasks scheduled with negative interval" taggedAs TimingTest in {
val negativeInterval = -1.seconds
import system.dispatcher
intercept[IllegalArgumentException] {
scheduleAdapter.schedule(100.millis, negativeInterval, testActor, "Not Allowed")
}
expectNoMessage(1.second)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,14 @@ class LightArrayRevolverScheduler(config: Config, log: LoggingAdapter, threadFac

override def scheduleWithFixedDelay(initialDelay: FiniteDuration, delay: FiniteDuration)(runnable: Runnable)(
implicit executor: ExecutionContext): Cancellable = {
checkPeriod(delay)
checkMaxDelay(roundUp(delay).toNanos)
super.scheduleWithFixedDelay(initialDelay, delay)(runnable)
}

override def schedule(initialDelay: FiniteDuration, delay: FiniteDuration, runnable: Runnable)(
implicit executor: ExecutionContext): Cancellable = {
checkPeriod(delay)
checkMaxDelay(roundUp(delay).toNanos)
try new AtomicReference[Cancellable](InitialRepeatMarker) with Cancellable { self =>
compareAndSet(
Expand Down Expand Up @@ -218,6 +220,12 @@ class LightArrayRevolverScheduler(config: Config, log: LoggingAdapter, threadFac
task
}

private def checkPeriod(delay: FiniteDuration): Unit =
if (delay.length <= 0)
throw new IllegalArgumentException(
s"Task scheduled with [${delay.toSeconds}] seconds delay, which means creating an infinite loop. " +
s"The expected delay must be greater than 0.")

private def checkMaxDelay(delayNanos: Long): Unit =
if (delayNanos / tickNanos > Int.MaxValue)
// 1 second margin in the error message due to rounding
Expand Down
18 changes: 9 additions & 9 deletions actor/src/main/scala/org/apache/pekko/actor/Scheduler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ trait Scheduler {
* If the `Runnable` throws an exception the repeated scheduling is aborted,
* i.e. the function will not be invoked any more.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `with Timers` should be preferred.
Expand Down Expand Up @@ -144,7 +144,7 @@ trait Scheduler {
* If the `Runnable` throws an exception the repeated scheduling is aborted,
* i.e. the function will not be invoked any more.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `AbstractActorWithTimers` should be preferred.
Expand Down Expand Up @@ -243,7 +243,7 @@ trait Scheduler {
* If the `Runnable` throws an exception the repeated scheduling is aborted,
* i.e. the function will not be invoked any more.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `with Timers` should be preferred.
Expand Down Expand Up @@ -279,7 +279,7 @@ trait Scheduler {
* If the `Runnable` throws an exception the repeated scheduling is aborted,
* i.e. the function will not be invoked any more.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `AbstractActorWithTimers` should be preferred.
Expand Down Expand Up @@ -443,7 +443,7 @@ trait Scheduler {
* Scala API: Schedules a message to be sent once with a delay, i.e. a time period that has
* to pass before the message is sent.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `with Timers` should be preferred.
Expand All @@ -461,7 +461,7 @@ trait Scheduler {
* Java API: Schedules a message to be sent once with a delay, i.e. a time period that has
* to pass before the message is sent.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `AbstractActorWithTimers` should be preferred.
Expand All @@ -480,7 +480,7 @@ trait Scheduler {
* Scala API: Schedules a function to be run once with a delay, i.e. a time period that has
* to pass before the function is run.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `with Timers` should be preferred.
Expand All @@ -494,7 +494,7 @@ trait Scheduler {
* Scala API: Schedules a Runnable to be run once with a delay, i.e. a time period that
* has to pass before the runnable is executed.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `with Timers` should be preferred.
Expand All @@ -505,7 +505,7 @@ trait Scheduler {
* Java API: Schedules a Runnable to be run once with a delay, i.e. a time period that
* has to pass before the runnable is executed.
*
* @throws IllegalArgumentException if the given delays exceed the maximum
* @throws IllegalArgumentException if the given delays is zero, negative or exceed the maximum
* reach (calculated as: `delay / tickNanos > Int.MaxValue`).
*
* Note: For scheduling within actors `AbstractActorWithTimers` should be preferred.
Expand Down