diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/util/SignalManager.scala | 47 | ||||
-rw-r--r-- | src/compiler/scala/tools/util/Signallable.scala | 13 | ||||
-rw-r--r-- | src/library/scala/collection/Iterator.scala | 2 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/DirectRunner.scala | 57 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/Worker.scala | 102 |
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) } } } |