From 33146961f4355fe5d1daad8241936bbcf81022cb Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 27 May 2020 16:12:57 +0200 Subject: [PATCH] app/internal/window: [macOS] avoid racy access to window width, height, scale The macOS redraw callback is not invoked on the main thread, so its access to window fields must be synchronized. An alternative would be to schedule the asynchronous redraws on the main thread, but I believe frame callbacks are performance-sensitive enough to warrant the extra locking complexity. Signed-off-by: Elias Naur --- app/internal/window/os_macos.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/app/internal/window/os_macos.go b/app/internal/window/os_macos.go index d1ffdbf1..fbc8b224 100644 --- a/app/internal/window/os_macos.go +++ b/app/internal/window/os_macos.go @@ -48,9 +48,12 @@ func init() { } type window struct { - view C.CFTypeRef - w Callbacks - stage system.Stage + view C.CFTypeRef + w Callbacks + stage system.Stage + + // mu protect the following fields + mu sync.Mutex scale float32 width, height float32 } @@ -204,8 +207,12 @@ func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy //export gio_onDraw func gio_onDraw(view C.CFTypeRef) { w := mustView(view) - w.scale = float32(C.gio_getViewBackingScale(w.view)) - w.width, w.height = float32(C.gio_viewWidth(w.view)), float32(C.gio_viewHeight(w.view)) + scale := float32(C.gio_getViewBackingScale(w.view)) + width, height := float32(C.gio_viewWidth(w.view)), float32(C.gio_viewHeight(w.view)) + w.mu.Lock() + w.scale = scale + w.width, w.height = width, height + w.mu.Unlock() w.draw(true) } @@ -216,13 +223,15 @@ func gio_onFocus(view C.CFTypeRef, focus C.BOOL) { } func (w *window) draw(sync bool) { - wf, hf := w.width, w.height + w.mu.Lock() + wf, hf, scale := w.width, w.height, w.scale + w.mu.Unlock() if wf == 0 || hf == 0 { return } - width := int(wf*w.scale + .5) - height := int(hf*w.scale + .5) - cfg := configFor(w.scale) + width := int(wf*scale + .5) + height := int(hf*scale + .5) + cfg := configFor(scale) cfg.now = time.Now() w.setStage(system.StageRunning) w.w.Event(FrameEvent{