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