diff --git a/CHANGELOG.md b/CHANGELOG.md index 399df9e5..57e12e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Added NetBSD support. - **Breaking:** On iOS, `UIView` is now the default root view. `WindowBuilderExt::with_root_view_class` can be used to set the root view objective-c class to `GLKView` (OpenGLES) or `MTKView` (Metal/MoltenVK). - On iOS, the `UIApplication` is not started until `Window::new` is called. +- Fixed thread unsafety with cursor hiding on macOS. # Version 0.16.2 (2018-07-07) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index a210bfcf..8a307a93 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -1,8 +1,9 @@ use std; +use std::cell::{Cell, RefCell}; use std::ops::Deref; use std::os::raw::c_void; use std::sync::Weak; -use std::cell::{Cell, RefCell}; +use std::sync::atomic::{Ordering, AtomicBool}; use cocoa; use cocoa::appkit::{ @@ -45,6 +46,7 @@ use window::MonitorId as RootMonitorId; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Id(pub usize); +// TODO: It's possible for delegate methods to be called asynchronously, causing data races / `RefCell` panics. pub struct DelegateState { view: IdRef, window: IdRef, @@ -526,7 +528,7 @@ pub struct Window2 { pub window: IdRef, pub delegate: WindowDelegate, pub input_context: IdRef, - cursor_hidden: Cell, + cursor_hidden: AtomicBool, } unsafe impl Send for Window2 {} @@ -1007,13 +1009,13 @@ impl Window2 { let cursor_class = class!(NSCursor); // macOS uses a "hide counter" like Windows does, so we avoid incrementing it more than once. // (otherwise, `hide_cursor(false)` would need to be called n times!) - if hide != self.cursor_hidden.get() { + if hide != self.cursor_hidden.load(Ordering::Acquire) { if hide { let _: () = unsafe { msg_send![cursor_class, hide] }; } else { let _: () = unsafe { msg_send![cursor_class, unhide] }; } - self.cursor_hidden.replace(hide); + self.cursor_hidden.store(hide, Ordering::Release); } }