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  }