Skip to content

Commit

Permalink
Make Active a no-ip on all platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
notgull committed Apr 4, 2023
1 parent 4abe033 commit 1be0e38
Showing 1 changed file with 28 additions and 181 deletions.
209 changes: 28 additions & 181 deletions src/borrowed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
//!
//! These should be 100% safe to pass around and use, no possibility of dangling or invalidity.
#[cfg(all(not(feature = "std"), target_os = "android"))]
compile_error!("Using borrowed handles on Android requires the `std` feature to be enabled.");

use core::cell::UnsafeCell;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
Expand All @@ -24,43 +22,11 @@ use crate::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindow
///
/// ## Explanation
///
/// On Android, there is an [Activity]-global [`ANativeWindow`] object that is used for drawing. This
/// handle is used [within the `RawWindowHandle` type] for Android NDK, since it is necessary for GFX
/// APIs to draw to the screen.
///
/// However, the [`ANativeWindow`] type can be arbitrarily invalidated by the underlying Android runtime.
/// The reasoning for this is complicated, but this idea is exposed to native code through the
/// [`onNativeWindowCreated`] and [`onNativeWindowDestroyed`] callbacks. To save you a click, the
/// conditions associated with these callbacks are:
///
/// - [`onNativeWindowCreated`] provides a valid [`ANativeWindow`] pointer that can be used for drawing.
/// - [`onNativeWindowDestroyed`] indicates that the previous [`ANativeWindow`] pointer is no longer
/// valid. The documentation clarifies that, *once the function returns*, the [`ANativeWindow`] pointer
/// can no longer be used for drawing without resulting in undefined behavior.
///
/// In [`winit`], these are exposed via the [`Resumed`] and [`Suspended`] events, respectively. Therefore,
/// between the last [`Suspended`] event and the next [`Resumed`] event, it is undefined behavior to use
/// the raw window handle. This condition makes it tricky to define an API that safely wraps the raw
/// window handles, since an existing window handle can be made invalid at any time.
///
/// The Android docs specifies that the [`ANativeWindow`] pointer is still valid while the application
/// is still in the [`onNativeWindowDestroyed`] block, and suggests that synchronization needs to take
/// place to ensure that the pointer has been invalidated before the function returns. `Active` aims
/// to be the solution to this problem. It keeps track of all currently active window handles, and
/// blocks until all of them are dropped before allowing the application to enter the suspended state.
///
/// [Activity]: https://developer.android.com/reference/android/app/Activity
/// [`ANativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [within the `RawWindowHandle` type]: struct.AndroidNdkWindowHandle.html#structfield.a_native_window
/// [`onNativeWindowCreated`]: https://developer.android.com/ndk/reference/struct/a-native-activity-callbacks#onnativewindowcreated
/// [`onNativeWindowDestroyed`]: https://developer.android.com/ndk/reference/struct/a-native-activity-callbacks#onnativewindowdestroyed
/// [`Resumed`]: https://docs.rs/winit/latest/winit/event/enum.Event.html#variant.Resumed
/// [`Suspended`]: https://docs.rs/winit/latest/winit/event/enum.Event.html#variant.Suspended
/// [`sdl2`]: https://crates.io/crates/sdl2
/// [`RawWindowHandle`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/enum.RawWindowHandle.html
/// [`HasRawWindowHandle`]: https://docs.rs/raw-window-handle/latest/raw_window_handle/trait.HasRawWindowHandle.html
/// [`winit`]: https://crates.io/crates/winit
pub struct Active(imp::Active);
/// On Android, it was previously believed that the application could enter the suspended state
/// and immediately invalidate all window handles. However, it was later discovered that the
/// handle actually remains valid, but the window does not produce any more GPU buffers. This
/// type is a no-op and will be removed at the next major release.
pub struct Active(());

impl fmt::Debug for Active {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -77,7 +43,7 @@ impl fmt::Debug for Active {
/// On non-Android platforms, this is a ZST. On Android, this is a reference counted handle that
/// keeps the application active while it is alive.
#[derive(Clone)]
pub struct ActiveHandle<'a>(imp::ActiveHandle<'a>);
pub struct ActiveHandle<'a>(PhantomData<&'a UnsafeCell<()>>);

impl<'a> fmt::Debug for ActiveHandle<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -97,7 +63,7 @@ impl Active {
/// let active = Active::new();
/// ```
pub const fn new() -> Self {
Self(imp::Active::new())
Self(())
}

/// Get a live window handle.
Expand All @@ -121,7 +87,7 @@ impl Active {
/// active.set_inactive();
/// ```
pub fn handle(&self) -> Option<ActiveHandle<'_>> {
self.0.handle().map(ActiveHandle)
Some(ActiveHandle(PhantomData))
}

