maplibre_native_ffi/
handle.rs1use maplibre_native_ffi_sys::NativeHandle;
2
3use crate::Result;
4use crate::call::Call;
5use crate::report::{Report, report};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct NativeHandleLeak {
11 pub type_name: &'static str,
13 pub id: u64,
15}
16
17pub(crate) struct Owner<H: NativeHandle + Send> {
23 native: H,
24 dispose: fn(H) -> Result<()>,
25 type_name: &'static str,
26}
27
28impl<H: NativeHandle + Send> Owner<H> {
29 pub(crate) unsafe fn adopt(
36 native: H,
37 type_name: &'static str,
38 dispose: fn(H) -> Result<()>,
39 ) -> Result<Self> {
40 if native.to_raw() == 0 {
41 return Err(crate::Error::invalid_argument(
42 "native handle must not be zero",
43 ));
44 }
45 Ok(Self {
46 native,
47 dispose,
48 type_name,
49 })
50 }
51
52 pub(crate) fn id(&self) -> u64 {
53 self.native.to_raw()
54 }
55
56 pub(crate) fn native(&self) -> H {
58 self.native
59 }
60
61 pub(crate) fn call(&self) -> Call<'_, H> {
62 Call::new(self.native)
63 }
64}
65
66impl<H: NativeHandle + Send> Drop for Owner<H> {
67 fn drop(&mut self) {
68 let (native, dispose, type_name) = (self.native, self.dispose, self.type_name);
69 crate::finalize::finalize(move || {
70 if dispose(native).is_err() {
71 report(Report::LeakedHandle(NativeHandleLeak {
72 type_name,
73 id: native.to_raw(),
74 }));
75 }
76 });
77 }
78}
79
80macro_rules! native_owner {
84 (
85 $(#[$meta:meta])*
86 pub struct $name:ident($native:ident) dispose $dispose:expr;
87 $(#[$id_meta:meta])*
88 fn id;
89 ) => {
90 $(#[$meta])*
91 pub struct $name {
92 pub(crate) inner: $crate::handle::Owner<maplibre_native_ffi_sys::$native>,
93 }
94 impl std::fmt::Debug for $name {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 write!(f, "{}(id={:#x})", stringify!($name), self.id())
97 }
98 }
99 impl $name {
100 pub(crate) fn adopt(raw: maplibre_native_ffi_sys::$native) -> $crate::Result<Self> {
101 let dispose: fn(maplibre_native_ffi_sys::$native) -> $crate::Result<()> = $dispose;
102 let inner = unsafe {
105 $crate::handle::Owner::adopt(raw, stringify!($native), dispose)
106 }?;
107 Ok(Self { inner })
108 }
109
110 pub(crate) fn borrow(
113 raw: maplibre_native_ffi_sys::$native,
114 ) -> $crate::Result<std::mem::ManuallyDrop<Self>> {
115 Self::adopt(raw).map(std::mem::ManuallyDrop::new)
116 }
117
118 pub(crate) fn consume<R>(
121 self,
122 call: impl FnOnce(maplibre_native_ffi_sys::$native) -> $crate::Result<R>,
123 ) -> std::result::Result<R, $crate::ReleaseError<Self>> {
124 match call(self.inner.native()) {
125 Ok(value) => {
126 std::mem::forget(self);
128 Ok(value)
129 }
130 Err(error) => Err($crate::ReleaseError { handle: self, error }),
131 }
132 }
133
134 #[allow(dead_code)]
138 pub(crate) fn consume_always<R>(
139 self,
140 call: impl FnOnce(maplibre_native_ffi_sys::$native) -> $crate::Result<R>,
141 ) -> $crate::Result<R> {
142 self.consume(call).map_err($crate::Error::from)
144 }
145
146 $(#[$id_meta])*
147 pub fn id(&self) -> u64 {
148 self.inner.id()
149 }
150 }
151 };
152}
153
154pub(crate) use native_owner;
155
156pub(crate) struct Drained<H: Copy> {
159 raw: H,
160 release: unsafe extern "C" fn(H),
161}
162
163impl<H: Copy> Drained<H> {
164 pub(crate) unsafe fn new(raw: H, release: unsafe extern "C" fn(H)) -> Self {
169 Self { raw, release }
170 }
171}
172
173impl<H: Copy> Drop for Drained<H> {
174 fn drop(&mut self) {
175 unsafe { (self.release)(self.raw) }
177 }
178}
179
180pub(crate) fn lock<T>(mutex: &std::sync::Mutex<T>) -> std::sync::MutexGuard<'_, T> {
181 mutex.lock().unwrap_or_else(|p| p.into_inner())
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use crate::Error;
188 use maplibre_native_ffi_sys as sys;
189 use std::sync::mpsc;
190 use std::time::Duration;
191
192 #[test]
193 fn a_handle_dropped_in_a_callback_reports_a_failed_disposal() {
194 let (sender, leaks) = mpsc::channel();
195 crate::set_reporter(Some(Box::new(move |report| {
196 if let crate::Report::LeakedHandle(leak) = report {
197 let _ = sender.send(leak);
198 }
199 })));
200 let id = 0x0d00_0000_0000_0007;
201 let owner = unsafe {
203 Owner::adopt(sys::mln_map(id), "mln_map", |_| {
204 Err(Error::invalid_argument("dispose refused"))
205 })
206 }
207 .unwrap();
208
209 {
212 let _callback = crate::finalize::CallbackScope::enter();
213 drop(owner);
214 }
215 let received = leaks.recv_timeout(Duration::from_secs(10));
216 crate::set_reporter(None);
217
218 let leak = received.expect("the failed disposal was never reported");
219 assert_eq!(
220 leak,
221 crate::NativeHandleLeak {
222 type_name: "mln_map",
223 id,
224 }
225 );
226 }
227}