Skip to main content

maplibre_native_ffi/
handle.rs

1use maplibre_native_ffi_sys::NativeHandle;
2
3use crate::Result;
4use crate::call::Call;
5use crate::report::{Report, report};
6
7/// A native handle a destructor attempted to destroy and could not. The handle
8/// stays live.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct NativeHandleLeak {
11    /// The native type name, such as `mln_map`.
12    pub type_name: &'static str,
13    /// The handle id that was not destroyed.
14    pub id: u64,
15}
16
17/// The one owner of a native handle, which `Drop` disposes.
18///
19/// Native keeps a handle's parent alive for as long as the handle needs it,
20/// and serializes calls and releases on the handle, so an owner holds only the
21/// id.
22pub(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    /// Takes ownership of `native` with its disposer.
30    ///
31    /// # Safety
32    ///
33    /// `native` must come from an accepted ownership transfer of handle type
34    /// `H`, and `dispose` must be its disposal.
35    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    /// The raw id; native validates whether it still names a live handle.
57    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
80/// Declares a public handle type that owns one native handle of type
81/// `$native`, which `$dispose` disposes when the owner drops. The attributes
82/// before `fn id` document its `id`.
83macro_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                // SAFETY: generated operations adopt only the handles that an
103                // accepted native call transferred, with their disposal.
104                let inner = unsafe {
105                    $crate::handle::Owner::adopt(raw, stringify!($native), dispose)
106                }?;
107                Ok(Self { inner })
108            }
109
110            /// Wraps `raw`, which a callback borrows, in an owner that never
111            /// disposes it. The callback reaches it only by reference.
112            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            /// Runs a consuming call, which ends this owner once native
119            /// accepts it and hands it back in the error otherwise.
120            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                        // Native took the handle, so nothing is left to dispose.
127                        std::mem::forget(self);
128                        Ok(value)
129                    }
130                    Err(error) => Err($crate::ReleaseError { handle: self, error }),
131                }
132            }
133
134            /// Runs an answer, which ends this owner whatever it returns. A
135            /// failure drops the owner, which releases the handle in case the
136            /// failure came before native saw the answer.
137            #[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                // Converting the refusal drops the handle it gave back.
143                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
156/// A batch that a drain published, which drop releases once its view is
157/// copied, whether or not the copy succeeds.
158pub(crate) struct Drained<H: Copy> {
159    raw: H,
160    release: unsafe extern "C" fn(H),
161}
162
163impl<H: Copy> Drained<H> {
164    /// # Safety
165    ///
166    /// Native must have published `raw` to this caller, and `release` must be
167    /// its release, which nothing else calls.
168    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        // SAFETY: `new` received the only claim on the batch.
176        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        // SAFETY: the disposer never reaches native.
202        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        // A drop inside a native callback disposes later, off the callback's
210        // stack, and its failure still reaches the reporter.
211        {
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}