Home | History | Annotate | Download | only in signal
      1 // Copyright 2012 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package signal implements access to incoming signals.
      6 package signal
      7 
      8 import (
      9 	"os"
     10 	"sync"
     11 )
     12 
     13 var handlers struct {
     14 	sync.Mutex
     15 	m   map[chan<- os.Signal]*handler
     16 	ref [numSig]int64
     17 }
     18 
     19 type handler struct {
     20 	mask [(numSig + 31) / 32]uint32
     21 }
     22 
     23 func (h *handler) want(sig int) bool {
     24 	return (h.mask[sig/32]>>uint(sig&31))&1 != 0
     25 }
     26 
     27 func (h *handler) set(sig int) {
     28 	h.mask[sig/32] |= 1 << uint(sig&31)
     29 }
     30 
     31 func (h *handler) clear(sig int) {
     32 	h.mask[sig/32] &^= 1 << uint(sig&31)
     33 }
     34 
     35 // Stop relaying the signals, sigs, to any channels previously registered to
     36 // receive them and either reset the signal handlers to their original values
     37 // (action=disableSignal) or ignore the signals (action=ignoreSignal).
     38 func cancel(sigs []os.Signal, action func(int)) {
     39 	handlers.Lock()
     40 	defer handlers.Unlock()
     41 
     42 	remove := func(n int) {
     43 		var zerohandler handler
     44 
     45 		for c, h := range handlers.m {
     46 			if h.want(n) {
     47 				handlers.ref[n]--
     48 				h.clear(n)
     49 				if h.mask == zerohandler.mask {
     50 					delete(handlers.m, c)
     51 				}
     52 			}
     53 		}
     54 
     55 		action(n)
     56 	}
     57 
     58 	if len(sigs) == 0 {
     59 		for n := 0; n < numSig; n++ {
     60 			remove(n)
     61 		}
     62 	} else {
     63 		for _, s := range sigs {
     64 			remove(signum(s))
     65 		}
     66 	}
     67 }
     68 
     69 // Ignore causes the provided signals to be ignored. If they are received by
     70 // the program, nothing will happen. Ignore undoes the effect of any prior
     71 // calls to Notify for the provided signals.
     72 // If no signals are provided, all incoming signals will be ignored.
     73 func Ignore(sig ...os.Signal) {
     74 	cancel(sig, ignoreSignal)
     75 }
     76 
     77 // Notify causes package signal to relay incoming signals to c.
     78 // If no signals are provided, all incoming signals will be relayed to c.
     79 // Otherwise, just the provided signals will.
     80 //
     81 // Package signal will not block sending to c: the caller must ensure
     82 // that c has sufficient buffer space to keep up with the expected
     83 // signal rate.  For a channel used for notification of just one signal value,
     84 // a buffer of size 1 is sufficient.
     85 //
     86 // It is allowed to call Notify multiple times with the same channel:
     87 // each call expands the set of signals sent to that channel.
     88 // The only way to remove signals from the set is to call Stop.
     89 //
     90 // It is allowed to call Notify multiple times with different channels
     91 // and the same signals: each channel receives copies of incoming
     92 // signals independently.
     93 func Notify(c chan<- os.Signal, sig ...os.Signal) {
     94 	if c == nil {
     95 		panic("os/signal: Notify using nil channel")
     96 	}
     97 
     98 	handlers.Lock()
     99 	defer handlers.Unlock()
    100 
    101 	h := handlers.m[c]
    102 	if h == nil {
    103 		if handlers.m == nil {
    104 			handlers.m = make(map[chan<- os.Signal]*handler)
    105 		}
    106 		h = new(handler)
    107 		handlers.m[c] = h
    108 	}
    109 
    110 	add := func(n int) {
    111 		if n < 0 {
    112 			return
    113 		}
    114 		if !h.want(n) {
    115 			h.set(n)
    116 			if handlers.ref[n] == 0 {
    117 				enableSignal(n)
    118 			}
    119 			handlers.ref[n]++
    120 		}
    121 	}
    122 
    123 	if len(sig) == 0 {
    124 		for n := 0; n < numSig; n++ {
    125 			add(n)
    126 		}
    127 	} else {
    128 		for _, s := range sig {
    129 			add(signum(s))
    130 		}
    131 	}
    132 }
    133 
    134 // Reset undoes the effect of any prior calls to Notify for the provided
    135 // signals.
    136 // If no signals are provided, all signal handlers will be reset.
    137 func Reset(sig ...os.Signal) {
    138 	cancel(sig, disableSignal)
    139 }
    140 
    141 // Stop causes package signal to stop relaying incoming signals to c.
    142 // It undoes the effect of all prior calls to Notify using c.
    143 // When Stop returns, it is guaranteed that c will receive no more signals.
    144 func Stop(c chan<- os.Signal) {
    145 	handlers.Lock()
    146 	defer handlers.Unlock()
    147 
    148 	h := handlers.m[c]
    149 	if h == nil {
    150 		return
    151 	}
    152 	delete(handlers.m, c)
    153 
    154 	for n := 0; n < numSig; n++ {
    155 		if h.want(n) {
    156 			handlers.ref[n]--
    157 			if handlers.ref[n] == 0 {
    158 				disableSignal(n)
    159 			}
    160 		}
    161 	}
    162 }
    163 
    164 func process(sig os.Signal) {
    165 	n := signum(sig)
    166 	if n < 0 {
    167 		return
    168 	}
    169 
    170 	handlers.Lock()
    171 	defer handlers.Unlock()
    172 
    173 	for c, h := range handlers.m {
    174 		if h.want(n) {
    175 			// send but do not block for it
    176 			select {
    177 			case c <- sig:
    178 			default:
    179 			}
    180 		}
    181 	}
    182 }
    183