-
-
Notifications
You must be signed in to change notification settings - Fork 240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reading from PTY in OSX makes the CPU go to 100% #52
Comments
I also tried writing my own copy function to see what was happening, log a little, but there is no use as it is a single |
Could you confirm the golang version for both your OSX and Ubuntu tests? |
Docker container golang:latest (id=1cdc81f11b10): OSX version 10.12.6: |
Thanks. For reference, I reproduced the issue with: package main
import (
"github.com/kr/pty"
)
func main() {
pty, _, err := pty.Open()
if err != nil {
panic(err)
}
_, _ = pty.Read([]byte{0})
} |
After investigating, it looks like the issues comes from flags added by Go's stdlib. Consider the following snippet: package main
import (
"errors"
"os"
"syscall"
"unsafe"
)
func ioctl(fd, cmd, ptr uintptr) error {
if _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr); e != 0 {
return e
}
return nil
}
func grantpt(fd uintptr) error {
return ioctl(fd, syscall.TIOCPTYGRANT, 0)
}
func unlockpt(fd uintptr) error {
return ioctl(fd, syscall.TIOCPTYUNLK, 0)
}
func ptsname(fd uintptr) (string, error) {
n := make([]byte, 128)
if err := ioctl(fd, syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))); err != nil {
return "", err
}
for i, c := range n {
if c == 0 {
return string(n[:i]), nil
}
}
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
}
func main1() {
// Open master.
ptmxFd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR, 0)
if err != nil {
panic(err)
}
fd := uintptr(ptmxFd)
// Fetch next available slave.
sname, err := ptsname(fd)
if err != nil {
panic(err)
}
// Grant/unlock slave.
if err := grantpt(fd); err != nil {
panic(err)
}
if err := unlockpt(fd); err != nil {
panic(err)
}
// Open slave.
tFd, err := syscall.Open(sname, syscall.O_RDWR, 0)
if err != nil {
panic(err)
}
_ = tFd
// Read master.
if _, err := syscall.Read(ptmxFd, []byte{0}); err != nil {
panic(err)
}
}
func main() {
// Open master.
ptmx, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
if err != nil {
panic(err)
}
fd := ptmx.Fd()
// Fetch next available slave.
sname, err := ptsname(fd)
if err != nil {
panic(err)
}
// Grant/unlock slave.
if err := grantpt(fd); err != nil {
panic(err)
}
if err := unlockpt(fd); err != nil {
panic(err)
}
// Open slave.
t, err := os.OpenFile(sname, os.O_RDWR, 0)
if err != nil {
panic(err)
}
_ = t
// Read slave.
if _, err := ptmx.Read([]byte{0}); err != nil {
panic(err)
}
}
|
Great work! Very interesting. |
Ok, I confirm the issue, it is due to the
This in turn tries to add the file to the poller. https://github.com/golang/go/blob/9f7fd893dc455339233a8e081f5fb5e2c51e7b5d/src/os/file_unix.go#L105 It then sets the non-block flag, which fails on the master, but works on the slave, my guess is that this is why it ends up taking 100% CPU https://github.com/golang/go/blob/9f7fd893dc455339233a8e081f5fb5e2c51e7b5d/src/os/file_unix.go#L115 I'm working on a fix now and I'll submit a report upstream to see if it is an expected behavior from the lib or an actual bug. I think the lib should expose a way to toggle the |
Solved by #53 |
The following go code running on OSX (version 10.12.6, MacBook Pro (Retina, 15-inch, Mid 2014)) will cause the CPU to go to 100% within 10 seconds, and stay there. It is due to the io.Copy(stdout, t) line. It doesn't happen on Ubuntu 16.04 (nor Android 5.1.1).
Anyone have any ideas as to why?
The text was updated successfully, but these errors were encountered: