Cancellation with Future and Promise in Scala

后端 未结 4 1382
情歌与酒
情歌与酒 2020-12-06 02:02

This is a followup to my previous question.

Suppose I have a task, which executes an interruptible blocking call. I would like to run it as a Future<

相关标签:
4条回答
  • 2020-12-06 02:35

    You can use Monix library instead of Future

    https://monix.io

    0 讨论(0)
  • 2020-12-06 02:42

    Twitter's futures implement cancellation. Have a look here:

    https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Future.scala

    Line 563 shows the abstract method responsible for this. Scala's futures currently do not support cancellation.

    0 讨论(0)
  • 2020-12-06 02:51

    Here is the interruptable version of Victor's code per his comments (Victor, please correct me if I misinterpreted).

    object CancellableFuture extends App {
    
      def interruptableFuture[T](fun: () => T)(implicit ex: ExecutionContext): (Future[T], () => Boolean) = {
        val p = Promise[T]()
        val f = p.future
        val aref = new AtomicReference[Thread](null)
        p tryCompleteWith Future {
          val thread = Thread.currentThread
          aref.synchronized { aref.set(thread) }
          try fun() finally {
            val wasInterrupted = (aref.synchronized { aref getAndSet null }) ne thread
            //Deal with interrupted flag of this thread in desired
          }
        }
    
        (f, () => {
          aref.synchronized { Option(aref getAndSet null) foreach { _.interrupt() } }
          p.tryFailure(new CancellationException)
        })
      }
    
      val (f, cancel) = interruptableFuture[Int] { () =>
        val latch = new CountDownLatch(1)
    
        latch.await(5, TimeUnit.SECONDS)    // Blocks for 5 sec, is interruptable
        println("latch timed out")
    
        42  // Completed
      }
    
      f.onFailure { case ex => println(ex.getClass) }
      f.onSuccess { case i => println(i) }
    
      Thread.sleep(6000)   // Set to less than 5000 to cancel
    
      val wasCancelled = cancel()
    
      println("wasCancelled: " + wasCancelled)
    }
    

    With Thread.sleep(6000) the output is:

    latch timed out
    42
    wasCancelled: false
    

    With Thread.sleep(1000) the output is:

    wasCancelled: true
    class java.util.concurrent.CancellationException
    
    0 讨论(0)
  • 2020-12-06 02:53

    scala.concurrent.Future is read-only, so one reader cannot mess things up for the other readers.

    It seems like you should be able to implement what you want as follows:

    def cancellableFuture[T](fun: Future[T] => T)(implicit ex: ExecutionContext): (Future[T], () => Boolean) = {
      val p = Promise[T]()
      val f = p.future
      p tryCompleteWith Future(fun(f))
      (f, () => p.tryFailure(new CancellationException))
    }
    
    val (f, cancel) = cancellableFuture( future => {
      while(!future.isCompleted) continueCalculation // isCompleted acts as our interrupted-flag
    
      result  // when we're done, return some result
    })
    
    val wasCancelled = cancel() // cancels the Future (sets its result to be a CancellationException conditionally)
    
    0 讨论(0)
提交回复
热议问题