summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-11-26 19:16:05 +0000
committerPaul Phillips <paulp@improving.org>2010-11-26 19:16:05 +0000
commit1fcb86507062f9923465cdf72e6b70c3a1cfe96b (patch)
tree0b9a713193a18a9dc47c054058ccba36e5fe218f
parent79ef52f9e374a6ef599cefebb878692b380ea755 (diff)
downloadscala-1fcb86507062f9923465cdf72e6b70c3a1cfe96b.tar.gz
scala-1fcb86507062f9923465cdf72e6b70c3a1cfe96b.tar.bz2
scala-1fcb86507062f9923465cdf72e6b70c3a1cfe96b.zip
A new totally safe signal manager, along with a...
A new totally safe signal manager, along with all the support code needed for that. See the classes in scala.tools.reflect.* for interesting new weapons. Also includes inaugural handy usage: scala> val x = 10 x: Int = 10 scala> while (true) () [ctrl-C] Execution interrupted by signal. scala> x res1: Int = 10 No review, but feedback welcome.
-rw-r--r--src/compiler/scala/tools/cmd/FromString.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala32
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/JLineReader.scala9
-rw-r--r--src/compiler/scala/tools/nsc/util/Exceptional.scala4
-rw-r--r--src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala10
-rw-r--r--src/compiler/scala/tools/reflect/Invoked.scala52
-rw-r--r--src/compiler/scala/tools/reflect/Mock.scala59
-rw-r--r--src/compiler/scala/tools/reflect/Shield.scala44
-rw-r--r--src/compiler/scala/tools/reflect/package.scala30
-rw-r--r--src/compiler/scala/tools/util/SignalManager.scala223
-rw-r--r--test/pending/run/signals.scala22
11 files changed, 407 insertions, 80 deletions
diff --git a/src/compiler/scala/tools/cmd/FromString.scala b/src/compiler/scala/tools/cmd/FromString.scala
index 81454e7a30..fd6c1331b0 100644
--- a/src/compiler/scala/tools/cmd/FromString.scala
+++ b/src/compiler/scala/tools/cmd/FromString.scala
@@ -7,7 +7,7 @@ package scala.tools
package cmd
import nsc.io.{ Path, File, Directory }
-import reflect.OptManifest
+import scala.reflect.OptManifest
/** A general mechanism for defining how a command line argument
* (always a String) is transformed into an arbitrary type. A few
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 3ef1a995b4..9c76fc2ff2 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -11,18 +11,19 @@ import File.pathSeparator
import java.lang.{ Class, ClassLoader }
import java.net.{ MalformedURLException, URL }
import java.lang.reflect
+import java.util.concurrent.Future
import reflect.InvocationTargetException
import scala.collection.{ mutable, immutable }
import scala.PartialFunction.{ cond, condOpt }
-import scala.tools.util.PathResolver
+import scala.tools.util.{ PathResolver, SignalManager }
import scala.reflect.Manifest
import scala.collection.mutable.{ ListBuffer, HashSet, HashMap, ArrayBuffer }
import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional }
import ScalaClassLoader.URLClassLoader
import scala.util.control.Exception.{ Catcher, catching, catchingPromiscuously, ultimately, unwrapping }
-import io.{ PlainFile, VirtualDirectory }
+import io.{ PlainFile, VirtualDirectory, spawn }
import reporters.{ ConsoleReporter, Reporter }
import symtab.{ Flags, Names }
import util.{ ScalaPrefs, JavaStackFrame, SourceFile, BatchSourceFile, ScriptSourceFile, ClassPath, Chars, stringFromWriter }
@@ -183,6 +184,17 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
}
+ private var currentExecution: Future[_] = null
+ private def sigintHandler = {
+ if (currentExecution == null) System.exit(1)
+ else currentExecution.cancel(true)
+ }
+ /** Try to install sigint handler: ignore failure. */
+ locally {
+ try { SignalManager("INT") = sigintHandler }
+ catch { case _: Exception => () }
+ }
+
/** interpreter settings */
lazy val isettings = new InterpreterSettings(this)
@@ -999,9 +1011,19 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
}
- catchingPromiscuously(onErr) {
- unwrapping(wrapperExceptions: _*) {
- (resultValMethod.invoke(loadedResultObject).toString, true)
+ /** Todo: clean this up. */
+ ultimately(currentExecution = null) {
+ catchingPromiscuously(onErr) {
+ unwrapping(wrapperExceptions: _*) {
+ /** Running it in a separate thread so it's easy to interrupt on ctrl-C. */
+ val future = spawn(resultValMethod.invoke(loadedResultObject).toString)
+ currentExecution = future
+ while (!future.isDone)
+ Thread.`yield`
+
+ if (future.isCancelled) ("Execution interrupted by signal.\n", false)
+ else (future.get(), true)
+ }
}
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
index c7289ad7e8..032da9c395 100644
--- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
@@ -8,7 +8,6 @@ package interpreter
import java.io.File
import jline.{ ConsoleReader, ArgumentCompletor, History => JHistory }
-// import scala.tools.util.SignalManager
/** Reads from the console using JLine */
class JLineReader(interpreter: Interpreter) extends InteractiveReader {
@@ -18,14 +17,6 @@ class JLineReader(interpreter: Interpreter) extends InteractiveReader {
override lazy val completion = Option(interpreter) map (x => new Completion(x))
override def init() = consoleReader.getTerminal().initializeTerminal()
- /** Requires two interrupt signals within three seconds
- * of one another to initiate exit.
- */
- // SignalManager.requireInterval(3, SignalManager.INT) {
- // case true => Console.println("\nPress ctrl-C again to exit.")
- // case false => System.exit(1)
- // }
-
val consoleReader = {
val r = new jline.ConsoleReader()
r setHistory (History().jhistory)
diff --git a/src/compiler/scala/tools/nsc/util/Exceptional.scala b/src/compiler/scala/tools/nsc/util/Exceptional.scala
index c5fc92443d..d0c04ae4d9 100644
--- a/src/compiler/scala/tools/nsc/util/Exceptional.scala
+++ b/src/compiler/scala/tools/nsc/util/Exceptional.scala
@@ -1,7 +1,7 @@
package scala.tools.nsc
package util
-import java.lang.reflect.InvocationTargetException
+import java.lang.reflect.{ InvocationTargetException, UndeclaredThrowableException }
import io.{ Sources, Fileish }
import scala.tools.util.StringOps._
@@ -117,7 +117,7 @@ object Exceptional {
case ex => x :: causes(ex)
}
def unwrap(x: Throwable): Throwable = x match {
- case _: InvocationTargetException | _: ExceptionInInitializerError if x.getCause != null => unwrap(x.getCause)
+ case _: InvocationTargetException | _: ExceptionInInitializerError | _: UndeclaredThrowableException if x.getCause != null => unwrap(x.getCause)
case _ => x
}
}
diff --git a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
index a739caa8a4..4604e63d59 100644
--- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
+++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
@@ -56,6 +56,9 @@ trait ScalaClassLoader extends JavaClassLoader {
result
}
+ def constructorsOf[T <: AnyRef : Manifest]: List[Constructor[T]] =
+ manifest[T].erasure.getConstructors.toList map (_.asInstanceOf[Constructor[T]])
+
/** The actual bytes for a class file, or an empty array if it can't be found. */
def findBytesForClassName(s: String): Array[Byte] = {
val name = s.replaceAll("""\.""", "/") + ".class"
@@ -80,6 +83,11 @@ trait ScalaClassLoader extends JavaClassLoader {
}
object ScalaClassLoader {
+ implicit def apply(cl: JavaClassLoader): ScalaClassLoader = {
+ val loader = if (cl == null) JavaClassLoader.getSystemClassLoader() else cl
+ new JavaClassLoader(loader) with ScalaClassLoader
+ }
+
class URLClassLoader(urls: Seq[URL], parent: JavaClassLoader)
extends java.net.URLClassLoader(urls.toArray, parent)
with ScalaClassLoader {
@@ -97,7 +105,7 @@ object ScalaClassLoader {
def setContextLoader(cl: JavaClassLoader) = Thread.currentThread.setContextClassLoader(cl)
def getContextLoader() = Thread.currentThread.getContextClassLoader()
- def getSystemLoader(): ScalaClassLoader = new JavaClassLoader(JavaClassLoader.getSystemClassLoader()) with ScalaClassLoader
+ def getSystemLoader(): ScalaClassLoader = ScalaClassLoader(null)
def defaultParentClassLoader() = findExtClassLoader()
def fromURLs(urls: Seq[URL], parent: ClassLoader = defaultParentClassLoader()): URLClassLoader =
diff --git a/src/compiler/scala/tools/reflect/Invoked.scala b/src/compiler/scala/tools/reflect/Invoked.scala
new file mode 100644
index 0000000000..935d493130
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/Invoked.scala
@@ -0,0 +1,52 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package reflect
+
+import java.lang.reflect.{ Method, Proxy }
+
+/** A class representing a single method call. It is primarily for use
+ * in tandem with Mock. If the invocation did not target an InvocationHandler,
+ * proxy will be null.
+ */
+class Invoked private (val proxy: AnyRef, val m: Method, val args: List[AnyRef]) {
+ def name = m.getName
+ def arity = m.getParameterTypes.size
+ def returnType = m.getReturnType
+ def returns[T: Manifest] = returnType == manifest[T].erasure
+
+ def invokeOn(target: AnyRef) = m.invoke(target, args: _*)
+ def isObjectMethod = Set("toString", "equals", "hashCode") contains name
+
+ override def toString = "Invoked: %s called with %s".format(
+ m.getName,
+ if (args.isEmpty) "no args" else "args '%s'".format(args mkString ", ")
+ )
+}
+
+object Invoked {
+ def apply(m: Method, args: Seq[Any]): Invoked = apply(null, m, args)
+ def apply(proxy: AnyRef, m: Method, args: Seq[Any]): Invoked = {
+ val fixedArgs = if (args == null) Nil else args.toList map (_.asInstanceOf[AnyRef])
+ new Invoked(proxy, m, fixedArgs)
+ }
+ def unapply(x: Any) = x match {
+ case x: Invoked => Some(x.proxy, x.m, x.args)
+ case _ => None
+ }
+ object NameAndArgs {
+ def unapply(x: Any) = x match {
+ case x: Invoked => Some(x.name, x.args)
+ case _ => None
+ }
+ }
+ object NameAndArity {
+ def unapply(x: Any) = x match {
+ case x: Invoked => Some(x.name, x.arity)
+ case _ => None
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/reflect/Mock.scala b/src/compiler/scala/tools/reflect/Mock.scala
new file mode 100644
index 0000000000..1c5c880113
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/Mock.scala
@@ -0,0 +1,59 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package reflect
+
+import java.lang.reflect.{ Method, Proxy, InvocationHandler }
+
+/** A wrapper around java dynamic proxies to make it easy to pose
+ * as an interface. See SignalManager for an example usage.
+ */
+trait Mock extends (Invoked => AnyRef) {
+ mock =>
+
+ def interfaces: List[Class[_]]
+ def classLoader: ClassLoader
+ def apply(invoked: Invoked): AnyRef
+
+ def newProxyInstance(handler: InvocationHandler): AnyRef =
+ Proxy.newProxyInstance(classLoader, interfaces.toArray, handler)
+ def newProxyInstance(): AnyRef =
+ newProxyInstance(newInvocationHandler())
+
+ def newInvocationHandler() = new InvocationHandler {
+ def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) =
+ mock(Invoked(proxy, method, args))
+ }
+}
+
+/** The methods in Mock create the actual proxy instance which can be used
+ * in place of the associated interface(s).
+ */
+object Mock {
+ /** The default implementation calls the partial function if defined, and
+ * routes Object methods to the proxy: otherwise it throws an exception.
+ */
+ def fromInterfaces(clazz: Class[_], clazzes: Class[_]*)(pf: PartialFunction[Invoked, AnyRef]): AnyRef = {
+ val ints = clazz :: clazzes.toList
+ require(ints forall (_.isInterface), "All class objects must represent interfaces")
+
+ val mock = new Mock {
+ val interfaces = ints
+ def classLoader = clazz.getClassLoader
+ def apply(invoked: Invoked) =
+ if (pf.isDefinedAt(invoked)) pf(invoked)
+ else if (invoked.isObjectMethod) invoked invokeOn this
+ else throw new NoSuchMethodException("" + invoked)
+ }
+ mock.newProxyInstance()
+ }
+ /** Tries to implement all the class's interfaces.
+ */
+ def fromClass(clazz: Class[_])(pf: PartialFunction[Invoked, AnyRef]): AnyRef = allInterfaces(clazz) match {
+ case Nil => Predef.error(clazz + " implements no interfaces.")
+ case x :: xs => fromInterfaces(x, xs: _*)(pf)
+ }
+}
diff --git a/src/compiler/scala/tools/reflect/Shield.scala b/src/compiler/scala/tools/reflect/Shield.scala
new file mode 100644
index 0000000000..5abd97d517
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/Shield.scala
@@ -0,0 +1,44 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package reflect
+
+import java.lang.reflect.Constructor
+import nsc.util.ScalaClassLoader
+
+/** A support class for simplifying the otherwise disbelief-inspiring
+ * process of working with classes completely reflectively. This is
+ * the case with e.g. sun.misc.Signal* due to environments which are
+ * antagonistic to their use. See SignalManager for an example.
+ *
+ * The name "Shield" is a reference to shielding the JVM from knowledge
+ * of what we're doing.
+ */
+trait Shield {
+ def className: String
+ def classLoader: ScalaClassLoader
+
+ // Override this if you are more ambitious about logging or throwing.
+ def onError[T >: Null](msg: String): T = null
+
+ /** This is handy because all reflective calls want back an AnyRef but
+ * we will often be generating Units.
+ */
+ protected implicit def boxedUnit(x: Unit): AnyRef = scala.runtime.BoxedUnit.UNIT
+
+ lazy val clazz: Class[_] = classLoader.tryToLoadClass(className) getOrElse onError("Failed to load " + className)
+ lazy val methods = clazz.getMethods.toList
+
+ def constructor(paramTypes: Class[_]*) = clazz.getConstructor(paramTypes: _*).asInstanceOf[Constructor[AnyRef]]
+ def method(name: String, arity: Int) = uniqueMethod(name, arity)
+ def field(name: String) = clazz getField name
+
+ def matchingMethods(name: String, arity: Int) = methods filter (m => nameAndArity(m) == (name, arity))
+ def uniqueMethod(name: String, arity: Int) = matchingMethods(name, arity) match {
+ case List(x) => x
+ case _ => onError("No unique match for " + name)
+ }
+}
diff --git a/src/compiler/scala/tools/reflect/package.scala b/src/compiler/scala/tools/reflect/package.scala
new file mode 100644
index 0000000000..cf3d6f9ab2
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/package.scala
@@ -0,0 +1,30 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+
+import java.lang.reflect.Method
+import java.{ lang => jl }
+
+package object reflect {
+ def nameAndArity(m: Method) = (m.getName, m.getParameterTypes.size)
+ def allInterfaces(cl: Class[_]): List[Class[_]] =
+ if (cl == null) Nil
+ else cl.getInterfaces.toList ++ allInterfaces(cl.getSuperclass) distinct
+
+ def zeroOfClass(clazz: Class[_]) = zeroOf(Manifest.classType(clazz))
+ def zeroOf[T](implicit m: Manifest[T]): AnyRef = {
+ if (m == manifest[Boolean] || m == manifest[jl.Boolean]) false: jl.Boolean
+ else if (m == manifest[Unit] || m == manifest[jl.Void]) scala.runtime.BoxedUnit.UNIT
+ else if (m == manifest[Char] || m == manifest[jl.Character]) 0.toChar: jl.Character
+ else if (m == manifest[Byte] || m == manifest[jl.Byte]) 0.toByte: jl.Byte
+ else if (m == manifest[Short] || m == manifest[jl.Short]) 0.toShort: jl.Short
+ else if (m == manifest[Int] || m == manifest[jl.Integer]) 0: jl.Integer
+ else if (m == manifest[Long] || m == manifest[jl.Long]) 0l: jl.Long
+ else if (m == manifest[Float] || m == manifest[jl.Float]) 0f: jl.Float
+ else if (m == manifest[Double] || m == manifest[jl.Double]) 0d: jl.Double
+ else null
+ }
+}
diff --git a/src/compiler/scala/tools/util/SignalManager.scala b/src/compiler/scala/tools/util/SignalManager.scala
index e44bc31081..338a6a2d1a 100644
--- a/src/compiler/scala/tools/util/SignalManager.scala
+++ b/src/compiler/scala/tools/util/SignalManager.scala
@@ -6,87 +6,188 @@
package scala.tools
package util
-class SignalManager // dummy for ant
-
-/** Disabled.
-
-import sun.misc.{ Signal, SignalHandler }
-import SignalHandler._
+import java.lang.reflect.{ Method, Constructor }
+import scala.tools.reflect._
+import scala.collection.{ mutable, immutable }
import nsc.io.timer
+import nsc.util.{ ScalaClassLoader, Exceptional }
-/** Unofficial signal handling code. According to sun it's unsupported,
- * but it's too useful not to take advantage of. Degrade gracefully.
+/** Signal handling code. 100% clean of any references to sun.misc:
+ * it's all reflection and proxies and invocation handlers and lasers,
+ * so even the choosiest runtimes will be cool with it.
+ *
+ * Sun/Oracle says sun.misc.* is unsupported and therefore so is all
+ * of this. Simple examples:
+ * {{{
+ val manager = scala.tools.util.SignalManager // or you could make your own
+ // Assignment clears any old handlers; += chains them.
+ manager("HUP") = println("HUP 1!")
+ manager("HUP") += println("HUP 2!")
+ // Use raise() to raise a signal: this will print both lines
+ manager("HUP").raise()
+ // See a report on every signal's current handler
+ manager.dump()
+ * }}}
*/
-class SignalManager {
- def apply(name: String): SignalWrapper =
- try { new SignalWrapper(new Signal(name)) }
- catch { case x: IllegalArgumentException => new SignalError(x.getMessage) }
+class SignalManager(classLoader: ScalaClassLoader) {
+ def this() = this(ScalaClassLoader.getSystemLoader)
+
+ object rSignalHandler extends Shield {
+ val className = "sun.misc.SignalHandler"
+ val classLoader = SignalManager.this.classLoader
+
+ lazy val SIG_DFL = field("SIG_DFL") get null
+ lazy val SIG_IGN = field("SIG_IGN") get null
+
+ /** Create a new signal handler based on the function.
+ */
+ def apply(action: Invoked => Unit) = Mock.fromInterfaces(clazz) {
+ case inv @ Invoked.NameAndArgs("handle", _ :: Nil) => action(inv)
+ }
+ def empty = rSignalHandler(_ => ())
+ }
+ import rSignalHandler.{ SIG_DFL, SIG_IGN }
+
+ object rSignal extends Shield {
+ val className = "sun.misc.Signal"
+ val classLoader = SignalManager.this.classLoader
+
+ lazy val handleMethod = method("handle", 2)
+ lazy val raiseMethod = method("raise", 1)
+ lazy val numberMethod = method("getNumber", 0)
+
+ /** Create a new Signal with the given name.
+ */
+ def apply(name: String) = constructor(classOf[String]) newInstance name
+ def handle(signal: AnyRef, current: AnyRef) = handleMethod.invoke(null, signal, current)
+ def raise(signal: AnyRef) = raiseMethod.invoke(null, signal)
+ def number(signal: AnyRef): Int = numberMethod.invoke(signal).asInstanceOf[Int]
+
+ class WSignal(val name: String) {
+ lazy val signal = rSignal apply name
+ def number = rSignal number signal
+ def raise() = rSignal raise signal
+ def handle(handler: AnyRef) = rSignal.handle(signal, handler)
+
+ def setTo(body: => Unit) = register(name, false, body)
+ def +=(body: => Unit) = register(name, true, body)
+
+ /** It's hard to believe there's no way to get a signal's current
+ * handler without replacing it, but if there is I couldn't find
+ * it, so we have this swapping code.
+ */
+ def withCurrentHandler[T](f: AnyRef => T): T = {
+ val swap = handle(rSignalHandler.empty)
+
+ try f(swap)
+ finally handle(swap)
+ }
- class ChainedHandler(prev: SignalHandler, current: SignalHandler) extends SignalHandler {
- def handle(sig: Signal): Unit = {
- current handle sig
- if (prev != SIG_DFL && prev != SIG_IGN)
- prev handle sig
+ def handlerString() = withCurrentHandler {
+ case SIG_DFL => "Default"
+ case SIG_IGN => "Ignore"
+ case x => "" + x
+ }
+ override def toString = "%10s %s".format("SIG" + name,
+ try handlerString()
+ catch { case x: Exception => "VM threw " + Exceptional.unwrap(x) }
+ )
}
}
- class SignalWrapper(val signal: Signal) {
- def name = signal.getName
- def add(body: => Unit) = {
- val handler = new SignalHandler { def handle(sig: Signal) = body }
- val prev = Signal.handle(signal, handler)
+ type WSignal = rSignal.WSignal
+
+ /** Adds a handler for the named signal. If shouldChain is true,
+ * the installed handler will call the previous handler after the
+ * new one has executed. If false, the old handler is dropped.
+ */
+ private def register(name: String, shouldChain: Boolean, body: => Unit) = {
+ val signal = rSignal(name)
+ val current = rSignalHandler(_ => body)
+ val prev = rSignal.handle(signal, current)
- new ChainedHandler(prev, handler)
+ if (shouldChain) {
+ val chainer = rSignalHandler { inv =>
+ val signal = inv.args.head
+
+ inv invokeOn current
+ prev match {
+ case SIG_IGN | SIG_DFL => ()
+ case _ => inv invokeOn prev
+ }
+ }
+ rSignal.handle(signal, chainer)
+ chainer
}
- override def toString = "SIG" + name
+ else current
}
- class SignalError(message: String) extends SignalWrapper(null) {
+
+ /** Use apply and update to get and set handlers.
+ */
+ def apply(name: String): WSignal =
+ try { new WSignal(name) }
+ catch { case x: IllegalArgumentException => new SignalError(x.getMessage) }
+
+ def update(name: String, body: => Unit): Unit = apply(name) setTo body
+
+ class SignalError(message: String) extends WSignal(null) {
override def toString = message
}
}
object SignalManager extends SignalManager {
- private implicit def mkSignalWrapper(name: String): SignalWrapper = this(name)
-
- def HUP: SignalWrapper = "HUP"
- def INT: SignalWrapper = "INT"
- def QUIT: SignalWrapper = "QUIT"
- def ILL: SignalWrapper = "ILL"
- def TRAP: SignalWrapper = "TRAP"
- def ABRT: SignalWrapper = "ABRT"
- def EMT: SignalWrapper = "EMT"
- def FPE: SignalWrapper = "FPE"
- def KILL: SignalWrapper = "KILL"
- def BUS: SignalWrapper = "BUS"
- def SEGV: SignalWrapper = "SEGV"
- def SYS: SignalWrapper = "SYS"
- def PIPE: SignalWrapper = "PIPE"
- def ALRM: SignalWrapper = "ALRM"
- def TERM: SignalWrapper = "TERM"
- def URG: SignalWrapper = "URG"
- def STOP: SignalWrapper = "STOP"
- def TSTP: SignalWrapper = "TSTP"
- def CONT: SignalWrapper = "CONT"
- def CHLD: SignalWrapper = "CHLD"
- def TTIN: SignalWrapper = "TTIN"
- def TTOU: SignalWrapper = "TTOU"
- def IO: SignalWrapper = "IO"
- def XCPU: SignalWrapper = "XCPU"
- def XFSZ: SignalWrapper = "XFSZ"
- def VTALRM: SignalWrapper = "VTALRM"
- def PROF: SignalWrapper = "PROF"
- def WINCH: SignalWrapper = "WINCH"
- def INFO: SignalWrapper = "INFO"
- def USR1: SignalWrapper = "USR1"
- def USR2: SignalWrapper = "USR2"
+ private implicit def mkWSignal(name: String): WSignal = this(name)
+ private lazy val signalNumberMap = all map (x => x.number -> x) toMap
+
+ def all = List(
+ HUP, INT, QUIT, ILL, TRAP, ABRT, EMT, FPE, // 1-8
+ KILL, BUS, SEGV, SYS, PIPE, ALRM, TERM, URG, // 9-15
+ STOP, TSTP, CONT, CHLD, TTIN, TTOU, IO, XCPU, // 16-23
+ XFSZ, VTALRM, PROF, WINCH, INFO, USR1, USR2 // 24-31
+ )
+ def dump() = all foreach (x => println("%2s %s".format(x.number, x)))
+
+ def apply(sigNumber: Int): WSignal = signalNumberMap(sigNumber)
+
+ def HUP: WSignal = "HUP"
+ def INT: WSignal = "INT"
+ def QUIT: WSignal = "QUIT"
+ def ILL: WSignal = "ILL"
+ def TRAP: WSignal = "TRAP"
+ def ABRT: WSignal = "ABRT"
+ def EMT: WSignal = "EMT"
+ def FPE: WSignal = "FPE"
+ def KILL: WSignal = "KILL"
+ def BUS: WSignal = "BUS"
+ def SEGV: WSignal = "SEGV"
+ def SYS: WSignal = "SYS"
+ def PIPE: WSignal = "PIPE"
+ def ALRM: WSignal = "ALRM"
+ def TERM: WSignal = "TERM"
+ def URG: WSignal = "URG"
+ def STOP: WSignal = "STOP"
+ def TSTP: WSignal = "TSTP"
+ def CONT: WSignal = "CONT"
+ def CHLD: WSignal = "CHLD"
+ def TTIN: WSignal = "TTIN"
+ def TTOU: WSignal = "TTOU"
+ def IO: WSignal = "IO"
+ def XCPU: WSignal = "XCPU"
+ def XFSZ: WSignal = "XFSZ"
+ def VTALRM: WSignal = "VTALRM"
+ def PROF: WSignal = "PROF"
+ def WINCH: WSignal = "WINCH"
+ def INFO: WSignal = "INFO"
+ def USR1: WSignal = "USR1"
+ def USR2: WSignal = "USR2"
/** Given a number of seconds, a signal, and a function: sets up a handler which upon
* receiving the signal once, calls the function with argument true, and if the
* signal is received again within the allowed time, calls it with argument false.
* (Otherwise it calls it with true and starts the timer over again.)
*/
- def requireInterval(seconds: Int, wrapper: SignalWrapper)(fn: Boolean => Unit) = {
+ def requireInterval(seconds: Int, wrapper: WSignal)(fn: Boolean => Unit) = {
var received = false
- wrapper add {
+ wrapper setTo {
if (received) fn(false)
else {
received = true
@@ -96,5 +197,3 @@ object SignalManager extends SignalManager {
}
}
}
-
-*/
diff --git a/test/pending/run/signals.scala b/test/pending/run/signals.scala
new file mode 100644
index 0000000000..608b3c7fd5
--- /dev/null
+++ b/test/pending/run/signals.scala
@@ -0,0 +1,22 @@
+// not exactly "pending", here as an example usage.
+//
+val manager = scala.tools.util.SignalManager
+
+manager.requireInterval(3, manager.INT) {
+ case true => Console.println("\nPress ctrl-C again to exit.")
+ case false => System.exit(1)
+}
+
+manager("HUP") = println("HUP 1!")
+manager("HUP").raise()
+
+manager("HUP") += println("HUP 2!")
+manager("HUP").raise()
+
+manager("HUP") += println("HUP 3!")
+manager("HUP").raise()
+
+manager("HUP") = println("Back to HUP 1!")
+manager("HUP").raise()
+
+manager.dump()