Skip to content

Commit

Permalink
Fixed SI-6505. Respond to ask calls by immediate failure even after c…
Browse files Browse the repository at this point in the history
…ompiler shutdown.

When the compiler is asked to shutdown, it may still have items on the working queue, and more can be added by clients in other thread that don't *know* the compiler is down yet. These requests were never serviced, leading to deadlocks or timeouts.

review by @odersky, @hubertp
  • Loading branch information
dragos committed Oct 10, 2012
1 parent 859ec02 commit 19ea47b
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 1 deletion.
11 changes: 11 additions & 0 deletions src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
Expand Up @@ -13,6 +13,7 @@ import scala.tools.nsc.util.FailedInterrupt
import scala.tools.nsc.util.EmptyAction
import scala.tools.nsc.util.WorkScheduler
import scala.reflect.internal.util.{SourceFile, Position}
import scala.tools.nsc.util.InterruptReq

/** Interface of interactive compiler to a client such as an IDE
* The model the presentation compiler consists of the following parts:
Expand Down Expand Up @@ -413,6 +414,16 @@ trait CompilerControl { self: Global =>
override def doQuickly[A](op: () => A): A = {
throw new FailedInterrupt(new Exception("Posted a work item to a compiler that's shutting down"))
}

override def askDoQuickly[A](op: () => A): InterruptReq { type R = A } = {
val ir = new InterruptReq {
type R = A
val todo = () => throw new MissingResponse
}
ir.execute()
ir
}

}

}
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/scala/tools/nsc/util/InterruptReq.scala
Expand Up @@ -47,7 +47,10 @@ abstract class InterruptReq {
}

def onComplete(k: Continuation) = synchronized {
waiting = k :: waiting
if (result.isDefined)
k(result.get)
else
waiting = k :: waiting
}
}

Expand Down
33 changes: 33 additions & 0 deletions test/files/presentation/forgotten-ask.scala
@@ -0,0 +1,33 @@
import scala.tools.nsc.interactive._
import tests._

/** Test that no ask calls are left unanswered after a compiler has shut down. */
object Test extends InteractiveTest {
import compiler._

def askItem(): Response[Unit] = {
compiler.askForResponse { () =>
Thread.sleep(100)
}
}

final val Timeout = 5000 //ms

override def main(args: Array[String]) {
val item1 = askItem()

compiler.askShutdown()

Thread.sleep(1000) // wait a bit, the compiler is shutting down
val item2 = askItem()

item1.get(Timeout) match {
case None => println("TIMEOUT")
case _ =>
}
item2.get(Timeout) match {
case None => println("TIMEOUT")
case _ =>
}
}
}

0 comments on commit 19ea47b

Please sign in to comment.