Skip to content

Commit a0c5e99

Browse files
authored
ndk-glue: Switch to parking_lot for mappable and Sendable lock guards (#282)
It has come up previously that the `native_window()` function is easier to use if we could transpose a `LockGuard<Option<>>` into an `Option<LockGuard<>>`. After all, as soon as you have the lock you only need to check the `Option<>` value once and are thereafter guaranteed that its value won't change, until the lock is given up. At the same time `parking_lot` has a `send_guard` feature which allows moving a lock guard to another thread (as long as `deadlock_detection` is disabled); a use case for this recently came up [in glutin] where the `NativeWindow` handle lock should be stored with a GL context so that our `fn on_window_destroyed()` callback doesn't return until after the `Surface` created on it is removed from the context and destroyed. Glutin forces this context to be `Send`, and a lock guard already allows `Sync` if `NativeWindow` is `Sync` (which it is). [in glutin]: rust-windowing/glutin#1411 (comment)
1 parent 8526787 commit a0c5e99

File tree

3 files changed

+30
-15
lines changed

3 files changed

+30
-15
lines changed

ndk-glue/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unreleased
22

33
- **Breaking:** Provide a `LockReadGuard` newtype around `NativeWindow`/`InputQueue` to hide the underlying lock implementation. (#288)
4+
- **Breaking:** Transpose `LockReadGuard<Option<T>>` into `Option<LockReadGuard<T>>` to only necessitate an `Option` unpack/`unwrap()` once. (#282)
45

56
# 0.6.2 (2022-04-19)
67

ndk-glue/Cargo.toml

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ homepage = "https://github.com/rust-windowing/android-ndk-rs"
1212
repository = "https://github.com/rust-windowing/android-ndk-rs"
1313

1414
[dependencies]
15+
android_logger = { version = "0.11", optional = true }
16+
libc = "0.2.84"
17+
log = "0.4.14"
1518
ndk = { path = "../ndk", version = "0.6.0" }
1619
ndk-context = { path = "../ndk-context", version = "0.1.1" }
1720
ndk-macro = { path = "../ndk-macro", version = "0.3.0" }
1821
ndk-sys = { path = "../ndk-sys", version = "0.3.0" }
1922
once_cell = "1"
20-
libc = "0.2.84"
21-
log = "0.4.14"
22-
android_logger = { version = "0.11", optional = true }
23+
parking_lot = "0.12"
2324

2425
[features]
2526
default = []

ndk-glue/src/lib.rs

+25-12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use ndk::native_activity::NativeActivity;
77
use ndk::native_window::NativeWindow;
88
use ndk_sys::{AInputQueue, ANativeActivity, ANativeWindow, ARect};
99
use once_cell::sync::Lazy;
10+
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
1011
use std::ffi::{CStr, CString};
1112
use std::fmt;
1213
use std::fs::File;
@@ -15,7 +16,7 @@ use std::ops::Deref;
1516
use std::os::raw;
1617
use std::os::unix::prelude::*;
1718
use std::ptr::NonNull;
18-
use std::sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard};
19+
use std::sync::{Arc, Condvar, Mutex};
1920
use std::thread;
2021

2122
#[cfg(feature = "logger")]
@@ -67,7 +68,20 @@ pub fn native_activity() -> &'static NativeActivity {
6768
unsafe { NATIVE_ACTIVITY.as_ref().unwrap() }
6869
}
6970

70-
pub struct LockReadGuard<T: ?Sized + 'static>(RwLockReadGuard<'static, T>);
71+
pub struct LockReadGuard<T: ?Sized + 'static>(MappedRwLockReadGuard<'static, T>);
72+
73+
impl<T> LockReadGuard<T> {
74+
/// Transpose an [`Option`] wrapped inside a [`LockReadGuard`]
75+
///
76+
/// This is a _read_ lock for which the contents can't change; hence allowing the user to only
77+
/// check for [`None`] once and hold a lock containing `T` directly thereafter, without
78+
/// subsequent infallible [`Option::unwrap()`]s.
79+
fn from_wrapped_option(wrapped: RwLockReadGuard<'static, Option<T>>) -> Option<Self> {
80+
RwLockReadGuard::try_map(wrapped, Option::as_ref)
81+
.ok()
82+
.map(Self)
83+
}
84+
}
7185

