nsf / termbox-go

Pure Go termbox implementation
http://godoc.org/github.com/nsf/termbox-go
MIT License
4.66k stars 372 forks source link

panic: bytes.Buffer: truncation out of range #169

Open rickzter opened 6 years ago

rickzter commented 6 years ago

When using I/O pipes, specifically, os.Pipe(), while reading the stdout and stderr data, termbox suddenly panics by throwing a "bytes.Buffer: truncation out of range".

Here's the stack trace: << panic: bytes.Buffer: truncation out of range

goroutine 1 [running]: panic(0x657da0, 0xc82012a840) /usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6 bytes.(Buffer).Truncate(0xa3d3c0, 0x0) /usr/lib/go-1.6/src/bytes/buffer.go:71 +0xbf bytes.(Buffer).WriteTo(0xa3d3c0, 0x7f5bb03504f8, 0xc820092028, 0x90b, 0x0, 0x0) /usr/lib/go-1.6/src/bytes/buffer.go:225 +0x1af io.copyBuffer(0x7f5bb03504f8, 0xc820092028, 0x7f5bb0350520, 0xa3d3c0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) /usr/lib/go-1.6/src/io/io.go:370 +0xd0 io.Copy(0x7f5bb03504f8, 0xc820092028, 0x7f5bb0350520, 0xa3d3c0, 0xa3d3c0, 0x0, 0x0) /usr/lib/go-1.6/src/io/io.go:350 +0x64 github.com/nsf/termbox-go.flush(0x0, 0x0) /home/rickzter/Dev/go/src/github.com/nsf/termbox-go/termbox.go:234 +0x92 github.com/nsf/termbox-go.Flush(0x0, 0x0) /home/rickzter/Dev/go/src/github.com/nsf/termbox-go/api.go:199 +0x224

It appears it originates at termbox.Flush(). My function is calling termbox.Clear and finally termbox.Flush(). Any ideas?

I have also encountered this error when using exec.StdoutPipe() as well. It seems termbox does not play nice with I/O pipes. Any workarounds? My program relies on external process communication and this is a show stopper for my app.

rickzter commented 6 years ago

I would also like to mention that it is not always the case, sometimes, termbox will not panic and continue, successfully handling the I/O pipe activity, but if I repeat the process, sometimes multiple times, it will eventually panic. Sometimes on the first try.

rickzter commented 6 years ago

I am calling termbox.Flush through my function inside a goroutine and using a bufio scanner.

nsf commented 6 years ago

Seems like a race condition. If you do termbox.Flush from multiple goroutines. This is now something allowed by the termbox. You can read input (PollEvent) from one goroutine and write output (Flush) in another goroutine. But going beyond that will result in race conditions.

rickzter commented 6 years ago

This happens with just running termbox.Flush() from one goroutine. termbox.Flush is being called for each line being read. I can understand if multiple threads were calling termbox.Flush(), but you mentioned it is supported, however, I am able to crash it by only running one go routine alongside the command I am piping to/from.

Here is the code where the problem is originating from:

r, w, := os.Pipe() stdOutOld := os.Stdout stdErrOld := os.Stderr os.Stdout = w os.Stderr = w go func() { scanner := bufio.NewScanner(r) for scanner.Scan() { ConsoleWriteLine(scanner.Text()) } termbox.Sync() }() , err := parser.ParseArgs(args) os.Stdout = stdOutOld os.Stderr = stdErrOld

ConsoleWriteLine() then calls DrawTerminal():

func (t *Terminal) DrawTerminal() { termbox.Clear(fg, bg) termbox.Flush() }

I am using go-flags parser to parse arguments in my cmd program and basically trying to intercept I/O from this library. It writes to stdout and stderr and redirecting it to my function "ConsoleWriteLine" which stores the information in my output buffer, this function then calls "DrawTerminal()" which clears the termbox buffer and displays it onscreen. Very simple. I am not running multiple goroutines to get this to crash, only this code above with one goroutine running asynchronous to "parser.ParseArgs" to capture I/O.

rickzter commented 6 years ago

I was just able to reproduce the error by simply calling termbox.Flush() from a goroutine. It may be related to #113 .