From 907e84b0272eaca1197dc7e7b1f1a6a92e4e2414 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 19 Jun 2013 19:20:37 -0400 Subject: [PATCH] Synchronize at a higher level on the Scala provider cache. Construction of Scala providers was already properly synchronized jvm and machine-wide. The cache on top of construction was not. This could cause the same Scala version to be loaded in multiple class loaders. --- launch/Cache.scala | 2 +- .../project/scala-loader/project/Build.scala | 19 +++++++++++++++++++ sbt/src/sbt-test/project/scala-loader/test | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 sbt/src/sbt-test/project/scala-loader/project/Build.scala create mode 100644 sbt/src/sbt-test/project/scala-loader/test diff --git a/launch/Cache.scala b/launch/Cache.scala index 488e5388e..669ea3819 100644 --- a/launch/Cache.scala +++ b/launch/Cache.scala @@ -9,7 +9,7 @@ import java.util.HashMap final class Cache[K,X,V](create: (K,X) => V) { private[this] val delegate = new HashMap[K,Reference[V]] - def apply(k: K, x: X): V = getFromReference(k, x, delegate.get(k)) + def apply(k: K, x: X): V = synchronized { getFromReference(k, x, delegate.get(k)) } private[this] def getFromReference(k: K, x: X, existingRef: Reference[V]) = if(existingRef eq null) newEntry(k, x) else get(k, x, existingRef.get) private[this] def get(k: K, x: X, existing: V) = if(existing == null) newEntry(k, x) else existing private[this] def newEntry(k: K, x: X): V = diff --git a/sbt/src/sbt-test/project/scala-loader/project/Build.scala b/sbt/src/sbt-test/project/scala-loader/project/Build.scala new file mode 100644 index 000000000..e3a0d22fb --- /dev/null +++ b/sbt/src/sbt-test/project/scala-loader/project/Build.scala @@ -0,0 +1,19 @@ +import sbt._ +import Keys._ + +object Build extends Build { + + lazy val checkLoader = TaskKey[Unit]("check-loaders") + + def checkTask = subs.map(sub => scalaInstance in LocalProject(sub.id)).join.map { sis => + assert(sis.sliding(2).forall{ case Seq(x,y) => x.loader == y.loader }, "Not all ScalaInstances had the same class loader.") + } + + override def projects = root +: subs + lazy val root = Project("root", file(".")).settings( checkLoader <<= checkTask ) + lazy val subs = ( for(i <- 1 to 20) yield newProject(i) ).toSeq + + def newProject(i: Int): Project = Project("x" + i.toString, file(i.toString)).settings( + scalaVersion := "2.10.2" + ) +} diff --git a/sbt/src/sbt-test/project/scala-loader/test b/sbt/src/sbt-test/project/scala-loader/test new file mode 100644 index 000000000..9967e5638 --- /dev/null +++ b/sbt/src/sbt-test/project/scala-loader/test @@ -0,0 +1,2 @@ +# verify that the class loader for Scala used for each project is the same +> check-loaders