termenv icon indicating copy to clipboard operation
termenv copied to clipboard

Timeouts with Docker TTY

Open caarlos0 opened this issue 1 year ago • 2 comments

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

caarlos0 avatar May 10 '23 19:05 caarlos0

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

johnstcn avatar Jun 01 '23 09:06 johnstcn

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.

korrat avatar Jul 26 '24 14:07 korrat