scalamonix

Monix - Why does this future complete?


Below is a simple program that awaits a cancellable future. The future should evaluate to wait 20 seconds. I'm trying to demonstrate the 5.second max time bound kicking in. However, the program seems to ignore the 5.second max time bound, and waits for 20 seconds. Does anyone know why this is? Thanks

import com.github.nscala_time.time.Imports.{DateTime, richReadableInstant, richReadableInterval}
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.Scheduler.Implicits.global

import scala.concurrent.duration._
import scala.concurrent.Await

object TaskRunToFuture extends App {

  def waitTwenty = {
    val start = DateTime.now()
    while ((start to DateTime.now()).millis < 20000) { /** do nothing */ }
   
  }

  val result = Await.result(Task.eval(waitTwenty).runToFuture, 5.seconds)

}

Solution

  • From Monix's Design Summary:

    doesn’t necessarily execute on another logical thread

    In your case, you've defined a Task that doesn't inherently have any "async boundaries" that would force Monix to shift to another thread, so when you call runToFuture it ends up executing waitTwenty on the current thread. Thus, the task has already completed by the time the Future is actually returned, so Await.result isn't actually called until the 20 seconds are up.

    See Task#executeAsync which inserts an async boundary before the task, e.g. Task.eval(waitTwenty).executeAsync.runToFuture

    Normally this kind of thing isn't an issue since you generally don't want to call any of the run* methods except in your application's main; instead you'll compose everything in terms of Task so your whole app is ultimately one big Task that can be run, e.g. as a TaskApp.

    def waitTwenty = {
      println("starting wait...")
      Thread.sleep(20000)
      println("done waiting!")
    }
    
    Await.result(Task.eval(waitTwenty).executeAsync.runToFuture, 5.seconds)
    

    Outputs

    waiting...
    java.util.concurrent.TimeoutException: Future timed out after [5 seconds]
      at scala.concurrent.impl.Promise$DefaultPromise.tryAwait0(Promise.scala:248)
      at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:261)
      at monix.execution.CancelableFuture$Async.result(CancelableFuture.scala:371)
      at scala.concurrent.Await$.$anonfun$result$1(package.scala:201)
      at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:62)
      at scala.concurrent.Await$.result(package.scala:124)
      ... 59 elided