github.com/elves/elvish@v0.15.0/pkg/eval/interrupts.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "os" 6 "os/signal" 7 "syscall" 8 ) 9 10 // Interrupts returns a channel that is closed when an interrupt signal comes. 11 func (fm *Frame) Interrupts() <-chan struct{} { 12 return fm.intCh 13 } 14 15 // ErrInterrupted is thrown when the execution is interrupted by a signal. 16 var ErrInterrupted = errors.New("interrupted") 17 18 // IsInterrupted reports whether there has been an interrupt. 19 func (fm *Frame) IsInterrupted() bool { 20 select { 21 case <-fm.Interrupts(): 22 return true 23 default: 24 return false 25 } 26 } 27 28 // ListenInterrupts returns a channel that is closed when SIGINT or SIGQUIT 29 // has been received by the process. It also returns a function that should be 30 // called when the channel is no longer needed. 31 func ListenInterrupts() (<-chan struct{}, func()) { 32 sigCh := make(chan os.Signal) 33 signal.Notify(sigCh, syscall.SIGINT, syscall.SIGQUIT) 34 // Channel to return, closed after receiving the first SIGINT or SIGQUIT. 35 intCh := make(chan struct{}) 36 37 // Closed in the cleanup function to request the relaying goroutine to stop. 38 stop := make(chan struct{}) 39 // Closed in the relaying goroutine to signal that it has stopped. 40 stopped := make(chan struct{}) 41 42 go func() { 43 closed := false 44 loop: 45 for { 46 select { 47 case <-sigCh: 48 if !closed { 49 close(intCh) 50 closed = true 51 } 52 case <-stop: 53 break loop 54 } 55 } 56 signal.Stop(sigCh) 57 close(stopped) 58 }() 59 60 return intCh, func() { 61 close(stop) 62 <-stopped 63 } 64 }