gocui icon indicating copy to clipboard operation
gocui copied to clipboard

force update

Open ripienaar opened this issue 4 years ago • 6 comments

I used g.Update to append lines to a text box that scrolls - like a tail -f.

Periodically i remove lines from buffer so the size doesnt grow forever, however the fact that g.Update() creates a go routine leaks lots of memory over time. After a few 10k lines you end up with 10k go routines worth of descriptors in memory that will never be freed.

Editing the code and changing update to:

func (g *Gui) Update(f func(*Gui) error) {
	g.userEvents <- userEvent{f: f}
}

improves things for me drastically without impacting anything noticable. Can you perhaps tell me the reason why Update does this in a go routine? Might we add a SyncUpdate or similar that doesnt create a go routine?

ripienaar avatar Jun 28 '20 15:06 ripienaar

I implemented something similar but left the original Update function alone (for backwards compatibility), and created an "Update2" function of that does the same code.

Below is code I have to trim the maximum lines in the buffer, with some other bug fixes. I should make a PR for this.

diff --git a/view.go b/view.go
index 42082f8..0b81d66 100644
--- a/view.go
+++ b/view.go
@@ -71,6 +71,9 @@ type View struct {
        // If Mask is true, the View will display the mask instead of the real
        // content
        Mask rune
+
+       // Set the maximum number of lines to store
+       MaxLines int
 }
 
 type viewLine struct {
@@ -215,7 +218,7 @@ func (v *View) Write(p []byte) (n int, err error) {
                        }
                default:
                        cells := v.parseInput(ch)
-                       if cells == nil {
+                       if cells == nil || len(cells) <= 0 {
                                continue
                        }
 
@@ -227,6 +230,12 @@ func (v *View) Write(p []byte) (n int, err error) {
                        }
                }
        }
+
+       if v.MaxLines > 0 && len(v.lines) > v.MaxLines {
+               cut := len(v.lines) - v.MaxLines
+               v.lines = v.lines[cut:]
+       }
+
        return len(p), nil
 }
 
@@ -322,7 +331,7 @@ func (v *View) draw() error {
        }
 
        if v.Autoscroll && len(v.viewLines) > maxY {
-               v.oy = len(v.viewLines) - maxY
+               v.oy = len(v.viewLines) - maxY - 1
        }
        y := 0
        for i, vline := range v.viewLines {

dsouzae avatar Jul 02 '20 17:07 dsouzae

I would be able to add a function called UpdateAsync(), this way we wouldn't change any existing code and still have the wanted functionalities.

What are your thoughts

glvr182 avatar Jul 03 '20 16:07 glvr182

Ideally to avoid a unbounded growth it would be nice if there was a maxlines setting as suggested.

But yes, the UpdateAsync() would be great. If you agree with that I can send a PR

ripienaar avatar Jul 03 '20 16:07 ripienaar

Yea totally! Go for it!

glvr182 avatar Jul 03 '20 17:07 glvr182

Can you open it on @awesome-gocui we run a fork of this project theere since the original ownwer hasnt responded to anything in a while

glvr182 avatar Jul 03 '20 20:07 glvr182

OK, I opened https://github.com/awesome-gocui/gocui/pull/67

ripienaar avatar Jul 04 '20 08:07 ripienaar