app: ensure no window wakeups are in flight when destroying it

When a window is destroyed, it is no longer valid to call its wakeup
method.

Thanks to Jack Mordaunt for identifying the race.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-07-18 19:02:07 +02:00
parent e711cbc004
commit 63d2353864
+7 -4
View File
@@ -62,6 +62,7 @@ type Window struct {
out chan event.Event out chan event.Event
frames chan *op.Ops frames chan *op.Ops
frameAck chan struct{} frameAck chan struct{}
destroy chan struct{}
// dead is closed when the window is destroyed. // dead is closed when the window is destroyed.
dead chan struct{} dead chan struct{}
@@ -155,6 +156,7 @@ func NewWindow(options ...Option) *Window {
driverFuncs: make(chan func(d driver), 1), driverFuncs: make(chan func(d driver), 1),
wakeups: make(chan struct{}, 1), wakeups: make(chan struct{}, 1),
wakeupFuncs: make(chan func()), wakeupFuncs: make(chan func()),
destroy: make(chan struct{}),
dead: make(chan struct{}), dead: make(chan struct{}),
options: make(chan []Option, 1), options: make(chan []Option, 1),
actions: make(chan system.Action, 1), actions: make(chan system.Action, 1),
@@ -860,8 +862,8 @@ func (w *Window) processEvent(d driver, e event.Event) bool {
if err := w.validateAndProcess(d, viewSize, e2.Sync, wrapper, signal); err != nil { if err := w.validateAndProcess(d, viewSize, e2.Sync, wrapper, signal); err != nil {
w.destroyGPU() w.destroyGPU()
w.out <- system.DestroyEvent{Err: err} w.out <- system.DestroyEvent{Err: err}
close(w.dead)
close(w.out) close(w.out)
w.destroy <- struct{}{}
break break
} }
w.processFrame(d, frameStart) w.processFrame(d, frameStart)
@@ -869,8 +871,8 @@ func (w *Window) processEvent(d driver, e event.Event) bool {
case system.DestroyEvent: case system.DestroyEvent:
w.destroyGPU() w.destroyGPU()
w.out <- e2 w.out <- e2
close(w.dead)
close(w.out) close(w.out)
w.destroy <- struct{}{}
case ViewEvent: case ViewEvent:
w.out <- e2 w.out <- e2
w.waitAck(d) w.waitAck(d)
@@ -917,8 +919,8 @@ func (w *Window) processEvent(d driver, e event.Event) bool {
func (w *Window) run(options []Option) { func (w *Window) run(options []Option) {
if err := newWindow(&w.callbacks, options); err != nil { if err := newWindow(&w.callbacks, options); err != nil {
w.out <- system.DestroyEvent{Err: err} w.out <- system.DestroyEvent{Err: err}
close(w.dead)
close(w.out) close(w.out)
w.destroy <- struct{}{}
return return
} }
var wakeup func() var wakeup func()
@@ -940,7 +942,8 @@ func (w *Window) run(options []Option) {
timer.Stop() timer.Stop()
} }
timer = time.NewTimer(time.Until(t)) timer = time.NewTimer(time.Until(t))
case <-w.dead: case <-w.destroy:
close(w.dead)
return return
case <-timeC: case <-timeC:
select { select {