Skip to content

Support for abi_stable #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ exclude = ["/link_tests"]
rust-version = "1.49" # 1.48 doesn't support `ManuallyDrop` in union.

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
abi_stable = { version = "0.10", default-features = false, optional = true }

[dev-dependencies]
tokio = { version = "1.4.0", features = ["macros", "rt-multi-thread", "sync", "time"] }
79 changes: 50 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,11 @@ use std::{
/// Every non-compatible ABI change will increase this number.
pub const ABI_VERSION: u32 = 2;

type PollFn<T> = unsafe extern "C" fn(fut_ptr: *mut (), context_ptr: *mut FfiContext) -> FfiPoll<T>;

/// The FFI compatible [`std::task::Poll`]
///
/// [`std::task::Poll`]: std::task::Poll
#[repr(C, u8)]
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
pub enum FfiPoll<T> {
/// Represents that a value is immediately ready.
Ready(T),
Expand Down Expand Up @@ -128,21 +127,22 @@ impl Drop for DropBomb {
///
/// [`std::task::Context`]: std::task::Context
#[repr(C)]
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
pub struct FfiContext<'a> {
/// This waker is passed as borrow semantic.
/// The external fn must not `drop` or `wake` it.
waker_ref: *const FfiWaker,
waker: *const FfiWakerBase,
/// Lets the compiler know that this references the FfiWaker and should not outlive it
phantom: PhantomData<&'a FfiWaker>,
_marker: PhantomData<&'a FfiWakerBase>,
}

impl<'a> FfiContext<'a> {
/// SAFETY: VTable functions of `waker` are unsafe, the caller must ensure they have a
/// sane behavior as a Waker. `with_context` relies on this to be safe.
unsafe fn new(waker: &'a FfiWaker) -> Self {
Self {
waker_ref: waker as *const _ as *const FfiWaker,
phantom: PhantomData,
waker: waker as *const FfiWaker as *const FfiWakerBase,
_marker: PhantomData,
}
}

Expand All @@ -155,30 +155,30 @@ impl<'a> FfiContext<'a> {
static RUST_WAKER_VTABLE: RawWakerVTable = {
unsafe fn clone(data: *const ()) -> RawWaker {
let waker = data.cast::<FfiWaker>();
let cloned = ((*waker).vtable.clone)(waker);
let cloned = ((*(*waker).base.vtable).clone)(waker.cast());
RawWaker::new(cloned.cast(), &RUST_WAKER_VTABLE)
}
unsafe fn wake(data: *const ()) {
let waker = data.cast::<FfiWaker>();
((*waker).vtable.wake)(waker);
((*(*waker).base.vtable).wake)(waker.cast());
}
unsafe fn wake_by_ref(data: *const ()) {
let waker = data.cast::<FfiWaker>();
((*waker).vtable.wake_by_ref)(waker);
((*(*waker).base.vtable).wake_by_ref)(waker.cast());
}
unsafe fn drop(data: *const ()) {
let waker = data.cast::<FfiWaker>();
((*waker).vtable.drop)(waker);
((*(*waker).base.vtable).drop)(waker.cast());
}
RawWakerVTable::new(clone, wake, wake_by_ref, drop)
};

// SAFETY: `waker_ref`'s vtable functions must have behavior, this is the contract of
// SAFETY: `waker`'s vtable functions must have behavior, this is the contract of
// `FfiContext::new`.
let waker = unsafe {
// The `waker_ref` is borrowed from external context. We must not call drop on it.
// The waker reference is borrowed from external context. We must not call drop on it.
ManuallyDrop::new(Waker::from_raw(RawWaker::new(
self.waker_ref.cast(),
self.waker.cast(),
&RUST_WAKER_VTABLE,
)))
};
Expand All @@ -201,11 +201,14 @@ pub trait ContextExt {
impl<'a> ContextExt for Context<'a> {
fn with_ffi_context<T, F: FnOnce(&mut FfiContext) -> T>(&mut self, closure: F) -> T {
static C_WAKER_VTABLE_OWNED: FfiWakerVTable = {
unsafe extern "C" fn clone(data: *const FfiWaker) -> *const FfiWaker {
unsafe extern "C" fn clone(data: *const FfiWakerBase) -> *const FfiWakerBase {
DropBomb::with("Waker::clone", || {
let data = data as *mut FfiWaker;
let waker: Waker = (*(*data).waker.owned).clone();
Box::into_raw(Box::new(FfiWaker {
vtable: &C_WAKER_VTABLE_OWNED,
base: FfiWakerBase {
vtable: &C_WAKER_VTABLE_OWNED,
},
waker: WakerUnion {
owned: ManuallyDrop::new(waker),
},
Expand All @@ -215,19 +218,20 @@ impl<'a> ContextExt for Context<'a> {
}
// In this case, we must own `data`. This can only happen when the `data` pointer is returned from `clone`.
// Thus the it is `Box<FfiWaker>`.
unsafe extern "C" fn wake(data: *const FfiWaker) {
unsafe extern "C" fn wake(data: *const FfiWakerBase) {
DropBomb::with("Waker::wake", || {
let b = Box::from_raw(data as *mut FfiWaker);
ManuallyDrop::into_inner(b.waker.owned).wake();
})
}
unsafe extern "C" fn wake_by_ref(data: *const FfiWaker) {
unsafe extern "C" fn wake_by_ref(data: *const FfiWakerBase) {
DropBomb::with("Waker::wake_by_ref", || {
let data = data as *mut FfiWaker;
(*data).waker.owned.wake_by_ref();
})
}
// Same as `wake`.
unsafe extern "C" fn drop(data: *const FfiWaker) {
unsafe extern "C" fn drop(data: *const FfiWakerBase) {
DropBomb::with("Waker::drop", || {
let mut b = Box::from_raw(data as *mut FfiWaker);
ManuallyDrop::drop(&mut b.waker.owned);
Expand All @@ -243,24 +247,28 @@ impl<'a> ContextExt for Context<'a> {
};

static C_WAKER_VTABLE_REF: FfiWakerVTable = {
unsafe extern "C" fn clone(data: *const FfiWaker) -> *const FfiWaker {
unsafe extern "C" fn clone(data: *const FfiWakerBase) -> *const FfiWakerBase {
DropBomb::with("Waker::clone", || {
let data = data as *mut FfiWaker;
let waker: Waker = (*(*data).waker.reference).clone();
Box::into_raw(Box::new(FfiWaker {
vtable: &C_WAKER_VTABLE_OWNED,
base: FfiWakerBase {
vtable: &C_WAKER_VTABLE_OWNED,
},
waker: WakerUnion {
owned: ManuallyDrop::new(waker),
},
}))
.cast()
})
}
unsafe extern "C" fn wake_by_ref(data: *const FfiWaker) {
unsafe extern "C" fn wake_by_ref(data: *const FfiWakerBase) {
DropBomb::with("Waker::wake_by_ref", || {
let data = data as *mut FfiWaker;
(*(*data).waker.reference).wake_by_ref();
})
}
unsafe extern "C" fn unreachable(_: *const FfiWaker) {
unsafe extern "C" fn unreachable(_: *const FfiWakerBase) {
std::process::abort();
}
FfiWakerVTable {
Expand All @@ -272,7 +280,9 @@ impl<'a> ContextExt for Context<'a> {
};

let waker = FfiWaker {
vtable: &C_WAKER_VTABLE_REF,
base: FfiWakerBase {
vtable: &C_WAKER_VTABLE_REF,
},
waker: WakerUnion {
reference: self.waker(),
},
Expand All @@ -287,9 +297,17 @@ impl<'a> ContextExt for Context<'a> {
}

// Inspired by Gary Guo (github.com/nbdd0121)
//
// The base is what can be accessed through FFI, and the regular struct contains
// internal data (the original waker).
#[repr(C)]
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
struct FfiWakerBase {
vtable: *const FfiWakerVTable,
}
#[repr(C)]
struct FfiWaker {
vtable: &'static FfiWakerVTable,
base: FfiWakerBase,
waker: WakerUnion,
}

Expand All @@ -302,17 +320,19 @@ union WakerUnion {

#[derive(PartialEq, Eq, Hash, Clone, Copy)]
#[repr(C)]
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
struct FfiWakerVTable {
clone: unsafe extern "C" fn(*const FfiWaker) -> *const FfiWaker,
wake: unsafe extern "C" fn(*const FfiWaker),
wake_by_ref: unsafe extern "C" fn(*const FfiWaker),
drop: unsafe extern "C" fn(*const FfiWaker),
clone: unsafe extern "C" fn(*const FfiWakerBase) -> *const FfiWakerBase,
wake: unsafe extern "C" fn(*const FfiWakerBase),
wake_by_ref: unsafe extern "C" fn(*const FfiWakerBase),
drop: unsafe extern "C" fn(*const FfiWakerBase),
}

/// The FFI compatible future type with `Send` bound.
///
/// See [module level documentation](index.html) for more details.
#[repr(transparent)]
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
pub struct BorrowingFfiFuture<'a, T>(LocalBorrowingFfiFuture<'a, T>);

/// The FFI compatible future type with `Send` bound and `'static` lifetime,
Expand Down Expand Up @@ -427,9 +447,10 @@ impl<T> Future for BorrowingFfiFuture<'_, T> {
///
/// See [module level documentation](index.html) for more details.
#[repr(C)]
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
pub struct LocalBorrowingFfiFuture<'a, T> {
fut_ptr: *mut (),
poll_fn: PollFn<T>,
poll_fn: unsafe extern "C" fn(fut_ptr: *mut (), context_ptr: *mut FfiContext) -> FfiPoll<T>,
drop_fn: unsafe extern "C" fn(*mut ()),
_marker: PhantomData<&'a ()>,
}
Expand Down