Skip to content

Commit

Permalink
Add unstable-c-unwind to block2
Browse files Browse the repository at this point in the history
Improves the situation in #539.
  • Loading branch information
madsmtm committed Jun 2, 2024
1 parent bbf81ac commit fc7e6e2
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 33 deletions.
3 changes: 3 additions & 0 deletions crates/block2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased - YYYY-MM-DD

### Added
* Added `unstable-c-unwind` feature flag.


## 0.5.1 - 2024-05-21

Expand Down
8 changes: 8 additions & 0 deletions crates/block2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ unstable-winobjc = ["gnustep-1-8"]
# Link to ObjFW.
unstable-objfw = []

# Uses `extern "C-unwind"` on relevant function declarations.
#
# This raises MSRV to `1.71`.
#
# Warning: Enabling this is a breaking change for consumer crates, as it
# changes the signature of functions.
unstable-c-unwind = []

# Expose private ffi functions and statics.
unstable-private = []

Expand Down
12 changes: 7 additions & 5 deletions crates/block2/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ pub struct BlockHeader {
/// If the BLOCK_USE_SRET & BLOCK_HAS_SIGNATURE flag is set, there is an
/// additional hidden parameter, which is a pointer to the space on the
/// stack allocated to hold the return value.
pub invoke: Option<unsafe extern "C" fn()>,
pub invoke: Option<crate::__c_unwind!(unsafe extern "C" fn())>,
/// The block's descriptor.
pub(crate) descriptor: BlockDescriptorPtr,
}
Expand Down Expand Up @@ -257,12 +257,13 @@ pub(crate) struct BlockDescriptorCopyDispose {
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) copy: Option<unsafe extern "C" fn(dst: *mut c_void, src: *const c_void)>,
pub(crate) copy:
Option<crate::__c_unwind!(unsafe extern "C" fn(dst: *mut c_void, src: *const c_void))>,
/// Helper to destroy the block after being copied.
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) dispose: Option<unsafe extern "C" fn(src: *mut c_void)>,
pub(crate) dispose: Option<crate::__c_unwind!(unsafe extern "C" fn(src: *mut c_void))>,
}

/// Block descriptor that has an encoding / a signature.
Expand Down Expand Up @@ -302,12 +303,13 @@ pub(crate) struct BlockDescriptorCopyDisposeSignature {
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) copy: Option<unsafe extern "C" fn(dst: *mut c_void, src: *const c_void)>,
pub(crate) copy:
Option<crate::__c_unwind!(unsafe extern "C" fn(dst: *mut c_void, src: *const c_void))>,
/// Helper to destroy the block after being copied.
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) dispose: Option<unsafe extern "C" fn(src: *mut c_void)>,
pub(crate) dispose: Option<crate::__c_unwind!(unsafe extern "C" fn(src: *mut c_void))>,

/// Objective-C type encoding of the block.
#[doc(alias = "signature")]
Expand Down
66 changes: 58 additions & 8 deletions crates/block2/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ use core::ffi::c_void;
use core::marker::{PhantomData, PhantomPinned};
use std::os::raw::c_int;

#[cfg(not(feature = "unstable-c-unwind"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __c_unwind {
(unsafe extern "C" $($t:tt)*) => {
unsafe extern "C" $($t)*
};
(extern "C" $($t:tt)*) => {
extern "C" $($t)*
};
}

#[cfg(feature = "unstable-c-unwind")]
#[doc(hidden)]
#[macro_export]
macro_rules! __c_unwind {
(unsafe extern "C" $($t:tt)*) => {
unsafe extern "C-unwind" $($t)*
};
(extern "C" $($t:tt)*) => {
extern "C-unwind" $($t)*
};
}

/// Type for block class ISAs.
///
/// This will likely become an extern type in the future.
Expand All @@ -31,9 +55,26 @@ pub struct Class {
_opaque: UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>,
}

