9

Hi,

I'm using Scala 2.10 with the new futures library and I'm trying to write some code to test an infinite loop. I use a scala.concurrent.Future to run the code with the loop in a separate thread. I would then like to wait a little while to do some testing and then kill off the separate thread/future. I have looked at Await.result but that doesn't actually kill the future. Is there any way to timeout or kill the new Scala 2.10 futures?

I would prefer not having to add external dependencies such as Akka just for this simple part.

Stephan
  • 191
  • 1
  • 8
  • 1
    No need for Akka - actually, Akka doesn't have futures after 2.1. Akka's futures moved to `scala.concurrent`. – sourcedelica Jan 22 '13 at 01:34
  • relevant, this blog post by Viktor Klang: https://viktorklang.com/blog/Futures-in-Scala-protips-6.html (not sure the year: 2017? 2018?) – Seth Tisue Mar 12 '19 at 20:10

3 Answers3

7

Do not try it at home.

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

class MyCustomExecutionContext extends AnyRef with ExecutionContext {
  import ExecutionContext.Implicits.global
  @volatile var lastThread: Option[Thread] = None
  override def execute(runnable: Runnable): Unit = {
    ExecutionContext.Implicits.global.execute(new Runnable() {
      override def run() {
        lastThread = Some(Thread.currentThread)
        runnable.run()
      }
    })
  }
  override def reportFailure(t: Throwable): Unit = ???
}    

implicit val exec = new MyCustomExecutionContext()
val f = future[Int]{ do{}while(true); 1 }
try {
  Await.result(f, 10 seconds) // 100% cpu here
} catch {
  case e: TimeoutException => 
    println("Stopping...")
    exec.lastThread.getOrElse(throw new RuntimeException("Not started"))
      .stop() // 0% cpu here
}
idonnie
  • 1,703
  • 12
  • 11
  • I was hoping for a slightly more elegant solutuion:). It seems to me like this is something you would want to do every so often. I'll leave the question open for a while longer in the hope someone comes up with something nicer before I accept. – Stephan Jan 22 '13 at 02:56
  • I would suggest 1) using `while(!terminated)` as in `sourcedelica`'s answer, and 2) create future's closure around `AtomicReference`-s to resources that need to be closed, then close them on completion, and on timeout. – idonnie Jan 22 '13 at 08:27
1

No - you will have to add a flag that your loop checks. If the flag is set, stop the loop. Make sure the flag is at least volatile.

See Java Concurrency in Practice, p 135-137.

sourcedelica
  • 23,940
  • 7
  • 66
  • 74
  • 2
    The thought of that gives me the willies. – DWright Jan 22 '13 at 01:50
  • No worries. Use `interrupt` then `wait` a bit, after than in a case `if (thread.isAlive)` do `connection.close()` and `stop` as a last resort. Customers never complied to me, of being able to actually interrupt a LONG queries in that way =) – idonnie Jan 22 '13 at 01:59
1

I had a similar problem and wrote the following nonblocking future op:

class TerminationToken(var isTerminated: Boolean)
object TerminationToken { def apply() = new TerminationToken(false) }

 implicit class FutureOps[T](future: Future[Option[T]]) {
 def terminate(timeout: FiniteDuration, token: TerminationToken): Future[Option[T]] = {
   val timeoutFuture = after[Option[T]](timeout, using = context.system.scheduler) {
     Future[Option[T]] { token.isTerminated = true; None } }
          Future.firstCompletedOf[Option[T]](Seq (future recover { case _ => None }, timeoutFuture))
     }
   }

Then just create a future that returns an option, and use .terminate(timeout, token) on it

aviemzur
  • 171
  • 2
  • 5