-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Hold on to Android NativeWindow lock to prevent races #1892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,11 +10,13 @@ use ndk::{ | |||||||||
| configuration::Configuration, | ||||||||||
| event::{InputEvent, KeyAction, MotionAction}, | ||||||||||
| looper::{ForeignLooper, Poll, ThreadLooper}, | ||||||||||
| native_window::NativeWindow, | ||||||||||
| }; | ||||||||||
| use ndk_glue::{Event, Rect}; | ||||||||||
| use raw_window_handle::RawWindowHandle; | ||||||||||
| use std::{ | ||||||||||
| collections::VecDeque, | ||||||||||
| sync::{Arc, Mutex, RwLock}, | ||||||||||
| sync::{Arc, Mutex, RwLock, RwLockReadGuard}, | ||||||||||
| time::{Duration, Instant}, | ||||||||||
| }; | ||||||||||
|
|
||||||||||
|
|
@@ -43,6 +45,12 @@ fn poll(poll: Poll) -> Option<EventSource> { | |||||||||
|
|
||||||||||
| pub struct EventLoop<T: 'static> { | ||||||||||
| window_target: event_loop::EventLoopWindowTarget<T>, | ||||||||||
| /// This read guard will be held between each pair of | ||||||||||
| /// [`event::Event::Resumed`] and [`event::Event::Suspended`] events to | ||||||||||
| /// ensure that [`ndk_glue`] does not release the [`NativeWindow`] | ||||||||||
| /// prematurely, invalidating raw handles obtained by the user through | ||||||||||
| /// [`Window::raw_window_handle()`]. | ||||||||||
| native_window_lock: Option<RwLockReadGuard<'static, Option<NativeWindow>>>, | ||||||||||
| user_queue: Arc<Mutex<VecDeque<T>>>, | ||||||||||
| first_event: Option<EventSource>, | ||||||||||
| start_cause: event::StartCause, | ||||||||||
|
|
@@ -65,10 +73,12 @@ impl<T: 'static> EventLoop<T> { | |||||||||
| Self { | ||||||||||
| window_target: event_loop::EventLoopWindowTarget { | ||||||||||
| p: EventLoopWindowTarget { | ||||||||||
| raw_window_handle: Default::default(), | ||||||||||
| _marker: std::marker::PhantomData, | ||||||||||
| }, | ||||||||||
| _marker: std::marker::PhantomData, | ||||||||||
| }, | ||||||||||
| native_window_lock: None, | ||||||||||
| user_queue: Default::default(), | ||||||||||
| first_event: None, | ||||||||||
| start_cause: event::StartCause::Init, | ||||||||||
|
|
@@ -106,22 +116,36 @@ impl<T: 'static> EventLoop<T> { | |||||||||
| match self.first_event.take() { | ||||||||||
| Some(EventSource::Callback) => match ndk_glue::poll_events().unwrap() { | ||||||||||
| Event::WindowCreated => { | ||||||||||
| call_event_handler!( | ||||||||||
| event_handler, | ||||||||||
| self.window_target(), | ||||||||||
| control_flow, | ||||||||||
| event::Event::Resumed | ||||||||||
| ); | ||||||||||
| let native_window_lock = ndk_glue::native_window(); | ||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two calls remaining to That might require some refactoring as
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried something like that first. My attempt ran into
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I came up with something. Let me know what you think.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have preferred to keep the entire thing in one place without two mutexes/locks. It looks like other backends like Wayland store modifyable data in winit/src/platform_impl/linux/wayland/event_loop/mod.rs Lines 50 to 51 in 86748fb
That's where I'd put Maybe the winit-Android developers have a better overview of where to place (mutable) data shared between the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer that too, but I assumed that
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Desktop platforms seem to make sure
winit/src/platform_impl/linux/x11/window.rs Line 105 in 86748fb
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MarijnS95 Still no idea how to solve this without extra synchronization. Some input from a maintainer might be useful.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately I have not had the time to look at any alternatives either, seems like wrapping our state (either the readlock or raw window handle) inside yet another mutex is the only option - given It would have also been nice if the lock could be transposed to remove the |
||||||||||
| // The window could have gone away before we got the message | ||||||||||
| if native_window_lock.is_some() { | ||||||||||
| self.window_target | ||||||||||
| .p | ||||||||||
| .update_native_window(native_window_lock.as_ref()); | ||||||||||
| self.native_window_lock = Some(native_window_lock); | ||||||||||
| call_event_handler!( | ||||||||||
| event_handler, | ||||||||||
| self.window_target(), | ||||||||||
| control_flow, | ||||||||||
| event::Event::Resumed | ||||||||||
| ); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| Event::WindowResized => resized = true, | ||||||||||
| Event::WindowRedrawNeeded => redraw = true, | ||||||||||
| Event::WindowDestroyed => { | ||||||||||
| call_event_handler!( | ||||||||||
| event_handler, | ||||||||||
| self.window_target(), | ||||||||||
| control_flow, | ||||||||||
| event::Event::Suspended | ||||||||||
| ); | ||||||||||
| let native_window_lock = self.native_window_lock.take(); | ||||||||||
| // This event is ignored if no window was actually grabbed onto | ||||||||||
| // in `WindowCreated` above | ||||||||||
| if native_window_lock.is_some() { | ||||||||||
| call_event_handler!( | ||||||||||
| event_handler, | ||||||||||
| self.window_target(), | ||||||||||
| control_flow, | ||||||||||
| event::Event::Suspended | ||||||||||
| ); | ||||||||||
| } | ||||||||||
| self.window_target.p.update_native_window(None); | ||||||||||
| } | ||||||||||
| Event::Pause => self.running = false, | ||||||||||
| Event::Resume => self.running = true, | ||||||||||
|
|
@@ -400,10 +424,19 @@ impl<T> Clone for EventLoopProxy<T> { | |||||||||
| } | ||||||||||
|
|
||||||||||
| pub struct EventLoopWindowTarget<T: 'static> { | ||||||||||
| raw_window_handle: Arc<Mutex<Option<RawWindowHandle>>>, | ||||||||||
| _marker: std::marker::PhantomData<T>, | ||||||||||
| } | ||||||||||
|
|
||||||||||
| impl<T: 'static> EventLoopWindowTarget<T> { | ||||||||||
| fn update_native_window(&self, native_window: Option<&NativeWindow>) { | ||||||||||
| *self.raw_window_handle.lock().unwrap() = native_window.map(|native_window| { | ||||||||||
| let mut handle = raw_window_handle::android::AndroidHandle::empty(); | ||||||||||
| handle.a_native_window = unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ }; | ||||||||||
| RawWindowHandle::Android(handle) | ||||||||||
| }); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| pub fn primary_monitor(&self) -> Option<monitor::MonitorHandle> { | ||||||||||
| Some(monitor::MonitorHandle { | ||||||||||
| inner: MonitorHandle, | ||||||||||
|
|
@@ -438,16 +471,20 @@ impl DeviceId { | |||||||||
| #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] | ||||||||||
| pub struct PlatformSpecificWindowBuilderAttributes; | ||||||||||
|
|
||||||||||
| pub struct Window; | ||||||||||
| pub struct Window { | ||||||||||
| raw_window_handle: Arc<Mutex<Option<RawWindowHandle>>>, | ||||||||||
| } | ||||||||||
|
|
||||||||||
| impl Window { | ||||||||||
| pub fn new<T: 'static>( | ||||||||||
| _el: &EventLoopWindowTarget<T>, | ||||||||||
| el: &EventLoopWindowTarget<T>, | ||||||||||
| _window_attrs: window::WindowAttributes, | ||||||||||
| _: PlatformSpecificWindowBuilderAttributes, | ||||||||||
| ) -> Result<Self, error::OsError> { | ||||||||||
| // FIXME this ignores requested window attributes | ||||||||||
| Ok(Self) | ||||||||||
| Ok(Self { | ||||||||||
| raw_window_handle: Arc::clone(&el.raw_window_handle), | ||||||||||
| }) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| pub fn id(&self) -> WindowId { | ||||||||||
|
|
@@ -563,14 +600,14 @@ impl Window { | |||||||||
| } | ||||||||||
|
|
||||||||||
| pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { | ||||||||||
| let a_native_window = if let Some(native_window) = ndk_glue::native_window().as_ref() { | ||||||||||
| unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ } | ||||||||||
| } else { | ||||||||||
| panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events."); | ||||||||||
| }; | ||||||||||
| let mut handle = raw_window_handle::android::AndroidHandle::empty(); | ||||||||||
| handle.a_native_window = a_native_window; | ||||||||||
| raw_window_handle::RawWindowHandle::Android(handle) | ||||||||||
| self.raw_window_handle | ||||||||||
| .lock() | ||||||||||
| .unwrap() | ||||||||||
| .as_ref() | ||||||||||
| .expect( | ||||||||||
| "The window can be obtained only between `Event::Resumed` and `Event::Suspended`!", | ||||||||||
| ) | ||||||||||
| .clone() | ||||||||||
| } | ||||||||||
|
|
||||||||||
| pub fn config(&self) -> Configuration { | ||||||||||
|
|
||||||||||
Uh oh!
There was an error while loading. Please reload this page.