summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/util/SignalManager.scala47
-rw-r--r--src/compiler/scala/tools/util/Signallable.scala13
-rw-r--r--src/library/scala/collection/Iterator.scala2
-rw-r--r--src/partest/scala/tools/partest/nest/DirectRunner.scala57
-rw-r--r--src/partest/scala/tools/partest/nest/Worker.scala102
5 files changed, 134 insertions, 87 deletions
diff --git a/src/compiler/scala/tools/util/SignalManager.scala b/src/compiler/scala/tools/util/SignalManager.scala
index 26274a2ddd..b4a0ccf384 100644
--- a/src/compiler/scala/tools/util/SignalManager.scala
+++ b/src/compiler/scala/tools/util/SignalManager.scala
@@ -81,6 +81,7 @@ class SignalManager(classLoader: ScalaClassLoader) {
def raise() = rSignal raise signal
def handle(handler: AnyRef) = rSignal.handle(signal, handler)
+ def isError = false
def setTo(body: => Unit) = register(name, false, body)
def +=(body: => Unit) = register(name, true, body)
@@ -158,34 +159,45 @@ class SignalManager(classLoader: ScalaClassLoader) {
def update(name: String, body: => Unit): Unit = apply(name) setTo body
- class SignalError(message: String) extends WSignal(null) {
+ class SignalError(message: String) extends WSignal("") {
+ override def isError = true
override def toString = message
}
def public(name: String, description: String)(body: => Unit): Unit = {
- val wsig = apply(name)
- wsig setTo body
- registerInfoHandler()
- addPublicHandler((wsig, description))
+ try {
+ val wsig = apply(name)
+ if (wsig.isError)
+ return
+
+ wsig setTo body
+ registerInfoHandler()
+ addPublicHandler(wsig, description)
+ }
+ catch {
+ case x: Exception => () // ignore failure
+ }
}
/** Makes sure the info handler is registered if we see activity. */
private def registerInfoHandler() = {
val INFO = apply("INFO")
if (publicHandlers.isEmpty && INFO.isDefault) {
INFO setTo Console.println(info())
- addPublicHandler((INFO, "Dump list of well known signal handler to console."))
+ addPublicHandler(INFO, "Print signal handler registry on console.")
}
}
- private def addPublicHandler(kv: (WSignal, String)) = {
- if (publicHandlers exists (_._1 == kv._1)) ()
- else publicHandlers = (kv :: publicHandlers) sortBy (_._1.number)
+ private def addPublicHandler(wsig: WSignal, description: String) = {
+ if (publicHandlers contains wsig) ()
+ else publicHandlers = publicHandlers.updated(wsig, description)
}
- private var publicHandlers: List[(WSignal, String)] = Nil
+ private var publicHandlers: Map[WSignal, String] = Map()
def info(): String = {
registerInfoHandler()
- "\nOutward facing signal handler registry:\n" + (
- publicHandlers map { case (wsig, descr) => " %2d %5s %s\n".format(wsig.number, wsig.name, descr) } mkString ""
- )
+ val xs = publicHandlers.toList sortBy (_._1.name) map {
+ case (wsig, descr) => " %2d %5s %s".format(wsig.number, wsig.name, descr)
+ }
+
+ xs.mkString("\nSignal handler registry:\n", "\n", "")
}
}
@@ -199,12 +211,11 @@ object SignalManager extends SignalManager {
STOP, TSTP, CONT, CHLD, TTIN, TTOU, IO, XCPU, // 16-23
XFSZ, VTALRM, PROF, WINCH, INFO, USR1, USR2 // 24-31
)
- /** Signals which seem like particularly bad choices
- * when looking for an open one.
+ /** Signals which are either inaccessible or which seem like
+ * particularly bad choices when looking for an open one.
*/
- def reserved = Set(QUIT, TRAP, ABRT, KILL, BUS, SEGV, ALRM, STOP, INT)
- def unreserved = all filterNot reserved
-
+ def reserved = Set(QUIT, TRAP, ABRT, KILL, BUS, SEGV, ALRM, STOP, INT)
+ def unreserved = all filterNot reserved
def defaultSignals() = unreserved filter (_.isDefault)
def ignoredSignals() = unreserved filter (_.isIgnored)
def findOpenSignal() = Random.shuffle(defaultSignals()).head
diff --git a/src/compiler/scala/tools/util/Signallable.scala b/src/compiler/scala/tools/util/Signallable.scala
index 0ad14fe02e..ed4d770f66 100644
--- a/src/compiler/scala/tools/util/Signallable.scala
+++ b/src/compiler/scala/tools/util/Signallable.scala
@@ -11,20 +11,33 @@ package util
abstract class Signallable[T] private (val signal: String, val description: String) {
private var last: Option[T] = None
private def lastString = last filterNot (_ == ()) map (_.toString) getOrElse ""
+
+ /** The most recent result from the signal handler. */
def lastResult: Option[T] = last
/** Method to be executed when the associated signal is received. */
def onSignal(): T
+ // todo:
+ // def unregister(): Boolean
+
override def toString = " SIG(%s) => %s%s".format(
signal, description, if (lastString == "") "" else " (" + lastString + ")"
)
}
object Signallable {
+ /** Same as the other apply, but an open signal is found for you.
+ */
def apply[T](description: String)(body: => T): Signallable[T] =
apply(SignalManager.findOpenSignal().name, description)(body)
+ /** Given a signal name, a description, and a handler body, this
+ * registers a signal handler and returns the Signallable instance.
+ * The signal handler registry is thereafter available by calling
+ * SignalManager.info(), or sending SIGINFO to the manager will
+ * dump it to console.
+ */
def apply[T](signal: String, description: String)(body: => T): Signallable[T] = {
val result = new Signallable[T](signal, description) {
def onSignal(): T = {
diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala
index 40f8bc7f81..b741834cf1 100644
--- a/src/library/scala/collection/Iterator.scala
+++ b/src/library/scala/collection/Iterator.scala
@@ -757,7 +757,7 @@ trait Iterator[+A] extends TraversableOnce[A] {
* Typical uses can be achieved via methods `grouped' and `sliding'.
*/
class GroupedIterator[B >: A](self: Iterator[A], size: Int, step: Int) extends Iterator[Seq[B]] {
- require(size >= 1 && step >= 1)
+ require(size >= 1 && step >= 1, "size=%d and step=%d, but both must be positive".format(size, step))
private[this] var buffer: ArrayBuffer[B] = ArrayBuffer() // the buffer
private[this] var filled = false // whether the buffer is "hot"
diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala
index cca7804b9b..d15f851ffc 100644
--- a/src/partest/scala/tools/partest/nest/DirectRunner.scala
+++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala
@@ -8,13 +8,13 @@
package scala.tools.partest
package nest
-import java.io.{File, PrintStream, FileOutputStream, BufferedReader,
- InputStreamReader, StringWriter, PrintWriter}
+import java.io.{ File }
import java.util.StringTokenizer
import scala.util.Properties.{ setProp }
+import scala.tools.util.Signallable
import scala.tools.nsc.util.ScalaClassLoader
import scala.tools.nsc.io.Directory
-
+import scala.collection.{ mutable, immutable }
import scala.actors.Actor._
import scala.actors.TIMEOUT
@@ -36,12 +36,28 @@ trait DirectRunner {
}
}
- def runTestsForFiles(_kindFiles: List[File], kind: String): scala.collection.immutable.Map[String, Int] = {
+ /** These things, formerly inside runTestsForFiles, have been promoted
+ * into private fields so I can inspect them via signal when partest shows
+ * signs of dementia.
+ */
+ private var workers: List[Worker] = Nil
+ private var logsToDelete: List[LogFile] = Nil
+ private var outdirsToDelete: List[File] = Nil
+ private val results = new mutable.HashMap[String, Int]()
+ private def addResults(kvs: Traversable[(String, Int)]) = synchronized { results ++= kvs }
+ private val signallable = Signallable("HUP", "Make partest dump its state.")(dumpState())
+
+ def dumpState() {
+ println("Dumping partest internals.")
+ println("results.size = " + results.size + ", " + workers.size + " workers.")
+ workers foreach println
+ }
+
+ def runTestsForFiles(_kindFiles: List[File], kind: String): immutable.Map[String, Int] = {
/** NO DUPLICATES, or partest will blow the count and hang forever. **/
val kindFiles = _kindFiles.distinct
- val len = kindFiles.length
- val (testsEach, lastFrag) = (len/numActors, len%numActors)
- val last = numActors-1
+ val groupSize = (kindFiles.length / numActors) + 1
+
val consFM = new ConsoleFileManager
import consFM.{ latestCompFile, latestLibFile, latestPartestFile }
val scalacheckURL = PathSettings.scalaCheck.toURL
@@ -49,44 +65,37 @@ trait DirectRunner {
List(scalacheckURL, latestCompFile.toURI.toURL, latestLibFile.toURI.toURL, latestPartestFile.toURI.toURL)
)
Output.init
- val workers = for (i <- List.range(0, numActors)) yield {
- val toTest = kindFiles.slice(i*testsEach, (i+1)*testsEach)
+
+ this.workers = kindFiles.grouped(groupSize).toList map { toTest =>
val worker = new Worker(fileManager, TestRunParams(scalaCheckParentClassLoader))
worker.start()
- if (i == last)
- worker ! RunTests(kind, (kindFiles splitAt (last*testsEach))._2)
- else
- worker ! RunTests(kind, toTest)
+ worker ! RunTests(kind, toTest)
worker
}
- var logsToDelete: List[File] = List()
- var outdirsToDelete: List[File] = List()
- var results = new scala.collection.immutable.HashMap[String, Int]
workers foreach { w =>
receiveWithin(3600 * 1000) {
case Results(res, logs, outdirs) =>
- logsToDelete :::= logs filter (_.toDelete)
- outdirsToDelete :::= outdirs
- results ++= res
+ logsToDelete ++= (logs filter (_.toDelete))
+ outdirsToDelete ++= outdirs
+ addResults(res)
case TIMEOUT =>
// add at least one failure
NestUI.verbose("worker timed out; adding failed test")
- results += ("worker timed out; adding failed test" -> 2)
+ addResults(Seq(("worker timed out; adding failed test" -> 2)))
}
}
if (isPartestDebug)
fileManager.showTestTimings()
-
- if (!isPartestDebug) {
- for (x <- logsToDelete ::: outdirsToDelete) {
+ else {
+ for (x <- logsToDelete ++ outdirsToDelete) {
NestUI.verbose("deleting "+x)
Directory(x).deleteRecursively()
}
}
- results
+ results.toMap
}
}
diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala
index 8d4a8f1a5b..293de5c427 100644
--- a/src/partest/scala/tools/partest/nest/Worker.scala
+++ b/src/partest/scala/tools/partest/nest/Worker.scala
@@ -23,6 +23,7 @@ import scala.actors.{ Actor, Exit, TIMEOUT }
import scala.actors.Actor._
import scala.tools.scalap.scalax.rules.scalasig.{ByteCode, ClassFileParser, ScalaSigAttributeParsers}
+import scala.collection.{ mutable, immutable }
import scala.collection.immutable.{ HashMap, Map => ImmMap }
import scala.collection.Map
@@ -115,6 +116,30 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
else
fileManager.JAVAC_CMD
+ /** Formerly deeper inside, these next few things are now promoted outside so
+ * I can see what they're doing when the world comes to a premature stop.
+ */
+ private val filesRemaining = new mutable.HashSet[File]
+ private def addFilesRemaining(xs: Traversable[File]) = filesRemaining ++= xs
+ private def getNextFile() = synchronized {
+ val file = filesRemaining.head
+ filesRemaining -= file
+ file
+ }
+ // maps canonical file names to the test result (0: OK, 1: FAILED, 2: TIMOUT)
+ private val status = new mutable.HashMap[String, Int]
+ private def updateStatus(key: String, num: Int) = synchronized {
+ status(key) = num
+ }
+ override def toString = (
+ ">> Partest Worker:\n" +
+ "TestRunParams = " + params + "\n" +
+ filesRemaining.size + " files remaining:\n" +
+ filesRemaining.toList.sortBy(_.toString).map(" " + _ + "\n").mkString("") +
+ "\nstatus hashmap contains " + status.size + " entries:\n" +
+ status.toList.map(x => " " + x._1 + " -> " + x._2).sorted.mkString("\n") + "\n"
+ )
+
def workerError(msg: String): Unit = reporter.error(
FakePos("scalac"),
msg + "\n scalac -help gives more information"
@@ -1023,8 +1048,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
try {
// delete log file only if test was successful
- if (good && !logFile.isEmpty && !isPartestDebug)
- logFile.get.toDelete = true
+ logFile filter (_ => good && !isPartestDebug) foreach (_.toDelete = true)
writers match {
case Some((swr, wr)) =>
@@ -1046,64 +1070,54 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
}
}
- val numFiles = files.size
- if (numFiles == 0)
- reportAll(ImmMap(), topcont)
-
- // maps canonical file names to the test result (0: OK, 1: FAILED, 2: TIMOUT)
- var status = new HashMap[String, Int]
+ if (files.isEmpty) reportAll(ImmMap(), topcont)
+ else addFilesRemaining(files)
- var fileCnt = 1
- Actor.loopWhile(fileCnt <= numFiles) {
+ Actor.loopWhile(filesRemaining.nonEmpty) {
val parent = self
actor {
- val testFile = files(fileCnt-1)
-
+ val testFile = getNextFile()
val ontimeout = new TimerTask {
def run() = parent ! Timeout(testFile)
}
timer.schedule(ontimeout, fileManager.timeout.toLong)
- val context = try {
- processSingleFile(testFile)
- } catch {
- case t: Throwable =>
- NestUI.verbose("while invoking compiler ("+files+"):")
- NestUI.verbose("caught "+t)
- t.printStackTrace
- if (t.getCause != null)
- t.getCause.printStackTrace
- LogContext(null, None)
- }
+ val context =
+ try processSingleFile(testFile)
+ catch {
+ case t: Throwable =>
+ NestUI.verbose("while invoking compiler ("+files+"):")
+ NestUI.verbose("caught "+t)
+ t.printStackTrace
+ if (t.getCause != null)
+ t.getCause.printStackTrace
+ LogContext(null, None)
+ }
parent ! Result(testFile, context)
}
react {
case res: TestResult =>
val path = res.file.getCanonicalPath
- status.get(path) match {
- case Some(stat) => // ignore message
- case None =>
- res match {
- case Timeout(_) =>
- status = status + (path -> 2)
- val swr = new StringWriter
- val wr = new PrintWriter(swr)
- printInfoStart(res.file, wr)
- succeeded = false
- reportResult(2, None, Some((swr, wr)))
- case Result(_, logs) =>
- status = status + (path -> (if (succeeded) 0 else 1))
- reportResult(
- if (succeeded) 0 else 1,
- if (logs != null) Some(logs.file) else None,
- if (logs != null) logs.writers else None)
- }
- if (fileCnt == numFiles)
- reportAll(status, topcont)
- fileCnt += 1
+ if (status contains path) () // ignore message
+ else res match {
+ case Timeout(_) =>
+ updateStatus(path, 2)
+ val swr = new StringWriter
+ val wr = new PrintWriter(swr)
+ printInfoStart(res.file, wr)
+ succeeded = false
+ reportResult(2, None, Some((swr, wr)))
+ case Result(_, logs) =>
+ updateStatus(path, (if (succeeded) 0 else 1))
+ reportResult(
+ if (succeeded) 0 else 1,
+ if (logs != null) Some(logs.file) else None,
+ if (logs != null) logs.writers else None)
}
+ if (filesRemaining.isEmpty)
+ reportAll(status.toMap, topcont)
}
}
}