lib/ui/ui: use atomic instead of channel

This makes it so an atomic `invalid` value is used instead of an unbuffered
channel. When many invalidations kick in, a lot of values were sent to the
channel.

(Since OnInvalidate's callback can be run in any goroutine, we need to be
careful about races here.)
This commit is contained in:
Simon Ser 2019-05-19 09:50:14 +00:00 committed by Drew DeVault
parent a15ea01cfb
commit 7c6325977b

View file

@ -15,7 +15,7 @@ type UI struct {
screen tcell.Screen screen tcell.Screen
tcEvents chan tcell.Event tcEvents chan tcell.Event
invalidations chan interface{} invalid int32 // access via atomic
} }
func Initialize(conf *config.AercConfig, func Initialize(conf *config.AercConfig,
@ -41,23 +41,21 @@ func Initialize(conf *config.AercConfig,
screen: screen, screen: screen,
tcEvents: make(chan tcell.Event, 10), tcEvents: make(chan tcell.Event, 10),
invalidations: make(chan interface{}),
} }
state.exit.Store(false) state.exit.Store(false)
go (func() { go func() {
for !state.ShouldExit() { for !state.ShouldExit() {
state.tcEvents <- screen.PollEvent() state.tcEvents <- screen.PollEvent()
} }
})() }()
go (func() {
state.invalidations <- nil state.invalid = 1
})()
content.OnInvalidate(func(_ Drawable) { content.OnInvalidate(func(_ Drawable) {
go (func() { atomic.StoreInt32(&state.invalid, 1)
state.invalidations <- nil
})()
}) })
content.Focus(true) content.Focus(true)
return &state, nil return &state, nil
} }
@ -74,6 +72,8 @@ func (state *UI) Close() {
} }
func (state *UI) Tick() bool { func (state *UI) Tick() bool {
more := false
select { select {
case event := <-state.tcEvents: case event := <-state.tcEvents:
switch event := event.(type) { switch event := event.(type) {
@ -84,21 +84,16 @@ func (state *UI) Tick() bool {
state.Content.Invalidate() state.Content.Invalidate()
} }
state.Content.Event(event) state.Content.Event(event)
case <-state.invalidations: more = true
for {
// Flush any other pending invalidations
select {
case <-state.invalidations:
break
default: default:
goto done
} }
}
done: wasInvalid := atomic.SwapInt32(&state.invalid, 0)
if wasInvalid != 0 {
state.Content.Draw(state.ctx) state.Content.Draw(state.ctx)
state.screen.Show() state.screen.Show()
default: more = true
return false
} }
return true
return more
} }