github.com/iDigitalFlame/xmt@v0.5.4/com/limits/signal_no_compat.go (about) 1 //go:build go1.14 2 // +build go1.14 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 // Package limits contains many options for setting Global limits on how the 21 // overall application behaves. Many of these options are configured by build tags. 22 // 23 // Other functions include re-implemented standard library functions. 24 package limits 25 26 import ( 27 "os" 28 "os/signal" 29 "sync" 30 "sync/atomic" 31 "time" 32 33 // Importing unsafe to use the linkname call 34 _ "unsafe" 35 ) 36 37 var ( 38 watchChan chan struct{} 39 watchStart sync.Once 40 watchStarted uint32 41 ) 42 43 //go:linkname watchSignalLoop os/signal.watchSignalLoop 44 var watchSignalLoop func() 45 46 func watchClose() { 47 <-watchChan 48 atomic.StoreUint32(&watchStarted, 2) 49 signalSend(0) 50 } 51 func watchSignals() { 52 // NOTE(dij): Start is here so we can prevent early attempts at waiting on 53 // a non-ready channel. 54 atomic.StoreUint32(&watchStarted, 1) 55 go watchClose() 56 for { 57 s := signalRecv() 58 if s == 0 && atomic.LoadUint32(&watchStarted) == 2 { 59 break 60 } 61 process(convertSignal(s)) 62 } 63 close(watchChan) 64 } 65 func startSignals() { 66 watchChan = make(chan struct{}) 67 watchSignalLoop = watchSignals 68 signalEnable(0) 69 } 70 71 //go:linkname process os/signal.process 72 func process(os.Signal) 73 74 //go:linkname signalRecv os/signal.signal_recv 75 func signalRecv() uint32 76 77 //go:linkname signalEnable os/signal.enableSignal 78 func signalEnable(uint32) 79 80 //go:linkname signalSend runtime.sigsend 81 func signalSend(uint32) bool 82 83 // StopNotify will stop the signal handling loop from running and will cause 84 // all signal handling to stop. 85 // 86 // This function will block until the Goroutine closes. 87 // 88 // This function has no effect if the loop is not started or stopped. 89 // 90 // The supplied chan can be nil but if non-nil will be passed to 'signal.Stop' 91 // for convince. 92 // 93 // If the Go version is 1.13 or less this function is just a wrapper for 94 // 'signal.Stop'. 95 func StopNotify(c chan<- os.Signal) { 96 if c != nil { 97 signal.Stop(c) 98 } 99 if atomic.LoadUint32(&watchStarted) == 1 { 100 watchChan <- struct{}{} 101 // NOTE(dij): Add a small NOP here, so we don't pull the value out the 102 // channel that's made to signal the other thread. We technically 103 // could call GoYield(), but this might be easier. 104 time.Sleep(time.Millisecond * 500) 105 <-watchChan 106 } 107 } 108 109 // Notify causes package signal to relay incoming signals to c. 110 // If no signals are provided, all incoming signals will be relayed to c. 111 // Otherwise, just the provided signals will. 112 // 113 // Package signal will not block sending to c: the caller must ensure 114 // that c has sufficient buffer space to keep up with the expected 115 // signal rate. For a channel used for notification of just one signal value, 116 // a buffer of size 1 is sufficient. 117 // 118 // It is allowed to call Notify multiple times with the same channel: 119 // each call expands the set of signals sent to that channel. 120 // The only way to remove signals from the set is to call Stop. 121 // 122 // It is allowed to call Notify multiple times with different channels 123 // and the same signals: each channel receives copies of incoming 124 // signals independently. 125 // 126 // This version will stop the signal handling loop once the 'StopNotify' 127 // function has been called. 128 // 129 // If the Go version is 1.13 or less this function is just a wrapper for 130 // 'signal.Notify'. 131 func Notify(c chan<- os.Signal, s ...os.Signal) { 132 watchStart.Do(startSignals) 133 signal.Notify(c, s...) 134 }