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

@ -14,8 +14,8 @@ type UI struct {
ctx *Context ctx *Context
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,
@ -40,24 +40,22 @@ func Initialize(conf *config.AercConfig,
ctx: NewContext(width, height, screen), ctx: NewContext(width, height, screen),
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 { default:
// Flush any other pending invalidations }
select {
case <-state.invalidations: wasInvalid := atomic.SwapInt32(&state.invalid, 0)
break if wasInvalid != 0 {
default:
goto done
}
}
done:
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
} }