7286
impl<T: ?Sized> Deref for LockReadGuard<T> {
7387
type Target = T;
@@ -102,8 +116,8 @@ impl<T: ?Sized + fmt::Display> fmt::Display for LockReadGuard<T> {
102116
/// # Warning
103117
/// This function accesses a `static` variable internally and must only be used if you are sure
104118
/// there is exactly one version of `ndk_glue` in your dependency tree.
105-
pub fn native_window() -> LockReadGuard<Option<NativeWindow>> {
106-
LockReadGuard(NATIVE_WINDOW.read().unwrap())
119+
pub fn native_window() -> Option<LockReadGuard<NativeWindow>> {
120+
LockReadGuard::from_wrapped_option(NATIVE_WINDOW.read())
107121
}
108122

109123
/// Returns an [`InputQueue`] held inside a lock, preventing Android from freeing it immediately
@@ -117,14 +131,14 @@ pub fn native_window() -> LockReadGuard<Option<NativeWindow>> {
117131
/// # Warning
118132
/// This function accesses a `static` variable internally and must only be used if you are sure
119133
/// there is exactly one version of `ndk_glue` in your dependency tree.
120-
pub fn input_queue() -> LockReadGuard<Option<InputQueue>> {
121-
LockReadGuard(INPUT_QUEUE.read().unwrap())
134+
pub fn input_queue() -> Option<LockReadGuard<InputQueue>> {
135+
LockReadGuard::from_wrapped_option(INPUT_QUEUE.read())
122136
}
123137

124138
/// This function accesses a `static` variable internally and must only be used if you are sure
125139
/// there is exactly one version of `ndk_glue` in your dependency tree.
126140
pub fn content_rect() -> Rect {
127-
CONTENT_RECT.read().unwrap().clone()
141+
CONTENT_RECT.read().clone()
128142
}
129143

130144
static PIPE: Lazy<[RawFd; 2]> = Lazy::new(|| {
@@ -345,7 +359,6 @@ unsafe extern "C" fn on_window_focus_changed(
345359
unsafe extern "C" fn on_window_created(activity: *mut ANativeActivity, window: *mut ANativeWindow) {
346360
NATIVE_WINDOW
347361
.write()
348-
.unwrap()
349362
.replace(NativeWindow::clone_from_ptr(NonNull::new(window).unwrap()));
350363
wake(activity, Event::WindowCreated);
351364
}
@@ -369,7 +382,7 @@ unsafe extern "C" fn on_window_destroyed(
369382
window: *mut ANativeWindow,
370383
) {
371384
wake(activity, Event::WindowDestroyed);
372-
let mut native_window_guard = NATIVE_WINDOW.write().unwrap();
385+
let mut native_window_guard = NATIVE_WINDOW.write();
373386
assert_eq!(native_window_guard.as_ref().unwrap().ptr().as_ptr(), window);
374387
native_window_guard.take();
375388
}
@@ -384,7 +397,7 @@ unsafe extern "C" fn on_input_queue_created(
384397
// future code cleans it up and sets it back to `None` again.
385398
let looper = locked_looper.as_ref().expect("Looper does not exist");
386399
input_queue.attach_looper(looper, NDK_GLUE_LOOPER_INPUT_QUEUE_IDENT);
387-
INPUT_QUEUE.write().unwrap().replace(input_queue);
400+
INPUT_QUEUE.write().replace(input_queue);
388401
wake(activity, Event::InputQueueCreated);
389402
}
390403

@@ -393,7 +406,7 @@ unsafe extern "C" fn on_input_queue_destroyed(
393406
queue: *mut AInputQueue,
394407
) {
395408
wake(activity, Event::InputQueueDestroyed);
396-
let mut input_queue_guard = INPUT_QUEUE.write().unwrap();
409+
let mut input_queue_guard = INPUT_QUEUE.write();
397410
assert_eq!(input_queue_guard.as_ref().unwrap().ptr().as_ptr(), queue);
398411
let input_queue = InputQueue::from_ptr(NonNull::new(queue).unwrap());
399412
input_queue.detach_looper();
@@ -407,6 +420,6 @@ unsafe extern "C" fn on_content_rect_changed(activity: *mut ANativeActivity, rec
407420
right: (*rect).right as _,
408421
bottom: (*rect).bottom as _,
409422
};
410-
*CONTENT_RECT.write().unwrap() = rect;
423+
*CONTENT_RECT.write() = rect;
411424
wake(activity, Event::ContentRectChanged);
412425
}

0 commit comments

Comments
 (0)