/// Set the application to be inactive.
Expand All @@ -140,9 +106,7 @@ impl Active {
/// // Set the application to be inactive.
/// active.set_inactive();
/// ```
pub fn set_inactive(&self) {
self.0.set_inactive()
}
pub fn set_inactive(&self) {}

/// Set the application to be active.
///
Expand All @@ -163,9 +127,7 @@ impl Active {
/// // Set the application to be inactive.
/// active.set_inactive();
/// ```
pub unsafe fn set_active(&self) {
self.0.set_active()
}
pub unsafe fn set_active(&self) {}
}

impl ActiveHandle<'_> {
Expand All @@ -190,7 +152,23 @@ impl ActiveHandle<'_> {
/// let handle = unsafe { ActiveHandle::new_unchecked() };
/// ```
pub unsafe fn new_unchecked() -> Self {
Self(imp::ActiveHandle::new_unchecked())
Self(PhantomData)
}

/// Create a new `ActiveHandle`.
///
/// This is safe because the handle is always active.
///
/// # Example
///
/// ```
/// use raw_window_handle::ActiveHandle;
/// let handle = ActiveHandle::new();
/// ```
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
// SAFETY: The handle is always active.
unsafe { super::ActiveHandle::new_unchecked() }
}
}

Expand Down Expand Up @@ -482,134 +460,3 @@ impl std::error::Error for HandleError {}
/// _assert::<WindowHandle<'static>>();
/// ```
fn _not_send_or_sync() {}

#[cfg(not(any(target_os = "android", raw_window_handle_force_refcount)))]
#[cfg_attr(docsrs, doc(cfg(not(target_os = "android"))))]
mod imp {
//! We don't need to refcount the handles, so we can just use no-ops.
use core::cell::UnsafeCell;
use core::marker::PhantomData;

pub(super) struct Active;

#[derive(Clone)]
pub(super) struct ActiveHandle<'a> {
_marker: PhantomData<&'a UnsafeCell<()>>,
}

impl Active {
pub(super) const fn new() -> Self {
Self
}

pub(super) fn handle(&self) -> Option<ActiveHandle<'_>> {
// SAFETY: The handle is always active.
Some(unsafe { ActiveHandle::new_unchecked() })
}

pub(super) unsafe fn set_active(&self) {}

pub(super) fn set_inactive(&self) {}
}

impl ActiveHandle<'_> {
pub(super) unsafe fn new_unchecked() -> Self {
Self {
_marker: PhantomData,
}
}
}

impl Drop for ActiveHandle<'_> {
fn drop(&mut self) {
// Done for consistency with the refcounted version.
}
}

impl super::ActiveHandle<'_> {
/// Create a new `ActiveHandle`.
///
/// This is safe because the handle is always active.
///
/// # Example
///
/// ```
/// use raw_window_handle::ActiveHandle;
/// let handle = ActiveHandle::new();
/// ```
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
// SAFETY: The handle is always active.
unsafe { super::ActiveHandle::new_unchecked() }
}
}
}

#[cfg(any(target_os = "android", raw_window_handle_force_refcount))]
#[cfg_attr(docsrs, doc(cfg(any(target_os = "android"))))]
mod imp {
//! We need to refcount the handles, so we use an `RwLock` to do so.
use std::sync::{RwLock, RwLockReadGuard};

pub(super) struct Active {
active: RwLock<bool>,
}

pub(super) struct ActiveHandle<'a> {
inner: Option<Inner<'a>>,
}

struct Inner<'a> {
_read_guard: RwLockReadGuard<'a, bool>,
active: &'a Active,
}

impl Clone for ActiveHandle<'_> {
fn clone(&self) -> Self {
Self {
inner: self.inner.as_ref().map(|inner| Inner {
_read_guard: inner.active.active.read().unwrap(),
active: inner.active,
}),
}
}
}

impl Active {
pub(super) const fn new() -> Self {
Self {
active: RwLock::new(false),
}
}

pub(super) fn handle(&self) -> Option<ActiveHandle<'_>> {
let active = self.active.read().ok()?;
if !*active {
return None;
}

Some(ActiveHandle {
inner: Some(Inner {
_read_guard: active,
active: self,
}),
})
}

pub(super) unsafe fn set_active(&self) {
*self.active.write().unwrap() = true;
}

pub(super) fn set_inactive(&self) {
*self.active.write().unwrap() = false;
}
}

impl ActiveHandle<'_> {
pub(super) unsafe fn new_unchecked() -> Self {
Self { inner: None }
}
}
}

0 comments on commit 1be0e38

Please sign in to comment.