From 03aa7fc3904033f8d2f6f14a87574a03553b7c72 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 29 Jun 2011 10:27:23 +0000 Subject: SI-6616 Check that unsafe operations are only called on the presentation compiler thread. The method that checks the actual constraint is @elidable, expecting it to be used for nightly builds but stripped-off in release builds. This way we don't lose any performance, but 'fail-fast' in IDE nightlies. This assumes that release builds will have at least `-Xelide-below ASSERTION`, but this pull request does not do that. --- .../scala/tools/nsc/interactive/Global.scala | 41 +++++++++++++++++----- .../scala/reflect/internal/SymbolTable.scala | 6 ++++ src/reflect/scala/reflect/internal/Symbols.scala | 2 ++ src/reflect/scala/reflect/internal/Types.scala | 1 + 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 01889f4f98..6f7695ba0e 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -20,18 +20,23 @@ import scala.tools.nsc.io.Pickler._ import scala.tools.nsc.typechecker.DivergentImplicit import scala.annotation.tailrec import symtab.Flags.{ACCESSOR, PARAMACCESSOR} +import scala.annotation.elidable import scala.language.implicitConversions /** The main class of the presentation compiler in an interactive environment such as an IDE */ -class Global(settings: Settings, _reporter: Reporter, projectName: String = "") - extends scala.tools.nsc.Global(settings, _reporter) - with CompilerControl - with RangePositions - with ContextTrees - with RichCompilationUnits - with ScratchPadMaker - with Picklers { +class Global(settings: Settings, _reporter: Reporter, projectName: String = "") extends { + /* Is the compiler initializing? Early def, so that the field is true during the + * execution of the super constructor. + */ + private var initializing = true +} with scala.tools.nsc.Global(settings, _reporter) + with CompilerControl + with RangePositions + with ContextTrees + with RichCompilationUnits + with ScratchPadMaker + with Picklers { import definitions._ @@ -51,6 +56,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") import log.logreplay debugLog("logger: " + log.getClass + " writing to " + (new java.io.File(logName)).getAbsolutePath) debugLog("classpath: "+classPath) + Console.err.println("\n ======= CHECK THREAD ACCESS compiler build ========\n") private var curTime = System.nanoTime private def timeStep = { @@ -433,7 +439,18 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") private var threadId = 0 /** The current presentation compiler runner */ - @volatile private[interactive] var compileRunner = newRunnerThread() + @volatile private[interactive] var compileRunner: Thread = newRunnerThread() + + /** Check that the currenyly executing thread is the presentation compiler thread. + * + * Compiler initialization may happen on a different thread (signalled by globalPhase being NoPhase) + */ + @elidable(elidable.WARNING) + override def assertCorrectThread() { + assert(initializing || (Thread.currentThread() eq compileRunner), + "Race condition detected: You are running a presentation compiler method outside the PC thread.[phase: %s]".format(globalPhase) + + " Please file a ticket with the current stack trace at https://www.assembla.com/spaces/scala-ide/support/tickets") + } /** Create a new presentation compiler runner. */ @@ -1110,6 +1127,12 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") alt } } + + /** The compiler has been initialized. Constructors are evaluated in textual order, + * so this is set to true only after all super constructors and the primary constructor + * have been executed. + */ + initializing = false } object CancelException extends Exception diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 2424e75949..881a5ee5d6 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -6,6 +6,7 @@ package scala.reflect package internal +import scala.annotation.elidable import scala.collection.{ mutable, immutable } import util._ @@ -107,6 +108,11 @@ abstract class SymbolTable extends macros.Universe val global: SymbolTable.this.type = SymbolTable.this } with util.TraceSymbolActivity + /** Check that the executing thread is the compiler thread. No-op here, + * overridden in interactive.Global. */ + @elidable(elidable.WARNING) + def assertCorrectThread() {} + /** Are we compiling for Java SE? */ // def forJVM: Boolean diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a6f156f947..449102dc00 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1213,6 +1213,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } val current = phase try { + assertCorrectThread() phase = phaseOf(infos.validFrom) tp.complete(this) } finally { @@ -1283,6 +1284,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => infos = infos.prev if (validTo < curPeriod) { + assertCorrectThread() // adapt any infos that come from previous runs val current = phase try { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 403bf7d492..cdfff12855 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -146,6 +146,7 @@ trait Types extends api.Types { self: SymbolTable => /** Undo all changes to constraints to type variables upto `limit`. */ //OPT this method is public so we can do `manual inlining` def undoTo(limit: UndoPairs) { + assertCorrectThread() while ((log ne limit) && log.nonEmpty) { val (tv, constr) = log.head tv.constr = constr -- cgit v1.2.3