summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/util/SignalManager.scala
blob: a6513ecb5083d133a752aa922fa4686513fca8de (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* NSC -- new Scala compiler
 * Copyright 2005-2010 LAMP/EPFL
 * @author Paul Phillips
 */

package scala.tools
package util

import sun.misc.{ Signal, SignalHandler }
import SignalHandler._
import nsc.io.timer

/** Unofficial signal handling code.  According to sun it's unsupported,
 *  but it's too useful not to take advantage of.  Degrade gracefully.
 */
class SignalManager {
  def apply(name: String): SignalWrapper =
    try   { new SignalWrapper(new Signal(name)) }
    catch { case x: IllegalArgumentException => new SignalError(x.getMessage) }

  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
    }
  }
  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)

      new ChainedHandler(prev, handler)
    }
    override def toString = "SIG" + name
  }
  class SignalError(message: String) extends SignalWrapper(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"

  /** 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) = {
    var received = false
    wrapper add {
      if (received) fn(false)
      else {
        received = true
        fn(true)
        timer(seconds)(received = false)
      }
    }
  }
}