summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2019-05-19 09:50:14 +0000
committerDrew DeVault <sir@cmpwn.com>2019-05-19 11:51:18 -0400
commit7c6325977b55385bc65f0a08f4da8ed6dfede52a (patch)
tree0aac968a8ade4204e57463ec68de7048eaf04668
parenta15ea01cfb0a303355b2e6bb31e85ece0d048ac2 (diff)
downloadaerc-7c6325977b55385bc65f0a08f4da8ed6dfede52a.tar.gz
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.)
-rw-r--r--lib/ui/ui.go47
1 files changed, 21 insertions, 26 deletions
diff --git a/lib/ui/ui.go b/lib/ui/ui.go
index 91a26da..f04d3d8 100644
--- a/lib/ui/ui.go
+++ b/lib/ui/ui.go
@@ -14,8 +14,8 @@ type UI struct {
 	ctx     *Context
 	screen  tcell.Screen
 
-	tcEvents      chan tcell.Event
-	invalidations chan interface{}
+	tcEvents chan tcell.Event
+	invalid  int32 // access via atomic
 }
 
 func Initialize(conf *config.AercConfig,
@@ -40,24 +40,22 @@ func Initialize(conf *config.AercConfig,
 		ctx:     NewContext(width, height, screen),
 		screen:  screen,
 
-		tcEvents:      make(chan tcell.Event, 10),
-		invalidations: make(chan interface{}),
+		tcEvents: make(chan tcell.Event, 10),
 	}
+
 	state.exit.Store(false)
-	go (func() {
+	go func() {
 		for !state.ShouldExit() {
 			state.tcEvents <- screen.PollEvent()
 		}
-	})()
-	go (func() {
-		state.invalidations <- nil
-	})()
+	}()
+
+	state.invalid = 1
 	content.OnInvalidate(func(_ Drawable) {
-		go (func() {
-			state.invalidations <- nil
-		})()
+		atomic.StoreInt32(&state.invalid, 1)
 	})
 	content.Focus(true)
+
 	return &state, nil
 }
 
@@ -74,6 +72,8 @@ func (state *UI) Close() {
 }
 
 func (state *UI) Tick() bool {
+	more := false
+
 	select {
 	case event := <-state.tcEvents:
 		switch event := event.(type) {
@@ -84,21 +84,16 @@ func (state *UI) Tick() bool {
 			state.Content.Invalidate()
 		}
 		state.Content.Event(event)
-	case <-state.invalidations:
-		for {
-			// Flush any other pending invalidations
-			select {
-			case <-state.invalidations:
-				break
-			default:
-				goto done
-			}
-		}
-	done:
+		more = true
+	default:
+	}
+
+	wasInvalid := atomic.SwapInt32(&state.invalid, 0)
+	if wasInvalid != 0 {
 		state.Content.Draw(state.ctx)
 		state.screen.Show()
-	default:
-		return false
+		more = true
 	}
-	return true
+
+	return more
 }