mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-11 11:09:16 +02:00
Handle EINTR
This commit is contained in:
@@ -19,14 +19,36 @@ type Term struct {
|
||||
states []unix.Termios
|
||||
}
|
||||
|
||||
func eintr_retry_noret(f func() error) error {
|
||||
for {
|
||||
qerr := f()
|
||||
if qerr == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
return qerr
|
||||
}
|
||||
}
|
||||
|
||||
func eintr_retry_intret(f func() (int, error)) (int, error) {
|
||||
for {
|
||||
q, qerr := f()
|
||||
if qerr == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
return q, qerr
|
||||
}
|
||||
}
|
||||
|
||||
func OpenTerm(name string, in_raw_mode bool) (self *Term, err error) {
|
||||
fd, err := unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666)
|
||||
fd, err := eintr_retry_intret(func() (int, error) {
|
||||
return unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: err}
|
||||
}
|
||||
|
||||
self = &Term{name: name, fd: fd}
|
||||
err = unix.SetNonblock(self.fd, false)
|
||||
err = eintr_retry_noret(func() error { return unix.SetNonblock(self.fd, false) })
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -46,19 +68,27 @@ func OpenControllingTerm(in_raw_mode bool) (self *Term, err error) {
|
||||
func (self *Term) Fd() int { return self.fd }
|
||||
|
||||
func (self *Term) Close() error {
|
||||
err := unix.Close(self.fd)
|
||||
err := eintr_retry_noret(func() error { return unix.Close(self.fd) })
|
||||
self.fd = -1
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *Term) Tcgetattr(ans *unix.Termios) error {
|
||||
return eintr_retry_noret(func() error { return termios.Tcgetattr(uintptr(self.fd), ans) })
|
||||
}
|
||||
|
||||
func (self *Term) Tcsetattr(when uintptr, ans *unix.Termios) error {
|
||||
return eintr_retry_noret(func() error { return termios.Tcsetattr(uintptr(self.fd), when, ans) })
|
||||
}
|
||||
|
||||
func (self *Term) SetRawWhen(when uintptr) (err error) {
|
||||
var state unix.Termios
|
||||
if err = termios.Tcgetattr(uintptr(self.fd), &state); err != nil {
|
||||
if err = self.Tcgetattr(&state); err != nil {
|
||||
return
|
||||
}
|
||||
new_state := state
|
||||
termios.Cfmakeraw(&new_state)
|
||||
err = termios.Tcsetattr(uintptr(self.fd), when, &new_state)
|
||||
err = self.Tcsetattr(when, &new_state)
|
||||
if err != nil {
|
||||
self.states = append(self.states, state)
|
||||
}
|
||||
@@ -74,7 +104,7 @@ func (self *Term) PopStateWhen(when uintptr) (err error) {
|
||||
return nil
|
||||
}
|
||||
idx := len(self.states) - 1
|
||||
err = termios.Tcsetattr(uintptr(self.fd), when, &self.states[idx])
|
||||
err = self.Tcsetattr(when, &self.states[idx])
|
||||
if err != nil {
|
||||
self.states = self.states[:idx]
|
||||
}
|
||||
@@ -121,12 +151,12 @@ func get_vmin_and_vtime(d time.Duration) (uint8, uint8) {
|
||||
|
||||
func (self *Term) SetReadTimeout(d time.Duration) (err error) {
|
||||
var a unix.Termios
|
||||
if err := termios.Tcgetattr(uintptr(self.fd), &a); err != nil {
|
||||
if err := self.Tcgetattr(&a); err != nil {
|
||||
return err
|
||||
}
|
||||
b := a
|
||||
b.Cc[unix.VMIN], b.Cc[unix.VTIME] = get_vmin_and_vtime(d)
|
||||
err = termios.Tcsetattr(uintptr(self.fd), termios.TCSANOW, &b)
|
||||
err = self.Tcsetattr(termios.TCSANOW, &b)
|
||||
if err != nil {
|
||||
self.states = append(self.states, a)
|
||||
}
|
||||
@@ -134,23 +164,33 @@ func (self *Term) SetReadTimeout(d time.Duration) (err error) {
|
||||
}
|
||||
|
||||
func (self *Term) ReadWithTimeout(b []byte, d time.Duration) (n int, err error) {
|
||||
var read, write, in_err unix.FdSet
|
||||
tv := NsecToTimespec(d)
|
||||
read.Set(self.fd)
|
||||
num_ready, err := unix.Pselect(self.fd+1, &read, &write, &in_err, &tv, nil)
|
||||
if err != nil {
|
||||
return
|
||||
var read, write, in_err unix.FdSet
|
||||
pselect := func() (int, error) {
|
||||
read.Zero()
|
||||
write.Zero()
|
||||
in_err.Zero()
|
||||
read.Set(self.fd)
|
||||
return unix.Pselect(self.fd+1, &read, &write, &in_err, &tv, nil)
|
||||
}
|
||||
if num_ready == 0 {
|
||||
err = os.ErrDeadlineExceeded
|
||||
return
|
||||
for {
|
||||
num_ready, qerr := eintr_retry_intret(pselect)
|
||||
if qerr != nil {
|
||||
err = qerr
|
||||
return
|
||||
}
|
||||
if num_ready == 0 {
|
||||
err = os.ErrDeadlineExceeded
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return self.Read(b)
|
||||
}
|
||||
|
||||
func (t *Term) Read(b []byte) (int, error) {
|
||||
n, e := unix.Read(t.fd, b)
|
||||
n, e := eintr_retry_intret(func() (int, error) { return unix.Read(t.fd, b) })
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
@@ -164,7 +204,7 @@ func (t *Term) Read(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
func (t *Term) Write(b []byte) (int, error) {
|
||||
n, e := unix.Write(t.fd, b)
|
||||
n, e := eintr_retry_intret(func() (int, error) { return unix.Write(t.fd, b) })
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
@@ -202,16 +242,21 @@ func (self *Term) WriteAllWithTimeout(b []byte, d time.Duration) (n int, err err
|
||||
var read, write, in_err unix.FdSet
|
||||
var num_ready int
|
||||
n = len(b)
|
||||
sysnv := NsecToTimespec(d)
|
||||
pselect := func() (int, error) {
|
||||
write.Zero()
|
||||
read.Zero()
|
||||
in_err.Zero()
|
||||
write.Set(self.fd)
|
||||
return unix.Pselect(self.fd+1, &read, &write, &in_err, &sysnv, nil)
|
||||
}
|
||||
for {
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
sysnv := NsecToTimespec(d)
|
||||
read.Zero()
|
||||
write.Zero()
|
||||
in_err.Zero()
|
||||
write.Set(self.fd)
|
||||
num_ready, err = unix.Pselect(self.fd+1, &read, &write, &in_err, &sysnv, nil)
|
||||
num_ready, err = pselect()
|
||||
if err != nil {
|
||||
n -= len(b)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user