Monix: Version 2.0.4 Released for Bug Fixing

This release is binary compatible with 2.0.x, being a bug fix release for Issue #244: AsyncScheduler.scheduleAtFixedRate and scheduleWithFixedDelay on the JVM have incorrect behavior.

In order to upgrade, just increment the version number in build.sbt:

libraryDependencies += "io.monix" %% "monix" % "2.0.4"


The AsyncScheduler.scheduleAtFixedRate method implementation has incorrect behavior and this includes Sample:

import java.util.concurrent.TimeUnit

global.scheduleAtFixedRate(1, 2, TimeUnit.SECONDS, runnable)

global.scheduleWithFixedDelay(1, 2, TimeUnit.SECONDS, runnable)

What happens is that if the given runnable takes more than 2 seconds to execute, then subsequent runnables are going to be executed concurrently, without waiting for the current runnable to finish. So if runnables are slow, you can end up with 2 or even more runnables being executed at the same time.

This happens because this periodic scheduling is deferred to the global Scheduler.DefaultScheduledExecutor instance, a single threaded Java ScheduledExecutor that then defers the actual execution of the task to the ForkJoinPool in charge of the actual execution. But by doing this deferring, the time it takes that runnable to execute is not taken into account at all. This behavior is inconsistent with how these operations work for Java’s ScheduledExecutor and with how they are documented in ScalaDoc.

NOTE: this behavior does NOT happen for ExecutorScheduler (e.g., Scheduler.fixedPool, Scheduler.singleThread) or any of the 2 available scheduler implementations for Javascript. It also doesn’t affect Observable.interval, Observable.intervalWithFixedDelay and Observable.intervalAtFixedRate. These observable builders have had correct behavior, regardless of the scheduler used.

The fix involves the AsyncScheduler implementation to piggy-back on the implementation in ReferenceScheduler, just like the Javascript scheduler implementations.