From 0a4089a8bafaa3b3822989a6790ce50bece44239 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 26 May 2009 10:55:20 +0000 Subject: SyncVars now return Either with a Throwable --- .../scala/tools/nsc/interactive/Global.scala | 172 +++++++++++---------- .../scala/tools/nsc/util/WorkScheduler.scala | 23 +-- 2 files changed, 98 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index ea2a396e09..3e47f164c7 100755 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -10,46 +10,47 @@ import scala.tools.nsc.ast._ /** The main class of the presentation compiler in an interactive environment such as an IDE */ -class Global(_settings: Settings, _reporter: Reporter) - extends nsc.Global(_settings, _reporter) - with ContextTrees { +class Global(settings: Settings, reporter: Reporter) + extends nsc.Global(settings, reporter) with ContextTrees { self => /** A list indicating in which order some units should be typechecked. * All units in firsts are typechecked before any unit not in this list - * Modified by askToDoFirst + * Modified by askToDoFirst, reload, typeAtTree. */ - var firsts: List[AbstractFile] = List() + var firsts: List[SourceFile] = List() - /** Is there a loaded unit not up-to-date wrt typechecking? - */ - var outOfDate: Boolean = false - - /** A map of all loaded units to the id of the last compiler run that typechecked them - * @see NotLoaded, JustParsed + /** A map of all loaded files units to the rich compilation units that corresponds to them. */ val unitOfFile = new LinkedHashMap[AbstractFile, RichCompilationUnit] with SynchronizedMap[AbstractFile, RichCompilationUnit] + /** The currently active typer run */ + private var currentTyperRun: TyperRun = _ + + /** Is a background compiler currently running? */ + private var compiling = false + + /** Is a reload/ background compiler currently running? */ + private var acting = false + /** The status value of a unit that has not yet been loaded */ final val NotLoaded = -1 /** The status value of a unit that has not yet been typechecked */ final val JustParsed = 0 - /** The currently active typer run */ - var currentTyperRun: TyperRun = _ - - /** Is a background compiler currently running? */ - var compiling = false - // ----------- Overriding hooks in nsc.Global ----------------------- - /** Make rangePos return a RangePosition */ + /** Create a RangePosition */ override def rangePos(source: SourceFile, start: Int, mid: Int, end: Int) = new RangePosition(source, start, mid, end) - /** Called from typechecker */ + /** Called from typechecker: signal that a node has been completely typechecked + * @param context The context that typechecked the node + * @param old The original node + * @param result The transformed node + */ override def signalDone(context: Context, old: Tree, result: Tree) { def integrateNew() { context.unit.body = new TreeReplacer(old, result) transform context.unit.body @@ -58,13 +59,17 @@ self => integrateNew() throw new TyperResult(result) } + val typerRun = currentTyperRun pollForWork() - if (outOfDate) { + if (typerRun != currentTyperRun) { integrateNew() throw new FreshRunReq } } + /** Called every time a context is created + * Register the context in a context tree + */ override def registerContext(c: Context) = c.unit match { case u: RichCompilationUnit => addContext(u.contexts, c) case _ => @@ -72,26 +77,29 @@ self => // ----------------- Polling --------------------------------------- - private var pollingEnabled = true - - /** Called from runner thread and singnalDone */ + /** Called from runner thread and signalDone: + * Poll for exeptions. + * Poll for work reload/typedTreeAt/doFirst commands during background checking. + */ def pollForWork() { - if (pollingEnabled) - scheduler.nextWorkItem() match { - case Some(action) => - pollingEnabled = false - try { - action() - } catch { - case ex: CancelActionReq => - } finally { - scheduler.doneWorkItem() - } - pollingEnabled = true - case None => - } - else - scheduler.pollException() + scheduler.pollException() match { + case Some(ex: CancelActionReq) => if (acting) throw ex + case Some(ex: FreshRunReq) => if (compiling) throw ex + case Some(ex) => throw ex + case _ => + } + scheduler.nextWorkItem() match { + case Some(action) => + try { + acting = true + action() + } catch { + case ex: CancelActionReq => + } finally { + acting = false + } + case None => + } } // ----------------- The Background Runner Thread ----------------------- @@ -101,20 +109,21 @@ self => /** The current presentation compiler runner */ private var compileRunner = newRunnerThread - compileRunner.start() - /** Create a new presentation compiler runner */ + /** Create a new presentation compiler runner. + */ def newRunnerThread: Thread = new Thread("Scala Presentation Compiler") { override def run() { try { while (true) { scheduler.waitForMoreWork() pollForWork() - while (outOfDate) { - outOfDate = false + var continue = true + while (continue) { try { compiling = true backgroundCompile() + continue = false } catch { case ex: FreshRunReq => } finally { @@ -129,31 +138,19 @@ self => ex.printStackTrace() inform("Fatal Error: "+ex) compileRunner = newRunnerThread - compileRunner.start() } } + start() } /** Compile all given units */ private def backgroundCompile() { inform("Starting new presentation compiler type checking pass") - val unitsToCompile = new LinkedHashSet[RichCompilationUnit] ++= unitOfFile.values - var rest = firsts - def getUnit(): RichCompilationUnit = rest match { - case List() => - unitsToCompile.head - case f :: fs => - unitsToCompile.find(_.source.file == f) match { - case Some(unit) => unit - case None => rest = fs; getUnit() - } - } - while (unitsToCompile.nonEmpty) { - val unit = getUnit() - recompile(unit) - unitsToCompile -= unit - } + firsts = firsts filter (s => unitOfFile contains (s.file)) + val prefix = firsts map unitOf + val units = prefix ::: (unitOfFile.values.toList diff prefix) + units foreach recompile } /** Reset unit to just-parsed state */ @@ -181,32 +178,44 @@ self => } /** Move list of files to front of firsts */ - def moveToFront(fs: List[AbstractFile]) { + def moveToFront(fs: List[SourceFile]) { firsts = fs ::: (firsts diff fs) } // ----------------- Implementations of client commmands ----------------------- /** Make sure a set of compilation units is loaded and parsed */ - def reload(sources: Set[SourceFile], reloaded: SyncVar[Unit]) = { - currentTyperRun = new TyperRun() - for (source <- sources) { - val unit = unitOf(source) - currentTyperRun.compileLate(unit) - unit.status = JustParsed + def reload(sources: List[SourceFile], result: SyncVar[Either[Unit, Throwable]]) { + try { + currentTyperRun = new TyperRun() + for (source <- sources) { + val unit = new RichCompilationUnit(source) + unitOfFile(source.file) = unit + currentTyperRun.compileLate(unit) + unit.status = JustParsed + } + moveToFront(sources) + result set Left(()) + if (compiling) throw new FreshRunReq + } catch { + case ex => + result set Right(ex) + throw ex } - outOfDate = true - moveToFront(sources.toList map (_.file)) - reloaded.set(()) - if (compiling) throw new FreshRunReq } /** Set sync var `result` to a fully attributed tree located at position `pos` */ - def typedTreeAt(pos: Position, result: SyncVar[Tree]) { - val unit = unitOf(pos) - assert(unit.status != NotLoaded) - moveToFront(List(unit.source.file)) - result set currentTyperRun.typedTreeAt(pos) + def typedTreeAt(pos: Position, result: SyncVar[Either[Tree, Throwable]]) { + try { + val unit = unitOf(pos) + assert(unit.status != NotLoaded) + moveToFront(List(unit.source)) + result set Left(currentTyperRun.typedTreeAt(pos)) + } catch { + case ex => + result set Right(ex) + throw ex + } } // ---------------- Helper classes --------------------------- @@ -299,6 +308,9 @@ self => class RichCompilationUnit(source: SourceFile) extends CompilationUnit(source) { + /** The runid of the latest compiler run that typechecked this unit, + * or else @see NotLoaded, JustParsed + */ var status: Int = NotLoaded /** the current edit point offset */ @@ -343,15 +355,15 @@ self => locateContext(unitOf(pos).contexts, pos) /** Make sure a set of compilation units is loaded and parsed */ - def askReload(sources: Set[SourceFile], reloaded: SyncVar[Unit]) = - scheduler.postWorkItem(() => reload(sources, reloaded)) + def askReload(sources: List[SourceFile], result: SyncVar[Either[Unit, Throwable]]) = + scheduler.postWorkItem(() => reload(sources, result)) /** Set sync var `result` to a fully attributed tree located at position `pos` */ - def askTypeAt(pos: Position, result: SyncVar[Tree]) = + def askTypeAt(pos: Position, result: SyncVar[Either[Tree, Throwable]]) = scheduler.postWorkItem(() => self.typedTreeAt(pos, result)) /** Ask to do unit first on present and subsequent type checking passes */ - def askToDoFirst(f: AbstractFile) = { + def askToDoFirst(f: SourceFile) = { scheduler.postWorkItem { () => moveToFront(List(f)) } } diff --git a/src/compiler/scala/tools/nsc/util/WorkScheduler.scala b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala index bcd57ba6c2..eced845136 100644 --- a/src/compiler/scala/tools/nsc/util/WorkScheduler.scala +++ b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala @@ -8,7 +8,6 @@ class WorkScheduler { private var todo = new Queue[Action] private var except: Option[Exception] = None - private var working = false /** Called from server: block until todo list is nonempty */ def waitForMoreWork() = synchronized { @@ -23,24 +22,15 @@ class WorkScheduler { /** Called from server: get first action in todo list, and pop it off */ def nextWorkItem(): Option[Action] = synchronized { if (!todo.isEmpty) { - working = true Some(todo.dequeue()) } else None } - /** Called from server: raise any exception posted by client */ - def pollException() = synchronized { - except match { - case Some(exc) => throw exc - case None => - } - } - - /** Called from server: mark workitem as finished (influences - * meaning of raise) + /** Called from server: return optional exception posted by client + * Reset to no exception. */ - def doneWorkItem() = synchronized { - working = false + def pollException(): Option[Exception] = synchronized { + val result = except; except = None; result } /** Called from client: have action executed by server */ @@ -55,10 +45,9 @@ class WorkScheduler { } /** Called from client: - * If work in progress, raise an exception in it next - * time pollException is called. + * Require an exception to be thrown on next poll. */ def raise(exc: Exception) = synchronized { - if (working) except = Some(exc) + except = Some(exc) } } -- cgit v1.2.3