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  }