file_reader_unix.go 1.6 KB

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