mirror of
https://git.sr.ht/~sircmpwn/aerc
synced 2025-12-28 01:51:44 +01:00
Many Drawable implementations have their own Invalidate and OnInvalidate
functions, with an unexported onInvalidate field. However OnInvalidate and
Invalidate are usually not called in the same goroutine. This results in a race
on this field, e.g.:
Read at 0x00c000094748 by goroutine 7:
git.sr.ht/~sircmpwn/aerc2/widgets.NewDirectoryList.func1()
/home/simon/src/aerc2/widgets/dirlist.go:85 +0x56
git.sr.ht/~sircmpwn/aerc2/widgets.(*Spinner).Start.func1()
/home/simon/src/aerc2/widgets/spinner.go:93 +0x1bb
Previous write at 0x00c000094748 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:46 +0x8f
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
To fix this, introduce a new type, Invalidatable, which protects the field.
Unfortunately the Drawable must be passed to the callback function in
Invalidate, so we still need to re-implement this in each Invalidatable user.
25 lines
344 B
Go
25 lines
344 B
Go
package ui
|
|
|
|
import (
|
|
"sync/atomic"
|
|
)
|
|
|
|
type Invalidatable struct {
|
|
onInvalidate atomic.Value
|
|
}
|
|
|
|
func (i *Invalidatable) OnInvalidate(f func(d Drawable)) {
|
|
i.onInvalidate.Store(f)
|
|
}
|
|
|
|
func (i *Invalidatable) DoInvalidate(d Drawable) {
|
|
v := i.onInvalidate.Load()
|
|
if v == nil {
|
|
return
|
|
}
|
|
f := v.(func(d Drawable))
|
|
if f != nil {
|
|
f(d)
|
|
}
|
|
}
|