uniffi_core/ffi/
foreigncallbacks.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains code to handle foreign callbacks - C-ABI functions that are defined by a
//! foreign language, then registered with UniFFI.  These callbacks are used to implement callback
//! interfaces, async scheduling etc. Foreign callbacks are registered at startup, when the foreign
//! code loads the exported library. For each callback type, we also define a "cell" type for
//! storing the callback.

use std::sync::atomic::{AtomicUsize, Ordering};

use crate::{ForeignExecutorHandle, RustBuffer, RustTaskCallback};

/// ForeignCallback is the Rust representation of a foreign language function.
/// It is the basis for all callbacks interfaces. It is registered exactly once per callback interface,
/// at library start up time.
/// Calling this method is only done by generated objects which mirror callback interfaces objects in the foreign language.
///
/// * The `handle` is the key into a handle map on the other side of the FFI used to look up the foreign language object
///   that implements the callback interface/trait.
/// * The `method` selector specifies the method that will be called on the object, by looking it up in a list of methods from
///   the IDL. The list is 1 indexed. Note that the list of methods is generated by UniFFI from the IDL and used in all
///   bindings, so we can rely on the method list being stable within the same run of UniFFI.
/// * `args_data` and `args_len` represents a serialized buffer of arguments to the function. The scaffolding code
///   writes the callback arguments to this buffer, in order, using `FfiConverter.write()`. The bindings code reads the
///   arguments from the buffer and passes them to the user's callback.
/// * `buf_ptr` is a pointer to where the resulting buffer will be written. UniFFI will allocate a
///   buffer to write the result into.
/// * Callbacks return one of the `CallbackResult` values
///   Note: The output buffer might still contain 0 bytes of data.
pub type ForeignCallback = unsafe extern "C" fn(
    handle: u64,
    method: u32,
    args_data: *const u8,
    args_len: i32,
    buf_ptr: *mut RustBuffer,
) -> i32;

/// Callback to schedule a Rust call with a `ForeignExecutor`. The bindings code registers exactly
/// one of these with the Rust code.
///
/// Delay is an approximate amount of ms to wait before scheduling the call.  Delay is usually 0,
/// which means schedule sometime soon.
///
/// As a special case, when Rust drops the foreign executor, with `task=null`.  The foreign
/// bindings should release the reference to the executor that was reserved for Rust.
///
/// This callback can be invoked from any thread, including threads created by Rust.
///
/// The callback should return one of the `ForeignExecutorCallbackResult` values.
pub type ForeignExecutorCallback = extern "C" fn(
    executor: ForeignExecutorHandle,
    delay: u32,
    task: Option<RustTaskCallback>,
    task_data: *const (),
) -> i8;

/// Store a [ForeignCallback] pointer
pub(crate) struct ForeignCallbackCell(AtomicUsize);

/// Store a [ForeignExecutorCallback] pointer
pub(crate) struct ForeignExecutorCallbackCell(AtomicUsize);

/// Macro to define foreign callback types as well as the callback cell.
macro_rules! impl_foreign_callback_cell {
    ($callback_type:ident, $cell_type:ident) => {
        // Overly-paranoid sanity checking to ensure that these types are
        // convertible between each-other. `transmute` actually should check this for
        // us too, but this helps document the invariants we rely on in this code.
        //
        // Note that these are guaranteed by
        // https://rust-lang.github.io/unsafe-code-guidelines/layout/function-pointers.html
        // and thus this is a little paranoid.
        static_assertions::assert_eq_size!(usize, $callback_type);
        static_assertions::assert_eq_size!(usize, Option<$callback_type>);

        impl $cell_type {
            pub const fn new() -> Self {
                Self(AtomicUsize::new(0))
            }

            pub fn set(&self, callback: $callback_type) {
                // Store the pointer using Ordering::Relaxed.  This is sufficient since callback
                // should be set at startup, before there's any chance of using them.
                self.0.store(callback as usize, Ordering::Relaxed);
            }

            pub fn get(&self) -> $callback_type {
                let ptr_value = self.0.load(Ordering::Relaxed);
                unsafe {
                    // SAFETY: self.0 was set in `set` from our function pointer type, so
                    // it's safe to transmute it back here.
                    ::std::mem::transmute::<usize, Option<$callback_type>>(ptr_value)
                        .expect("Bug: callback not set.  This is likely a uniffi bug.")
                }
            }
        }
    };
}

impl_foreign_callback_cell!(ForeignCallback, ForeignCallbackCell);
impl_foreign_callback_cell!(ForeignExecutorCallback, ForeignExecutorCallbackCell);