diff options
author | Martin Odersky <odersky@gmail.com> | 2011-03-24 16:32:15 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2011-03-24 16:32:15 +0000 |
commit | 469a08c1ed9b7e51470e29aaba641bd372e8966e (patch) | |
tree | 631f60323314150660e9e6133979043ab6f8ac56 /src/compiler | |
parent | d0e519a3091ce7e14781a8b929c35a3e228194d8 (diff) | |
download | scala-469a08c1ed9b7e51470e29aaba641bd372e8966e.tar.gz scala-469a08c1ed9b7e51470e29aaba641bd372e8966e.tar.bz2 scala-469a08c1ed9b7e51470e29aaba641bd372e8966e.zip |
Three things to make Eclipse more robust agains...
Three things to make Eclipse more robust against deadlocks: (1) catch
stale responses in presentation compile thread. (2) Avoid stale
responses by two try-finallys in getEnteredParsed, askLoadedTyped.
Diffstat (limited to 'src/compiler')
3 files changed, 41 insertions, 20 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index 7b9a7fdb85..2ced0788c1 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -103,6 +103,10 @@ trait CompilerControl { self: Global => throw new FatalError("no context found for "+pos) } + private def postWorkItem(item: WorkItem) { + scheduler.postWorkItem(item) + } + /** Makes sure a set of compilation units is loaded and parsed. * Returns () to syncvar `response` on completions. * Afterwards a new background compiler run is started with @@ -113,15 +117,15 @@ trait CompilerControl { self: Global => case ri: ReloadItem if ri.sources == sources => Some(ri) case _ => None } - superseeded foreach (_.response.set()) - scheduler postWorkItem new ReloadItem(sources, response) + superseeded.foreach(_.response.set()) + postWorkItem(new ReloadItem(sources, response)) } /** Sets sync var `response` to the smallest fully attributed tree that encloses position `pos`. * Note: Unlike for most other ask... operations, the source file belonging to `pos` needs not be be loaded. */ def askTypeAt(pos: Position, response: Response[Tree]) = - scheduler postWorkItem new AskTypeAtItem(pos, response) + postWorkItem(new AskTypeAtItem(pos, response)) /** Sets sync var `response` to the fully attributed & typechecked tree contained in `source`. * @pre `source` needs to be loaded. @@ -131,7 +135,7 @@ trait CompilerControl { self: Global => println("ask type called") new Exception().printStackTrace() } - scheduler postWorkItem new AskTypeItem(source, forceReload, response) + postWorkItem(new AskTypeItem(source, forceReload, response)) } /** Sets sync var `response` to the position of the definition of the given link in @@ -146,25 +150,25 @@ trait CompilerControl { self: Global => * is unloaded, it stays that way. */ def askLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) = - scheduler postWorkItem new AskLinkPosItem(sym, source, response) + postWorkItem(new AskLinkPosItem(sym, source, response)) /** Sets sync var `response' to list of members that are visible * as members of the tree enclosing `pos`, possibly reachable by an implicit. * @pre source is loaded */ def askTypeCompletion(pos: Position, response: Response[List[Member]]) = - scheduler postWorkItem new AskTypeCompletionItem(pos, response) + postWorkItem(new AskTypeCompletionItem(pos, response)) /** Sets sync var `response' to list of members that are visible * as members of the scope enclosing `pos`. * @pre source is loaded */ def askScopeCompletion(pos: Position, response: Response[List[Member]]) = - scheduler postWorkItem new AskScopeCompletionItem(pos, response) + postWorkItem(new AskScopeCompletionItem(pos, response)) /** Asks to do unit corresponding to given source file on present and subsequent type checking passes */ def askToDoFirst(source: SourceFile) = - scheduler postWorkItem new AskToDoFirstItem(source) + postWorkItem(new AskToDoFirstItem(source)) /** If source is not yet loaded, loads it, and starts a new run, otherwise * continues with current pass. @@ -175,7 +179,7 @@ trait CompilerControl { self: Global => * the a NoSuchUnitError is raised in the response. */ def askLoadedTyped(source: SourceFile, response: Response[Tree]) = - scheduler postWorkItem new AskLoadedTypedItem(source, response) + postWorkItem(new AskLoadedTypedItem(source, response)) /** If source if not yet loaded, get an outline view with askParseEntered. * If source is loaded, wait for it to be typechecked. @@ -196,7 +200,7 @@ trait CompilerControl { self: Global => * @param response The response. */ def askParsedEntered(source: SourceFile, keepLoaded: Boolean, response: Response[Tree]) = - scheduler postWorkItem new AskParsedEnteredItem(source, keepLoaded, response) + postWorkItem(new AskParsedEnteredItem(source, keepLoaded, response)) /** Cancels current compiler run and start a fresh one where everything will be re-typechecked * (but not re-loaded). @@ -314,3 +318,5 @@ object ShutdownReq extends ControlThrowable class NoSuchUnitError(file: AbstractFile) extends Exception("no unit found for file "+file) +class MissingResponse extends Exception("response missing") + diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 155597b559..95aec8b0a2 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -120,6 +120,22 @@ class Global(settings: Settings, reporter: Reporter) } } + private def cleanAllResponses() { + cleanResponses(waitLoadedTypeResponses) + cleanResponses(getParsedEnteredResponses) + } + + private def checkNoOutstanding(rmap: ResponseMap): Unit = + for ((_, rs) <- rmap.toList; r <- rs) { + debugLog("ERROR: missing response, request will be discarded") + r raise new MissingResponse + } + + def checkNoResponsesOutstanding() { + checkNoOutstanding(waitLoadedTypeResponses) + checkNoOutstanding(getParsedEnteredResponses) + } + /** The compilation unit corresponding to a source file * if it does not yet exist create a new one atomically * Note: We want to remove this. @@ -417,8 +433,7 @@ class Global(settings: Settings, reporter: Reporter) } // clean out stale waiting responses - cleanResponses(waitLoadedTypeResponses) - cleanResponses(getParsedEnteredResponses) + cleanAllResponses() // wind down if (waitLoadedTypeResponses.nonEmpty || getParsedEnteredResponses.nonEmpty) { @@ -841,8 +856,8 @@ class Global(settings: Settings, reporter: Reporter) else { debugLog("wait for later"); outOfDate = true; waitLoadedTypeResponses(source) += response } case None => debugLog("load unit and type") - reloadSources(List(source)) - waitLoadedTyped(source, response) + try reloadSources(List(source)) + finally waitLoadedTyped(source, response) } } @@ -852,14 +867,13 @@ class Global(settings: Settings, reporter: Reporter) case Some(unit) => getParsedEnteredNow(source, response) case None => - if (keepLoaded) { - reloadSources(List(source)) - getParsedEnteredNow(source, response) - } else if (outOfDate) { + if (keepLoaded) + try reloadSources(List(source)) + finally getParsedEnteredNow(source, response) + else if (outOfDate) getParsedEnteredResponses(source) += response - } else { + else getParsedEnteredNow(source, response) - } } } diff --git a/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala b/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala index 55f8264020..b61d7f6638 100644 --- a/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala +++ b/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala @@ -20,6 +20,7 @@ class PresentationCompilerThread(var compiler: Global, threadId: Int) extends Th compiler.debugLog("starting new runner thread") try { while (true) { + compiler.checkNoResponsesOutstanding() compiler.log.logreplay("wait for more work", { compiler.scheduler.waitForMoreWork(); true }) compiler.pollForWork(compiler.NoPosition) while (compiler.isOutOfDate) { |