about summary refs log tree commit diff stats
path: root/widgets
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2019-04-27 15:09:59 +0000
committerDrew DeVault <sir@cmpwn.com>2019-04-27 11:42:12 -0400
commit2159eb876e7e04e81f65e64b1d742ad832890289 (patch)
tree672eabb782e3a0647a6607cd0c973834021747fc /widgets
parente72574c308c04cb30af95f5c88983e0cab798fea (diff)
downloadaerc-2159eb876e7e04e81f65e64b1d742ad832890289.tar.gz
widgets/spinner: fix Spinner.frame race
It's accessed by the goroutine which increments it and the goroutine that draws
the widget at the same time. Use atomic instead.

    Write at 0x00c00000ebc0 by goroutine 7:
      git.sr.ht/~sircmpwn/aerc2/widgets.(*Spinner).Start.func1()
          /home/simon/src/aerc2/widgets/spinner.go:50 +0x169

    Previous read at 0x00c00000ebc0 by main goroutine:
      [failed to restore the stack]

    Goroutine 7 (running) created at:
      git.sr.ht/~sircmpwn/aerc2/widgets.(*Spinner).Start()
          /home/simon/src/aerc2/widgets/spinner.go:44 +0x8b
      git.sr.ht/~sircmpwn/aerc2/widgets.NewDirectoryList()
          /home/simon/src/aerc2/widgets/dirlist.go:37 +0x286
      git.sr.ht/~sircmpwn/aerc2/widgets.NewAccountView()
          /home/simon/src/aerc2/widgets/account.go:50 +0x5ca
      git.sr.ht/~sircmpwn/aerc2/widgets.NewAerc()
          /home/simon/src/aerc2/widgets/aerc.go:60 +0x800
      main.main()
          /home/simon/src/aerc2/aerc.go:65 +0x33e
Diffstat (limited to 'widgets')
-rw-r--r--widgets/spinner.go27
1 files changed, 15 insertions, 12 deletions
diff --git a/widgets/spinner.go b/widgets/spinner.go
index bafc712..0ab3e13 100644
--- a/widgets/spinner.go
+++ b/widgets/spinner.go
@@ -1,6 +1,7 @@
 package widgets
 
 import (
+	"sync/atomic"
 	"time"
 
 	"github.com/gdamore/tcell"
@@ -22,14 +23,14 @@ var (
 )
 
 type Spinner struct {
-	frame        int
+	frame        int64 // access via atomic
 	onInvalidate func(d ui.Drawable)
-	stop         chan interface{}
+	stop         chan struct{}
 }
 
 func NewSpinner() *Spinner {
 	spinner := Spinner{
-		stop:  make(chan interface{}),
+		stop:  make(chan struct{}),
 		frame: -1,
 	}
 	return &spinner
@@ -40,17 +41,17 @@ func (s *Spinner) Start() {
 		return
 	}
 
-	s.frame = 0
+	atomic.StoreInt64(&s.frame, 0)
+
 	go func() {
 		for {
 			select {
 			case <-s.stop:
+				atomic.StoreInt64(&s.frame, -1)
+				s.stop <- struct{}{}
 				return
 			case <-time.After(200 * time.Millisecond):
-				s.frame++
-				if s.frame >= len(frames) {
-					s.frame = 0
-				}
+				atomic.AddInt64(&s.frame, 1)
 				s.Invalidate()
 			}
 		}
@@ -62,13 +63,13 @@ func (s *Spinner) Stop() {
 		return
 	}
 
-	s.stop <- nil
-	s.frame = -1
+	s.stop <- struct{}{}
+	<-s.stop
 	s.Invalidate()
 }
 
 func (s *Spinner) IsRunning() bool {
-	return s.frame != -1
+	return atomic.LoadInt64(&s.frame) != -1
 }
 
 func (s *Spinner) Draw(ctx *ui.Context) {
@@ -76,9 +77,11 @@ func (s *Spinner) Draw(ctx *ui.Context) {
 		s.Start()
 	}
 
+	cur := int(atomic.LoadInt64(&s.frame) % int64(len(frames)))
+
 	ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
 	col := ctx.Width()/2 - len(frames[0])/2 + 1
-	ctx.Printf(col, 0, tcell.StyleDefault, "%s", frames[s.frame])
+	ctx.Printf(col, 0, tcell.StyleDefault, "%s", frames[cur])
 }
 
 func (s *Spinner) OnInvalidate(onInvalidate func(d ui.Drawable)) {