Skip to content

Commit bde27cc

Browse files
committed
Adding Handle and HandleAlloc
`Handle` is a u64 newtype that I hope to use to represent objects that get passed across the FFI. `HandleAlloc` is an ffi trait that converts between `Arc<>` and `Handle`.
1 parent 10caead commit bde27cc

File tree

5 files changed

+157
-2
lines changed

5 files changed

+157
-2
lines changed

uniffi_core/src/ffi/ffidefault.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ impl FfiDefault for () {
3939
fn ffi_default() {}
4040
}
4141

42+
impl FfiDefault for crate::Handle {
43+
fn ffi_default() -> Self {
44+
Self::default()
45+
}
46+
}
47+
4248
impl FfiDefault for *const std::ffi::c_void {
4349
fn ffi_default() -> Self {
4450
std::ptr::null()

uniffi_core/src/ffi/handle.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
/// Object handle
6+
///
7+
/// Handles opaque `u64` values used to pass objects across the FFI, both for objects implemented in
8+
/// Rust and ones implemented in the foreign language.
9+
///
10+
/// Rust handles are generated by leaking a raw pointer
11+
/// Foreign handles are generated with a handle map that only generates odd values.
12+
/// For all currently supported architectures and hopefully any ones we add in the future:
13+
/// * 0 is an invalid value.
14+
/// * The lowest bit will always be set for foreign handles and never set for Rust ones (since the
15+
/// leaked pointer will be aligned).
16+
///
17+
/// Rust handles are mainly managed is through the [crate::HandleAlloc] trait.
18+
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
19+
#[repr(transparent)]
20+
pub struct Handle(u64);
21+
22+
impl Handle {
23+
pub fn from_pointer<T>(ptr: *const T) -> Self {
24+
Self(ptr as u64)
25+
}
26+
27+
pub fn as_pointer<T>(&self) -> *const T {
28+
self.0 as *const T
29+
}
30+
31+
pub fn from_raw(raw: u64) -> Option<Self> {
32+
if raw == 0 {
33+
None
34+
} else {
35+
Some(Self(raw))
36+
}
37+
}
38+
39+
pub fn as_raw(&self) -> u64 {
40+
self.0
41+
}
42+
}

uniffi_core/src/ffi/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod ffidefault;
99
pub mod foreignbytes;
1010
pub mod foreigncallbacks;
1111
pub mod foreignexecutor;
12+
pub mod handle;
1213
pub mod rustbuffer;
1314
pub mod rustcalls;
1415
pub mod rustfuture;
@@ -18,6 +19,7 @@ pub use ffidefault::FfiDefault;
1819
pub use foreignbytes::*;
1920
pub use foreigncallbacks::*;
2021
pub use foreignexecutor::*;
22+
pub use handle::*;
2123
pub use rustbuffer::*;
2224
pub use rustcalls::*;
2325
pub use rustfuture::*;

uniffi_core/src/ffi_converter_traits.rs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ use std::{borrow::Borrow, sync::Arc};
5151
use anyhow::bail;
5252
use bytes::Buf;
5353

54-
use crate::{FfiDefault, MetadataBuffer, Result, RustBuffer, UnexpectedUniFFICallbackError};
54+
use crate::{
55+
FfiDefault, Handle, MetadataBuffer, Result, RustBuffer, UnexpectedUniFFICallbackError,
56+
};
5557

5658
/// Generalized FFI conversions
5759
///
@@ -351,6 +353,66 @@ pub trait ConvertError<UT>: Sized {
351353
fn try_convert_unexpected_callback_error(e: UnexpectedUniFFICallbackError) -> Result<Self>;
352354
}
353355

356+
/// Manage handles for `Arc<Self>` instances
357+
///
358+
/// Handles are used to manage objects that are passed across the FFI. They general usage is:
359+
///
360+
/// * Rust creates an `Arc<>`
361+
/// * Rust uses `new_handle` to create a handle that represents the Arc reference
362+
/// * Rust passes the handle to the foreign code as a `u64`
363+
/// * The foreign code passes the handle back to `Rust` to refer to the object:
364+
/// * Handle are usually passed as borrowed values. When an FFI function inputs a handle as an
365+
/// argument, the foreign code simply passes a copy of the `u64` to Rust, which calls `get_arc`
366+
/// to get a new `Arc<>` clone for it.
367+
/// * Handles are returned as owned values. When an FFI function returns a handle, the foreign
368+
/// code either stops using the handle after returning it or calls `clone_handle` and returns
369+
/// the clone. TODO: actually start doing this (#1797)
370+
/// * Eventually the foreign code may destroy their handle by passing it into a "free" FFI
371+
/// function. This functions input an owned handle and consume it.
372+
///
373+
/// The foreign code also defines their own handles. These represent foreign objects that are
374+
/// passed to Rust. Using foreign handles is essentially the same as above, but in reverse.
375+
///
376+
/// Handles must always be `Send` and the objects they reference must always be `Sync`.
377+
/// This means that it must be safe to send handles to other threads and use them there.
378+
///
379+
/// Note: this only needs to be derived for unsized types, there's a blanket impl for `T: Sized`.
380+
///
381+
/// ## Safety
382+
///
383+
/// All traits are unsafe (implementing it requires `unsafe impl`) because we can't guarantee
384+
/// that it's safe to pass your type out to foreign-language code and back again. Buggy
385+
/// implementations of this trait might violate some assumptions made by the generated code,
386+
/// or might not match with the corresponding code in the generated foreign-language bindings.
387+
/// These traits should not be used directly, only in generated code, and the generated code should
388+
/// have fixture tests to test that everything works correctly together.
389+
/// `&T` using the Arc.
390+
pub unsafe trait HandleAlloc<UT> {
391+
/// Create a new handle for an Arc value
392+
///
393+
/// Use this to lower an Arc into a handle value before passing it across the FFI.
394+
/// The newly-created handle will have reference count = 1.
395+
fn new_handle(value: Arc<Self>) -> Handle;
396+
397+
/// Clone a handle
398+
///
399+
/// This creates a new handle from an existing one.
400+
/// It's used when the foreign code wants to pass back an owned handle and still keep a copy
401+
/// for themselves.
402+
fn clone_handle(handle: Handle) -> Handle;
403+
404+
/// Get a clone of the `Arc<>` using a "borrowed" handle.
405+
///
406+
/// Take care that the handle can not be destroyed between when it's passed and when
407+
/// `get_arc()` is called. #1797 is a cautionary tale.
408+
fn get_arc(handle: Handle) -> Arc<Self> {
409+
Self::consume_handle(Self::clone_handle(handle))
410+
}
411+
412+
/// Consume a handle, getting back the initial `Arc<>`
413+
fn consume_handle(handle: Handle) -> Arc<Self>;
414+
}
415+
354416
/// Derive FFI traits
355417
///
356418
/// This can be used to derive:
@@ -463,4 +525,46 @@ macro_rules! derive_ffi_traits {
463525
}
464526
}
465527
};
528+
529+
(impl $(<$($generic:ident),*>)? $(::uniffi::)? HandleAlloc<$ut:path> for $ty:ty $(where $($where:tt)*)?) => {
530+
unsafe impl $(<$($generic),*>)* $crate::HandleAlloc<$ut> for $ty $(where $($where)*)*
531+
{
532+
// To implement HandleAlloc for an unsized type, wrap it with a second Arc which
533+
// converts the wide pointer into a normal pointer.
534+
535+
fn new_handle(value: ::std::sync::Arc<Self>) -> $crate::Handle {
536+
$crate::Handle::from_pointer(::std::sync::Arc::into_raw(::std::sync::Arc::new(value)))
537+
}
538+
539+
fn clone_handle(handle: $crate::Handle) -> $crate::Handle {
540+
unsafe {
541+
::std::sync::Arc::<::std::sync::Arc<Self>>::increment_strong_count(handle.as_pointer::<::std::sync::Arc<Self>>());
542+
}
543+
handle
544+
}
545+
546+
fn consume_handle(handle: $crate::Handle) -> ::std::sync::Arc<Self> {
547+
unsafe {
548+
::std::sync::Arc::<Self>::clone(
549+
&std::sync::Arc::<::std::sync::Arc::<Self>>::from_raw(handle.as_pointer::<::std::sync::Arc<Self>>())
550+
)
551+
}
552+
}
553+
}
554+
};
555+
}
556+
557+
unsafe impl<T: Sized, UT> HandleAlloc<UT> for T {
558+
fn new_handle(value: Arc<Self>) -> Handle {
559+
Handle::from_pointer(Arc::into_raw(value))
560+
}
561+
562+
fn clone_handle(handle: Handle) -> Handle {
563+
unsafe { Arc::increment_strong_count(handle.as_pointer::<T>()) };
564+
handle
565+
}
566+
567+
fn consume_handle(handle: Handle) -> Arc<Self> {
568+
unsafe { Arc::from_raw(handle.as_pointer()) }
569+
}
466570
}

uniffi_core/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ pub mod metadata;
4545

4646
pub use ffi::*;
4747
pub use ffi_converter_traits::{
48-
ConvertError, FfiConverter, FfiConverterArc, Lift, LiftRef, LiftReturn, Lower, LowerReturn,
48+
ConvertError, FfiConverter, FfiConverterArc, HandleAlloc, Lift, LiftRef, LiftReturn, Lower,
49+
LowerReturn,
4950
};
5051
pub use metadata::*;
5152

0 commit comments

Comments
 (0)