From 43116400d041787be246d9f73f84297815994e97 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 23 Jun 2022 14:21:44 +0200 Subject: [PATCH] app: fix racing app.Window.Perform and app.Window.Option Signed-off-by: Elias Naur --- app/window.go | 68 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/app/window.go b/app/window.go index 9e480129..641e7c01 100644 --- a/app/window.go +++ b/app/window.go @@ -54,17 +54,16 @@ type Window struct { immediateRedraws chan struct{} // scheduledRedraws is sent the most recent delayed redraw time. scheduledRedraws chan time.Time + // options are the options waiting to be applied. + options chan []Option + // actions are the actions waiting to be performed. + actions chan system.Action out chan event.Event frames chan *op.Ops frameAck chan struct{} - closing bool // dead is closed when the window is destroyed. dead chan struct{} - // options are the options waiting to be applied. - options []Option - // actions are the actions waiting to be performed. - actions system.Action stage system.Stage animating bool @@ -154,6 +153,8 @@ func NewWindow(options ...Option) *Window { wakeups: make(chan struct{}, 1), wakeupFuncs: make(chan func()), dead: make(chan struct{}), + options: make(chan []Option, 1), + actions: make(chan system.Action, 1), nocontext: cnf.CustomRenderer, } w.imeState.compose = key.Range{Start: -1, End: -1} @@ -333,8 +334,18 @@ func (w *Window) Invalidate() { // Option applies the options to the window. func (w *Window) Option(opts ...Option) { - w.options = append(w.options, opts...) - w.wakeup() + if len(opts) == 0 { + return + } + for { + select { + case old := <-w.options: + opts = append(old, opts...) + case w.options <- opts: + w.wakeup() + return + } + } } // ReadClipboard initiates a read of the clipboard in the form @@ -445,19 +456,24 @@ func (c *callbacks) Event(e event.Event) bool { c.waitEvents = c.waitEvents[:len(c.waitEvents)-1] handled = c.w.processEvent(c.d, e) } + closing := false if _, iswakeup := e.(wakeupEvent); iswakeup { - if len(c.w.options) > 0 { - c.d.Configure(c.w.options) - c.w.options = nil + select { + case opts := <-c.w.options: + c.d.Configure(opts) + default: } - if c.w.actions != 0 { - c.d.Perform(c.w.actions) - c.w.actions = 0 + select { + case acts := <-c.w.actions: + // Pospone closing to last. + closing = (acts & system.ActionClose) != 0 + acts &^= system.ActionClose + c.d.Perform(acts) + default: } } c.w.updateState(c.d) - if c.w.closing { - c.w.closing = false + if closing { c.d.Close() } return handled @@ -1008,20 +1024,28 @@ func (w *Window) Perform(actions system.Action) { walkActions(actions, func(action system.Action) { switch action { case system.ActionMinimize: - w.options = append(w.options, Minimized.Option()) + w.Option(Minimized.Option()) case system.ActionMaximize: - w.options = append(w.options, Maximized.Option()) + w.Option(Maximized.Option()) case system.ActionUnmaximize: - w.options = append(w.options, Windowed.Option()) - case system.ActionClose: - w.closing = true + w.Option(Windowed.Option()) default: return } actions &^= action }) - w.actions = w.actions | actions - w.wakeup() + if actions == 0 { + return + } + for { + select { + case old := <-w.actions: + actions |= old + case w.actions <- actions: + w.wakeup() + return + } + } } func (q *queue) Events(k event.Tag) []event.Event {