From 2f0f42ebe2d330e8ce9e73bb241a6f261b77eace Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 14 Jan 2014 16:18:28 -0500 Subject: [PATCH] Drop parallel evaluation of settings. The overhead outweighs the gain. --- .../collection/src/main/scala/sbt/INode.scala | 129 ++---------------- .../src/main/scala/sbt/Settings.scala | 14 +- 2 files changed, 18 insertions(+), 125 deletions(-) diff --git a/util/collection/src/main/scala/sbt/INode.scala b/util/collection/src/main/scala/sbt/INode.scala index 4ce5ef8bb..0d17d5d5b 100644 --- a/util/collection/src/main/scala/sbt/INode.scala +++ b/util/collection/src/main/scala/sbt/INode.scala @@ -5,20 +5,12 @@ package sbt import atomic.{AtomicBoolean, AtomicInteger} import Types.{:+:, ConstK, Id} -object EvaluationState extends Enumeration { - val New, Blocked, Ready, Calling, Evaluated = Value -} - abstract class EvaluateSettings[Scope] { protected val init: Init[Scope] import init._ - protected def executor: Executor protected def compiledSettings: Seq[Compiled[_]] - import EvaluationState.{Value => EvaluationState, _} - - private[this] val complete = new LinkedBlockingQueue[Option[Throwable]] private[this] val static = PMap.empty[ScopedKey, INode] private[this] val allScopes: Set[Scope] = compiledSettings.map(_.key.scope).toSet private[this] def getStatic[T](key: ScopedKey[T]): INode[T] = static get key getOrElse sys.error("Illegal reference to key " + key) @@ -42,136 +34,41 @@ abstract class EvaluateSettings[Scope] t }): Seq[INode[_]] } - private[this] var running = new AtomicInteger - private[this] var cancel = new AtomicBoolean(false) def run(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = { - assert(running.get() == 0, "Already running") - startWork() - roots.foreach( _.registerIfNew() ) - workComplete() - complete.take() foreach { ex => - cancel.set(true) - throw ex - } + roots.foreach( _.value ) getResults(delegates) } private[this] def getResults(implicit delegates: Scope => Seq[Scope]) = (empty /: static.toTypedSeq) { case (ss, static.TPair(key, node)) => - if(key.key.isLocal) ss else ss.set(key.scope, key.key, node.get) + if(key.key.isLocal) ss else ss.set(key.scope, key.key, node.value) } - private[this] val getValue = new (INode ~> Id) { def apply[T](node: INode[T]) = node.get } - private[this] def submitEvaluate(node: INode[_]) = submit(node.evaluate()) - private[this] def submitCallComplete[T](node: BindNode[_, T], value: T) = submit(node.callComplete(value)) - private[this] def submit(work: => Unit): Unit = - { - startWork() - executor.execute(new Runnable { def run = if(!cancel.get()) run0(work) }) - } - private[this] def run0(work: => Unit): Unit = - { - try { work } catch { case e: Throwable => complete.put( Some(e) ) } - workComplete() - } - - private[this] def startWork(): Unit = running.incrementAndGet() - private[this] def workComplete(): Unit = - if(running.decrementAndGet() == 0) - complete.put( None ) - private[this] sealed abstract class INode[T] { - private[this] var state: EvaluationState = New - private[this] var value: T = _ - private[this] val blocking = new collection.mutable.ListBuffer[INode[_]] - private[this] var blockedOn: Int = 0 - private[this] val calledBy = new collection.mutable.ListBuffer[BindNode[_, T]] + final lazy val value: T = { + val v = evaluate + if(v == null) sys.error("Setting value cannot be null: " + keyString) + v + } - override def toString = getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " + - keyString + override def toString = getClass.getName + ": " + keyString private[this] def keyString = (static.toSeq.flatMap { case (key, value) => if(value eq this) init.showFullKey(key) :: Nil else Nil }).headOption getOrElse "non-static" - - final def get: T = synchronized { - assert(value != null, toString + " not evaluated") - value - } - final def doneOrBlock(from: INode[_]): Boolean = synchronized { - val ready = state == Evaluated - if(!ready) blocking += from - registerIfNew() - ready - } - final def isDone: Boolean = synchronized { state == Evaluated } - final def isNew: Boolean = synchronized { state == New } - final def isCalling: Boolean = synchronized { state == Calling } - final def registerIfNew(): Unit = synchronized { if(state == New) register() } - private[this] def register() - { - assert(state == New, "Already registered and: " + toString) - val deps = dependsOn - blockedOn = deps.size - deps.count(_.doneOrBlock(this)) - if(blockedOn == 0) - schedule() - else - state = Blocked - } - - final def schedule(): Unit = synchronized { - assert(state == New || state == Blocked, "Invalid state for schedule() call: " + toString) - state = Ready - submitEvaluate(this) - } - final def unblocked(): Unit = synchronized { - assert(state == Blocked, "Invalid state for unblocked() call: " + toString) - blockedOn -= 1 - assert(blockedOn >= 0, "Negative blockedOn: " + blockedOn + " for " + toString) - if(blockedOn == 0) schedule() - } - final def evaluate(): Unit = synchronized { evaluate0() } - protected final def makeCall(source: BindNode[_, T], target: INode[T]) { - assert(state == Ready, "Invalid state for call to makeCall: " + toString) - state = Calling - target.call(source) - } - protected final def setValue(v: T) { - assert(state != Evaluated, "Already evaluated (trying to set value to " + v + "): " + toString) - if(v == null) sys.error("Setting value cannot be null: " + keyString) - value = v - state = Evaluated - blocking foreach { _.unblocked() } - blocking.clear() - calledBy foreach { node => submitCallComplete(node, value) } - calledBy.clear() - } - final def call(by: BindNode[_, T]): Unit = synchronized { - registerIfNew() - state match { - case Evaluated => submitCallComplete(by, value) - case _ => calledBy += by - } - } - protected def dependsOn: Seq[INode[_]] - protected def evaluate0(): Unit - } + protected def evaluate: T + } + private[this] val getValue = new (INode ~> Id) { def apply[T](i: INode[T]): T = i.value } private[this] def constant[T](f: () => T): INode[T] = new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty) private[this] def single[S,T](in: INode[S], f: S => T): INode[T] = new MixedNode[ ({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S]) private[this] final class BindNode[S,T](in: INode[S], f: S => INode[T]) extends INode[T] { - protected def dependsOn = in :: Nil - protected def evaluate0(): Unit = makeCall(this, f(in.get) ) - def callComplete(value: T): Unit = synchronized { - assert(isCalling, "Invalid state for callComplete(" + value + "): " + toString) - setValue(value) - } + protected def evaluate: T = f(in.value).value } private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K]) extends INode[T] { - protected def dependsOn = alist.toList(in) - protected def evaluate0(): Unit = setValue( f( alist.transform(in, getValue) ) ) + protected def evaluate: T = f( alist.transform(in, getValue) ) } } diff --git a/util/collection/src/main/scala/sbt/Settings.scala b/util/collection/src/main/scala/sbt/Settings.scala index 61bdc471d..4cc139c2e 100644 --- a/util/collection/src/main/scala/sbt/Settings.scala +++ b/util/collection/src/main/scala/sbt/Settings.scala @@ -182,15 +182,11 @@ trait Init[Scope] private[this] def applyInits(ordered: Seq[Compiled[_]])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = { - val x = java.util.concurrent.Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors) - try { - val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] { - override val init: Init.this.type = Init.this - def compiledSettings = ordered - def executor = x - } - eval.run - } finally { x.shutdown() } + val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] { + override val init: Init.this.type = Init.this + def compiledSettings = ordered + } + eval.run } def showUndefined(u: Undefined, validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope])(implicit display: Show[ScopedKey[_]]): String =