github.com/elves/elvish@v0.15.0/pkg/cli/term/file_reader_unix.go (about)

     1  // +build !windows,!plan9
     2  
     3  package term
     4  
     5  import (
     6  	"io"
     7  	"os"
     8  	"sync"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/elves/elvish/pkg/sys"
    13  )
    14  
    15  // A helper for reading from a file.
    16  type fileReader interface {
    17  	byteReaderWithTimeout
    18  	// Stop stops any outstanding read call. It blocks until the read returns.
    19  	Stop() error
    20  	// Close releases new resources allocated for the fileReader. It does not
    21  	// close the underlying file.
    22  	Close()
    23  }
    24  
    25  func newFileReader(file *os.File) (fileReader, error) {
    26  	rStop, wStop, err := os.Pipe()
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	return &bReader{file: file, rStop: rStop, wStop: wStop}, nil
    31  }
    32  
    33  type bReader struct {
    34  	file  *os.File
    35  	rStop *os.File
    36  	wStop *os.File
    37  	// A mutex that is held when Read is in process.
    38  	mutex sync.Mutex
    39  }
    40  
    41  func (r *bReader) ReadByteWithTimeout(timeout time.Duration) (byte, error) {
    42  	r.mutex.Lock()
    43  	defer r.mutex.Unlock()
    44  	for {
    45  		ready, err := sys.WaitForRead(timeout, r.file, r.rStop)
    46  		if err != nil {
    47  			if err == syscall.EINTR {
    48  				continue
    49  			}
    50  			return 0, err
    51  		}
    52  		if ready[1] {
    53  			var b [1]byte
    54  			r.rStop.Read(b[:])
    55  			return 0, ErrStopped
    56  		}
    57  		if !ready[0] {
    58  			return 0, errTimeout
    59  		}
    60  		var b [1]byte
    61  		nr, err := r.file.Read(b[:])
    62  		if err != nil {
    63  			return 0, err
    64  		}
    65  		if nr != 1 {
    66  			return 0, io.ErrNoProgress
    67  		}
    68  		return b[0], nil
    69  	}
    70  }
    71  
    72  func (r *bReader) Stop() error {
    73  	_, err := r.wStop.Write([]byte{'q'})
    74  	r.mutex.Lock()
    75  	r.mutex.Unlock()
    76  	return err
    77  }
    78  
    79  func (r *bReader) Close() {
    80  	r.rStop.Close()
    81  	r.wStop.Close()
    82  }