muesli / termenv

Advanced ANSI style & color support for your terminal applications
MIT License
1.71k stars 68 forks source link

Timeouts with Docker TTY #136

Open caarlos0 opened 1 year ago

caarlos0 commented 1 year ago

Not sure if it is intended or not, but if you run a docker container with --tty, many queries termenv does timeout.

Minimum reproducible:

// main.go
package main

import (
    "fmt"

    "github.com/muesli/termenv"
)

func main() {
    fmt.Println("BG", termenv.BackgroundColor())
}
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o a .
docker run --rm --tty -v $PWD:/tmp alpine /tmp/a
johnstcn commented 1 year ago

I amended @caarlos0 's example above to write tracing output:

// main.go
package main

import (
    "fmt"
    "os"
    "runtime/trace"

    "github.com/muesli/termenv"
)

func main() {
    f, err := os.OpenFile("/tmp/trace.out", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
    if err != nil {
        panic(err)
    }
    defer f.Close()
    trace.Start(f)
    defer trace.Stop()
    fmt.Println("BG", termenv.BackgroundColor())
}
// go.mod
module termenvtimeout

go 1.20

require github.com/muesli/termenv v0.15.1

require (
    github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
    github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
    github.com/mattn/go-isatty v0.0.17 // indirect
    github.com/mattn/go-runewidth v0.0.14 // indirect
    github.com/rivo/uniseg v0.2.0 // indirect
    golang.org/x/sys v0.6.0 // indirect
)

Running with Docker (in this case, Colima v0.5.5 aarch64) shows the following:

$ GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o a .
$ docker run --rm --tty -v $PWD:/tmp alpine time /tmp/a
^[]11;rgb:14a7/195f/1efb^G^[[116;1RBG #000000
real    0m 5.03s
user    0m 0.00s
sys 0m 0.00s

I'm attaching the resulting trace, but here's the syscall graph for reference:

image

It looks like the culprit is https://github.com/muesli/termenv/blob/master/termenv_unix.go#L142

korrat commented 1 month ago

I ran into a similar issue with treefmt. When running under lefthook, I observe consistent hangs of almost exactly 10s. Setting the CI=1 environment variable fixes the issue.

After some debugging, I was able to narrow it down to running through PTY.

// client.go
package main

import (
    "os"

    "github.com/muesli/termenv"
)

func main() {
    termenv.NewOutput(os.Stdout)
    termenv.ForegroundColor()
}

// main.go
package main

import (
    "io"
    "os"
    "os/exec"

    "github.com/creack/pty"
)

func main() {
    command := exec.Command("./client")
    p, err := pty.Start(command)
    if err != nil {
        panic(err)
    }

    io.Copy(os.Stdout, p)

    _ = command.Wait()
}

When running, I see a 5s hang:

> gob client.go && time gor main.go
^[]10;rgb:bfbf/bdbd/b6b6^[\^[[68;1Rgo run main.go  0,36s user 0,21s system 10% cpu 5,179 total

I also see garbage output (what looks like escape sequences) on stdout and on the next prompt.