// TODO: Use `extern "C-unwind"` when in MSRV (because the runtime functions
// may call external routines).
extern "C" {
#[cfg(not(feature = "unstable-c-unwind"))]
macro_rules! extern_c_unwind {
($($t:tt)*) => {
extern "C" {
$($t)*
}
};
}

#[cfg(feature = "unstable-c-unwind")]
macro_rules! extern_c_unwind {
($($t:tt)*) => {
extern "C-unwind" {
$($t)*
}
};
}

// Use `extern "C-unwind"`, runtime functions may call external routines.
extern_c_unwind! {
/// Class ISA used for global blocks.
pub static _NSConcreteGlobalBlock: Class;

Expand Down Expand Up @@ -85,7 +126,7 @@ pub mod private {
#[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
use std::os::raw::c_ulong;

extern "C" {
extern_c_unwind! {
pub static _NSConcreteMallocBlock: Class;
#[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
pub static _NSConcreteAutoBlock: Class;
Expand Down Expand Up @@ -160,12 +201,21 @@ mod tests {
println!("{:?}", unsafe {
ptr::addr_of!(private::_NSConcreteMallocBlock)
});
println!("{:p}", _Block_copy as unsafe extern "C" fn(_) -> _);
println!(
"{:p}",
_Block_object_assign as unsafe extern "C" fn(_, _, _)
_Block_copy as __c_unwind!(unsafe extern "C" fn(_) -> _)
);
println!(
"{:p}",
_Block_object_assign as __c_unwind!(unsafe extern "C" fn(_, _, _))
);
println!(
"{:p}",
_Block_object_dispose as __c_unwind!(unsafe extern "C" fn(_, _))
);
println!(
"{:p}",
_Block_release as __c_unwind!(unsafe extern "C" fn(_))
);
println!("{:p}", _Block_object_dispose as unsafe extern "C" fn(_, _));
println!("{:p}", _Block_release as unsafe extern "C" fn(_));
}
}
11 changes: 7 additions & 4 deletions crates/block2/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,17 @@ macro_rules! global_block {
let mut header = $crate::GlobalBlock::<dyn Fn($($t),*) $(-> $r)? + 'static>::__DEFAULT_HEADER;
header.isa = ::core::ptr::addr_of!($crate::ffi::_NSConcreteGlobalBlock);
header.invoke = ::core::option::Option::Some({
unsafe extern "C" fn inner(_: *mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)? {
$crate::__c_unwind!(unsafe extern "C" fn inner(
_: *mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>,
$($a: $t),*
) $(-> $r)? {
$body
}
});

// TODO: SAFETY
::core::mem::transmute::<
unsafe extern "C" fn(*mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)?,
unsafe extern "C" fn(),
$crate::__c_unwind!(unsafe extern "C" fn(*mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)?),
$crate::__c_unwind!(unsafe extern "C" fn()),
>(inner)
});
$crate::GlobalBlock::from_header(header)
Expand Down
16 changes: 8 additions & 8 deletions crates/block2/src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
/// the heap in `_Block_copy` operations.
const SIZE: c_ulong = mem::size_of::<Self>() as _;

/// Drop the closure that this block contains.
unsafe extern "C" fn drop_closure(block: *mut c_void) {
// Drop the closure that this block contains.
crate::__c_unwind! {unsafe extern "C" fn drop_closure(block: *mut c_void) {
let block: *mut Self = block.cast();
// When this function is called, the block no longer lives on the
// stack, it has been moved to the heap as part of some `_Block_copy`
Expand All @@ -97,7 +97,7 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
// part of some `_Block_copy` operation, and as such it is valid to
// drop here.
unsafe { ptr::drop_in_place(closure) };
}
}}

const DESCRIPTOR_BASIC: BlockDescriptor = BlockDescriptor {
reserved: 0,
Expand All @@ -107,8 +107,8 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {

// `StackBlock::new`
impl<'f, A, R, Closure: Clone> StackBlock<'f, A, R, Closure> {
/// Clone the closure from one block to another.
unsafe extern "C" fn clone_closure(dst: *mut c_void, src: *const c_void) {
// Clone the closure from one block to another.
crate::__c_unwind! {unsafe extern "C" fn clone_closure(dst: *mut c_void, src: *const c_void) {
let dst: *mut Self = dst.cast();
let src: *const Self = src.cast();
// When this function is called as part of some `_Block_copy`
Expand All @@ -132,7 +132,7 @@ impl<'f, A, R, Closure: Clone> StackBlock<'f, A, R, Closure> {
// already `memmove`d data once more, which is unnecessary for closure
// captures that implement `Copy`.
unsafe { ptr::write(dst_closure, src_closure.clone()) };
}
}}

const DESCRIPTOR_WITH_CLONE: BlockDescriptorCopyDispose = BlockDescriptorCopyDispose {
reserved: 0,
Expand Down Expand Up @@ -182,10 +182,10 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {

// `RcBlock::new`
impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
unsafe extern "C" fn empty_clone_closure(_dst: *mut c_void, _src: *const c_void) {
crate::__c_unwind! {unsafe extern "C" fn empty_clone_closure(_dst: *mut c_void, _src: *const c_void) {
// We do nothing, the closure has been `memmove`'d already, and
// ownership will be passed in `RcBlock::new`.
}
}}

const DESCRIPTOR_WITH_DROP: BlockDescriptorCopyDispose = BlockDescriptorCopyDispose {
reserved: 0,
Expand Down
16 changes: 8 additions & 8 deletions crates/block2/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub unsafe trait BlockFn: private::Sealed<Self::Args, Self::Output> {
/// Calls the given invoke function with the block and arguments.
#[doc(hidden)]
unsafe fn __call_block(
invoke: unsafe extern "C" fn(),
invoke: crate::__c_unwind!(unsafe extern "C" fn()),
block: *mut Block<Self>,
args: Self::Args,
) -> Self::Output;
Expand All @@ -60,7 +60,7 @@ where
type Dyn: ?Sized + BlockFn<Args = A, Output = R>;

#[doc(hidden)]
fn __get_invoke_stack_block() -> unsafe extern "C" fn();
fn __get_invoke_stack_block() -> crate::__c_unwind!(unsafe extern "C" fn());
}

macro_rules! impl_traits {
Expand All @@ -77,7 +77,7 @@ macro_rules! impl_traits {

#[inline]
unsafe fn __call_block(
invoke: unsafe extern "C" fn(),
invoke: crate::__c_unwind!(unsafe extern "C" fn()),
block: *mut Block<Self>,
($($a,)*): Self::Args,
) -> Self::Output {
Expand All @@ -99,8 +99,8 @@ macro_rules! impl_traits {
type Dyn = dyn Fn($($t),*) -> R + 'f;

#[inline]
fn __get_invoke_stack_block() -> unsafe extern "C" fn() {
unsafe extern "C" fn invoke<'f, $($t,)* R, Closure>(
fn __get_invoke_stack_block() -> crate::__c_unwind!(unsafe extern "C" fn()) {
crate::__c_unwind!(unsafe extern "C" fn invoke<'f, $($t,)* R, Closure>(
block: *mut StackBlock<'f, ($($t,)*), R, Closure>,
$($a: $t,)*
) -> R
Expand All @@ -109,12 +109,12 @@ macro_rules! impl_traits {
{
let closure = unsafe { &*ptr::addr_of!((*block).closure) };
(closure)($($a),*)
}
});

unsafe {
mem::transmute::<
unsafe extern "C" fn(*mut StackBlock<'f, ($($t,)*), R, Closure>, $($t,)*) -> R,
unsafe extern "C" fn(),
crate::__c_unwind!(unsafe extern "C" fn(*mut StackBlock<'f, ($($t,)*), R, Closure>, $($t,)*) -> R),
crate::__c_unwind!(unsafe extern "C" fn()),
>(invoke)
}
}
Expand Down

0 comments on commit fc7e6e2

Please sign in to comment.