gizak / termui

Golang terminal dashboard
MIT License
13.11k stars 786 forks source link

Table: Fix changing Rows during runtime #136

Closed rck closed 5 years ago

rck commented 7 years ago

This fixes a concurrency problem in termui.Table. This was the part I cared most about, but maybe this is also a starting point if these kind of problems should get fixed deeper down in the core or per termui module.

Original commit message:

So far changing table.Rows had two problems that made it panic in dynamic environments, where users tried to reassign new table.Rows.

This introduces a sync.Mutex and a setter method. The implementation of public methods that are also used in Buffer() got split out to a private implementation (e.g. Analysis -> analysis). The public implementation now does the locking and calls the private implementation. This allows us to take the lock once in Buffer() and then call multiple internal implementations.

The setter (i.e., SetRows()) also takes care of allocating/initializing potentially new Color elements. As commented in the source code, currently not used Color elements are kept as cache without immediately deallocating them.

rck commented 7 years ago

Hi guys, any updates on that?

Am I using the table wrong and you think the number of rows has to be fixed and a public member (Rows) is not allowed to be touched? Do you need an example that this actually happens without the patches?

Regards, rck

maxdec commented 7 years ago

I'm facing that issue too. It's annoying for a long-running dashboard.

rck commented 7 years ago

@maxdec : did you try the patched version? I use LINBIT/termui for a project, basically also a long-running, dynamic dashboard, and have not had any problems since.

Somehow strange that this simply gets ignored...

maxdec commented 7 years ago

Yep I'm using that fork and it works great. I'm no Go expert but the changes make sense to me. (Maybe it needs specs?)

toby1991 commented 7 years ago

Facing the same issue

cjbassi commented 5 years ago

Hi, thanks for this PR! So most of termui was reworked in 958a285, and I think this issue has been taken care of since then. Table just has a Draw (previously Buffer) function now that renders the widget and the helper functions have been removed. The only time you should get a race condition with any widgets now is if you have 2 goroutines: 1 for rendering widgets, and 1 for updating any widgets, in which case you have to have a mutex.

Of course, if you run into issues still or notice something missing, feel free to create a new issue or comment here.

Also, if you're curious, the way I've handled race conditions in gotop is by having a loop in main that renders the widgets every second (check out the termui rework/examples for how to do this now) and locks a RWMutex for writing (so it has a unique lock on it) when it renders, and then I have a goroutine for each widget that updates the widget periodically and locks the RWMutex for reading (so it has a shared lock) when it updates. This has been working well for me, but let me know if there are any of ways of doing this.