From 92fc01f1851ccce23fbbc4e406f2b86ebb755d4e Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sat, 24 Oct 2009 13:07:42 -0400 Subject: [PATCH] Use lock file to control access to boot directory during update --- launch/Launch.scala | 6 +++++- launch/Locks.scala | 2 +- launch/Provider.scala | 41 ++++++++++++++++++++++------------------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/launch/Launch.scala b/launch/Launch.scala index 62ca5ea89..c0e10b3a7 100644 --- a/launch/Launch.scala +++ b/launch/Launch.scala @@ -72,10 +72,12 @@ final class RunConfiguration(val scalaVersion: String, val app: xsbti.Applicatio import BootConfiguration.{appDirectoryName, baseDirectoryName, ScalaDirectoryName, TestLoadScalaClasses} class Launch(val bootDirectory: File, repositories: List[Repository]) extends xsbti.Launcher { + bootDirectory.mkdirs private val scalaProviders = new Cache[String, ScalaProvider](new ScalaProvider(_)) def getScala(version: String): xsbti.ScalaProvider = scalaProviders(version) lazy val topLoader = new BootFilteredLoader(getClass.getClassLoader) + val updateLockFile = new File(bootDirectory, "sbt.boot.lock") def globalLock: xsbti.GlobalLock = Locks @@ -93,6 +95,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository]) extends xs def testLoadClasses = TestLoadScalaClasses def target = UpdateScala def failLabel = "Scala " + version + def lockFile = updateLockFile def app(id: xsbti.ApplicationID): xsbti.AppProvider = new AppProvider(id) @@ -106,6 +109,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository]) extends xs def testLoadClasses = List(id.mainClass) def target = new UpdateApp(Application(id)) def failLabel = id.name + " " + id.version + def lockFile = updateLockFile lazy val mainClass: Class[T] forSome { type T <: xsbti.AppMain } = { @@ -121,7 +125,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository]) extends xs class ComponentProvider(baseDirectory: File) extends xsbti.ComponentProvider { def componentLocation(id: String): File = new File(baseDirectory, id) - def component(id: String) = GetJars.wrapNull(componentLocation(id).listFiles).filter(_.isFile) + def component(id: String) = Provider.wrapNull(componentLocation(id).listFiles).filter(_.isFile) def defineComponent(id: String, files: Array[File]) = { val location = componentLocation(id) diff --git a/launch/Locks.scala b/launch/Locks.scala index 10476121c..431860ca4 100644 --- a/launch/Locks.scala +++ b/launch/Locks.scala @@ -8,7 +8,7 @@ import java.util.concurrent.Callable object Locks extends xsbti.GlobalLock { private[this] val locks = new Cache[File, GlobalLock](new GlobalLock(_)) - def apply[T](file: File, action: Callable[T]) = + def apply[T](file: File, action: Callable[T]): T = synchronized { locks(file.getCanonicalFile).withLock(action) } private[this] class GlobalLock(file: File) diff --git a/launch/Provider.scala b/launch/Provider.scala index 00b1c9bf2..69d93f751 100644 --- a/launch/Provider.scala +++ b/launch/Provider.scala @@ -3,6 +3,7 @@ package xsbt.boot import Pre._ import java.io.{File, FileFilter} import java.net.{URL, URLClassLoader} +import java.util.concurrent.Callable trait Provider extends NotNull { @@ -12,39 +13,41 @@ trait Provider extends NotNull def target: UpdateTarget def failLabel: String def parentLoader: ClassLoader + def lockFile: File - val (jars, loader) = + val (jars, loader) = Locks(lockFile, new initialize) + private final class initialize extends Callable[(Array[File], ClassLoader)] { - val (existingJars, existingLoader) = createLoader - if(Check.needsUpdate(existingLoader, testLoadClasses)) + def call = { - ( new Update(configuration) )(target) - val (newJars, newLoader) = createLoader - Check.failIfMissing(newLoader, testLoadClasses, failLabel) - (newJars, newLoader) + val (existingJars, existingLoader) = createLoader + if(Provider.needsUpdate(existingLoader, testLoadClasses)) + { + ( new Update(configuration) )(target) + val (newJars, newLoader) = createLoader + Provider.failIfMissing(newLoader, testLoadClasses, failLabel) + (newJars, newLoader) + } + else + (existingJars, existingLoader) + } + def createLoader = + { + val jars = Provider.getJars(baseDirectories) + (jars, new URLClassLoader(jars.map(_.toURI.toURL), parentLoader) ) } - else - (existingJars, existingLoader) - } - def createLoader = - { - val jars = GetJars(baseDirectories) - (jars, new URLClassLoader(jars.map(_.toURI.toURL), parentLoader) ) } } -object GetJars +object Provider { - def apply(directories: List[File]): Array[File] = toArray(directories.flatMap(directory => wrapNull(directory.listFiles(JarFilter)))) + def getJars(directories: List[File]): Array[File] = toArray(directories.flatMap(directory => wrapNull(directory.listFiles(JarFilter)))) def wrapNull(a: Array[File]): Array[File] = if(a == null) Array() else a object JarFilter extends FileFilter { def accept(file: File) = !file.isDirectory && file.getName.endsWith(".jar") } -} -object Check -{ def failIfMissing(loader: ClassLoader, classes: Iterable[String], label: String) = checkTarget(loader, classes, (), missing => error("Could not retrieve " + label + ": missing " + missing.mkString(", "))) def needsUpdate(loader: ClassLoader, classes: Iterable[String]) = checkTarget(loader, classes, false, x => true)