1use std::ffi::c_void;
2use std::future::Future;
3use std::panic::{AssertUnwindSafe, catch_unwind};
4use std::pin::Pin;
5use std::sync::{Arc, Condvar, Mutex};
6use std::task::{Context, Poll, Waker};
7use std::time::Duration;
8
9use maplibre_native_ffi_sys as sys;
10
11use crate::convert::{FromNative, from_native};
12use crate::handle::lock;
13use crate::{Error, Result};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct CommandCompletion {
22 pub generation: u64,
24 pub render_update_generation: u64,
27}
28
29struct State<T> {
30 result: Mutex<Option<Result<T>>>,
31 ready: Condvar,
32 waker: Mutex<Option<Waker>>,
33}
34
35type Converter<T> = Box<dyn FnOnce(&sys::mln_completion_result) -> Result<T> + Send>;
36
37struct Bridge<T> {
38 state: Arc<State<T>>,
39 convert: Mutex<Option<Converter<T>>>,
40}
41
42#[must_use = "the operation is already submitted, and dropping its future ignores a failure; \
52 use the owner's `discarding()` view to submit a command without one"]
53pub struct NativeFuture<T> {
54 state: Arc<State<T>>,
55}
56
57impl<T> std::fmt::Debug for NativeFuture<T> {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 f.debug_struct("NativeFuture")
60 .field("ready", &self.is_ready())
61 .finish()
62 }
63}
64
65impl<T> NativeFuture<T> {
66 pub fn is_ready(&self) -> bool {
68 lock(&self.state.result).is_some()
69 }
70
71 pub fn wait(self) -> Result<T> {
76 let result = lock(&self.state.result);
77 self.state
78 .ready
79 .wait_while(result, |result| result.is_none())
80 .unwrap_or_else(|poisoned| poisoned.into_inner())
81 .take()
82 .expect("the wait ends once a result is present")
83 }
84
85 pub fn wait_timeout(self, timeout: Duration) -> std::result::Result<Result<T>, Self> {
90 let result = {
91 let result = lock(&self.state.result);
92 self.state
93 .ready
94 .wait_timeout_while(result, timeout, |result| result.is_none())
95 .unwrap_or_else(|poisoned| poisoned.into_inner())
96 .0
97 .take()
98 };
99 result.ok_or(self)
100 }
101}
102
103impl<T> Future for NativeFuture<T> {
104 type Output = Result<T>;
105
106 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
107 let mut result = lock(&self.state.result);
108 if let Some(result) = result.take() {
109 return Poll::Ready(result);
110 }
111 *lock(&self.state.waker) = Some(context.waker().clone());
112 Poll::Pending
113 }
114}
115
116unsafe extern "C" fn complete<T>(
117 user_data: *mut c_void,
118 result: *const sys::mln_completion_result,
119) {
120 let _scope = crate::finalize::CallbackScope::enter();
121 if user_data.is_null() || result.is_null() {
122 return;
123 }
124 let bridge = unsafe { &*user_data.cast::<Bridge<T>>() };
126 let raw = unsafe { &*result };
128 let converted = if raw.status == sys::MLN_STATUS_OK {
129 match lock(&bridge.convert).take() {
130 Some(convert) => match catch_unwind(AssertUnwindSafe(|| convert(raw))) {
131 Ok(result) => result,
132 Err(payload) => {
133 std::mem::forget(payload);
135 Err(Error::new(
136 crate::Status::NativeError,
137 "completion conversion panicked",
138 ))
139 }
140 },
141 None => Err(Error::new(
142 crate::Status::InvalidState,
143 "native completion ran more than once",
144 )),
145 }
146 } else {
147 Err(Error::Status {
148 status: crate::Status::from_raw(raw.status),
149 diagnostic: copy_diagnostic(raw),
150 generation: raw.generation,
151 })
152 };
153 *lock(&bridge.state.result) = Some(converted);
154 bridge.state.ready.notify_all();
155 let waker = lock(&bridge.state.waker).take();
156 if let Some(waker) = waker
157 && let Err(payload) = catch_unwind(AssertUnwindSafe(|| waker.wake()))
158 {
159 crate::callback::report_panic("mln_completion_callback", payload);
160 }
161}
162
163unsafe extern "C" fn release<T>(user_data: *mut c_void) {
164 let _scope = crate::finalize::CallbackScope::enter();
165 if !user_data.is_null() {
166 let bridge = unsafe { Box::from_raw(user_data.cast::<Bridge<T>>()) };
168 if let Err(payload) = catch_unwind(AssertUnwindSafe(|| drop(bridge))) {
169 crate::callback::report_panic("mln_user_data_release", payload);
170 }
171 }
172}
173
174pub(crate) fn submit<T, S, C>(submit: S, convert: C) -> Result<NativeFuture<T>>
178where
179 T: Send + 'static,
180 S: FnOnce(*const sys::mln_completion, *mut sys::mln_diagnostic) -> sys::mln_status,
181 C: FnOnce(&sys::mln_completion_result) -> Result<T> + Send + 'static,
182{
183 let state = Arc::new(State {
184 result: Mutex::new(None),
185 ready: Condvar::new(),
186 waker: Mutex::new(None),
187 });
188 let bridge = Box::new(Bridge {
189 state: Arc::clone(&state),
190 convert: Mutex::new(Some(Box::new(convert))),
191 });
192 let bridge = Box::into_raw(bridge);
193 let completion = sys::mln_completion {
194 size: std::mem::size_of::<sys::mln_completion>() as u32,
195 callback: Some(complete::<T>),
196 user_data: bridge.cast(),
197 release_user_data: Some(release::<T>),
198 queue: sys::mln_delivery_queue(0),
199 };
200 if let Err(error) = crate::error::check(|diagnostic| submit(&completion, diagnostic)) {
201 drop(unsafe { Box::from_raw(bridge) });
203 return Err(error);
204 }
205 Ok(NativeFuture { state })
206}
207
208pub(crate) fn unit(result: &sys::mln_completion_result) -> Result<()> {
209 if !result.value.is_null() || result.value_count != 0 {
210 return Err(Error::new(
211 crate::Status::NativeError,
212 "unit completion returned a value",
213 ));
214 }
215 Ok(())
216}
217
218pub(crate) fn command(result: &sys::mln_completion_result) -> Result<CommandCompletion> {
219 unit(result)?;
220 Ok(CommandCompletion {
221 generation: result.generation,
222 render_update_generation: result.render_update_generation,
223 })
224}
225
226fn copy_diagnostic(result: &sys::mln_completion_result) -> String {
227 if result.diagnostic.data.is_null() || result.diagnostic.size == 0 {
228 return String::new();
229 }
230 String::from_utf8_lossy(unsafe {
232 std::slice::from_raw_parts(result.diagnostic.data.cast::<u8>(), result.diagnostic.size)
233 })
234 .into_owned()
235}
236
237pub(crate) fn copy_value<T: Copy>(result: &sys::mln_completion_result) -> Result<T> {
238 if result.value.is_null() || result.value_count != 1 {
239 return Err(Error::new(
240 crate::Status::NativeError,
241 "native completion returned no value",
242 ));
243 }
244 Ok(unsafe { result.value.cast::<T>().read_unaligned() })
246}
247
248pub(crate) fn copy_slice<T: Copy>(result: &sys::mln_completion_result) -> Result<Vec<T>> {
249 if result.value_count == 0 {
250 return Ok(Vec::new());
251 }
252 if result.value.is_null() {
253 return Err(Error::new(
254 crate::Status::NativeError,
255 "native completion returned a null slice",
256 ));
257 }
258 unsafe {
261 crate::convert::strided_items(
262 result.value.cast::<T>(),
263 result.value_count,
264 result.value_size,
265 )
266 }
267}
268
269pub(crate) fn value<N: Copy, T: FromNative<N>>(result: &sys::mln_completion_result) -> Result<T> {
270 let value = copy_value::<N>(result)?;
271 unsafe { from_native(value) }
274}
275
276pub(crate) fn optional<N: Copy, T: FromNative<N>>(
277 result: &sys::mln_completion_result,
278) -> Result<Option<T>> {
279 if result.value_count == 0 {
280 return Ok(None);
281 }
282 value::<N, T>(result).map(Some)
283}
284
285pub(crate) fn list<N: Copy, T: FromNative<N>>(
286 result: &sys::mln_completion_result,
287) -> Result<Vec<T>> {
288 copy_slice::<N>(result)?
289 .into_iter()
290 .map(|value| unsafe { from_native(value) })
292 .collect()
293}
294
295#[cfg(test)]
296mod tests {
297 use std::sync::atomic::{AtomicUsize, Ordering};
298
299 use super::*;
300
301 struct AcceptedSubmission {
304 callback: sys::mln_completion_callback,
305 user_data: *mut c_void,
306 release_user_data: sys::mln_user_data_release,
307 }
308
309 fn result(status: sys::mln_status) -> sys::mln_completion_result {
310 let mut result = unsafe { std::mem::zeroed::<sys::mln_completion_result>() };
312 result.size = std::mem::size_of::<sys::mln_completion_result>() as u32;
313 result.status = status;
314 result
315 }
316
317 impl AcceptedSubmission {
318 fn deliver(&self, status: sys::mln_status) {
319 self.deliver_result(&result(status));
320 }
321
322 fn deliver_result(&self, result: &sys::mln_completion_result) {
323 unsafe { self.callback.unwrap()(self.user_data, result) };
326 }
327
328 fn release(&self) {
329 unsafe { self.release_user_data.unwrap()(self.user_data) };
332 }
333 }
334
335 fn accept<T, C>(convert: C) -> (NativeFuture<T>, AcceptedSubmission)
338 where
339 T: Send + 'static,
340 C: FnOnce(&sys::mln_completion_result) -> Result<T> + Send + 'static,
341 {
342 let mut accepted = None;
343 let future = submit(
344 |completion, _| {
345 let completion = unsafe { &*completion };
347 accepted = Some(AcceptedSubmission {
348 callback: completion.callback,
349 user_data: completion.user_data,
350 release_user_data: completion.release_user_data,
351 });
352 sys::MLN_STATUS_OK
353 },
354 convert,
355 )
356 .unwrap();
357 (future, accepted.unwrap())
358 }
359
360 fn completed<T>(future: NativeFuture<T>) -> Result<T> {
361 future
362 .wait_timeout(Duration::ZERO)
363 .expect("the future has completed")
364 }
365
366 #[test]
367 fn polling_a_future_wakes_the_task_that_registered_a_waker() {
368 struct CountingWaker(AtomicUsize);
369
370 impl std::task::Wake for CountingWaker {
371 fn wake(self: Arc<Self>) {
372 self.0.fetch_add(1, Ordering::Release);
373 }
374 }
375
376 let (future, accepted) = accept(unit);
377 let waker = Arc::new(CountingWaker(AtomicUsize::new(0)));
378 let context_waker = Waker::from(Arc::clone(&waker));
379 let mut context = Context::from_waker(&context_waker);
380 let mut future = Box::pin(future);
381
382 assert!(future.as_mut().poll(&mut context).is_pending());
383 assert_eq!(waker.0.load(Ordering::Acquire), 0);
384
385 accepted.deliver(sys::MLN_STATUS_OK);
386 assert_eq!(waker.0.load(Ordering::Acquire), 1);
387 assert!(matches!(
388 future.as_mut().poll(&mut context),
389 Poll::Ready(Ok(()))
390 ));
391 accepted.release();
392 }
393
394 #[test]
398 fn an_array_result_is_read_at_its_value_size() {
399 #[repr(C)]
401 #[derive(Clone, Copy)]
402 struct Wide {
403 point: sys::mln_lat_lng,
404 newer: f64,
405 }
406 let wide = [(1.0, 2.0), (3.0, 4.0)].map(|(latitude, longitude)| Wide {
407 point: sys::mln_lat_lng {
408 latitude,
409 longitude,
410 },
411 newer: -1.0,
412 });
413 let mut result = unsafe { std::mem::zeroed::<sys::mln_completion_result>() };
415 result.status = sys::MLN_STATUS_OK;
416 result.value = wide.as_ptr().cast();
417 result.value_count = wide.len();
418 result.value_size = std::mem::size_of::<Wide>() as u32;
419
420 let points = copy_slice::<sys::mln_lat_lng>(&result).unwrap();
421 assert_eq!(
422 points
423 .iter()
424 .map(|point| (point.latitude, point.longitude))
425 .collect::<Vec<_>>(),
426 [(1.0, 2.0), (3.0, 4.0)]
427 );
428
429 result.value_size = std::mem::size_of::<sys::mln_lat_lng>() as u32 - 1;
430 assert_eq!(
431 copy_slice::<sys::mln_lat_lng>(&result)
432 .unwrap_err()
433 .status(),
434 crate::Status::NativeError
435 );
436 }
437
438 #[test]
439 fn a_panicking_converter_completes_with_an_error_before_native_release() {
440 let (future, accepted) = accept(|_| -> Result<()> { panic!("converter panic") });
441 accepted.deliver(sys::MLN_STATUS_OK);
442 assert_eq!(
443 completed(future).unwrap_err().status(),
444 crate::Status::NativeError
445 );
446 accepted.release();
447 }
448
449 #[test]
450 fn a_wait_gives_the_future_back_until_native_completes_it() {
451 let (future, accepted) = accept(unit);
452
453 let future = future.wait_timeout(Duration::ZERO).unwrap_err();
454
455 accepted.deliver(sys::MLN_STATUS_OK);
456 completed(future).unwrap();
457 accepted.release();
458 }
459
460 #[test]
461 fn a_failed_or_cancelled_command_fails_its_wait_with_its_status_diagnostic_and_generation() {
462 let diagnostic = b"style parse failed";
463 let mut failed = result(sys::MLN_STATUS_INVALID_ARGUMENT);
464 failed.generation = 7;
465 failed.diagnostic = sys::mln_buffer_view {
466 data: diagnostic.as_ptr().cast(),
467 size: diagnostic.len(),
468 };
469 let cancelled = result(sys::MLN_STATUS_CANCELLED);
470 for (delivered, status, diagnostic, generation) in [
471 (
472 failed,
473 crate::Status::InvalidArgument,
474 "style parse failed",
475 7,
476 ),
477 (cancelled, crate::Status::Cancelled, "", 0),
478 ] {
479 let (future, accepted) = accept(command);
480 accepted.deliver_result(&delivered);
481 let error = completed(future).unwrap_err();
482 assert_eq!(error.status(), status);
483 assert_eq!(error.diagnostic(), diagnostic);
484 assert_eq!(error.generation(), generation);
485 accepted.release();
486 }
487
488 let mut committed = result(sys::MLN_STATUS_OK);
489 committed.generation = 8;
490 committed.render_update_generation = 3;
491 let (future, accepted) = accept(command);
492 accepted.deliver_result(&committed);
493 assert_eq!(
494 completed(future).unwrap(),
495 CommandCompletion {
496 generation: 8,
497 render_update_generation: 3,
498 }
499 );
500 accepted.release();
501 }
502
503 #[test]
504 fn an_untimed_wait_returns_once_native_completes_the_future() {
505 let (future, accepted) = accept(unit);
506
507 let waiter = std::thread::spawn(move || future.wait());
508 accepted.deliver(sys::MLN_STATUS_OK);
509
510 waiter.join().unwrap().unwrap();
511 accepted.release();
512 }
513
514 #[test]
515 fn a_wait_times_out_and_a_dropped_future_accepts_a_late_completion() {
516 let (future, accepted) = accept(unit);
517
518 let future = future.wait_timeout(Duration::ZERO).unwrap_err();
519
520 drop(future);
523 accepted.deliver(sys::MLN_STATUS_OK);
524 accepted.release();
525 }
526
527 #[test]
528 fn a_rejected_submission_returns_its_status() {
529 let error = submit(|_, _| sys::MLN_STATUS_INVALID_STATE, unit).unwrap_err();
530 assert_eq!(error.status(), crate::Status::InvalidState);
531 }
532}