Skip to content
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

Support #[swift_bridge(Sendable)] to implement Rust Send+Sync for Swift Sendable types #269

Open
chinedufn opened this issue Apr 15, 2024 · 1 comment
Labels
good first issue Good for newcomers

Comments

@chinedufn
Copy link
Owner

Right now when generating the Rust representation of a Swift type we create a non-Send and non-Sync type.

#[swift_bridge::bridge]
mod ffi {
    extern "Swift" {
        type SomeSwiftType;
    }
}

// Generated code
// This DOES NOT implement Send or Sync
struct SomeSwiftType(*mut std::ffi::c_void);

If the user knows that SomeSwiftType is Sendable then they should be able to Send + Sync on the Rust side.

#[swift_bridge::bridge]
mod ffi {
    extern "Swift" {
        #[swift_bridge(Sendable)]
        type AnotherSwiftType;
    }
}

// Generated Rust code
struct AnotherSwiftType(std::ffi::c_void);
unsafe impl Send for AnotherSwiftType {}
unsafe impl Sync for AnotherSwiftType {}

Then it would generate Swift code like:

// This would appear once in `SwiftBridgeCore.swift`
func __assert_sendable<__SwiftType: Sendable>() {}

// This is auto-generated.
// It confirms that `AnotherSwiftType` does in fact
// implement Swift's `Sendable` protocol.
func check_AnotherSwiftType_is_Sendable() {
    __assert_sendable::<AnotherSwiftType>()
}
@chinedufn
Copy link
Owner Author

chinedufn commented Apr 15, 2024

Implementation Guide

Check out the contributing guide https://chinedufn.github.io/swift-bridge/contributing/adding-support-for-a-signature/index.html


Here's a pull-request where we implemented support for Swift's Equatable protocol via #[swift_bridge(Equatable)].

It should serve as a guide for implementing #[swift_bridge(Sendable)] support.

#139


unsafe impl Send for MySwiftType {} and unsafe impl Sync for MySwiftType {} would go here

let struct_tokens = quote! {
#[repr(C)]
pub struct #ty_name(*mut std::ffi::c_void);
#impls
impl Drop for #ty_name {
fn drop (&mut self) {
unsafe { #free_mem_func_name(self.0) }
}
}
};
structs_for_swift_classes.push(struct_tokens);

let impl_send_sync = if ty.attributes.sendable {
    quote! {
        unsafe impl Send for #ty_name {}
        unsafe impl Sync for #ty_name {}
    }
} else {
    quote!{}
};

The Swift XCode test can call a Rust function that:

  1. Creates an instance of the Swift type
  2. Sends the instance to another thread

Example of an integration test:

fn test_call_swift_fn_with_owned_opaque_rust_arg() {
let some_rust_type = SomeRustType::new(5);
let counter = some_rust_type.counter.clone();
// Unwrap fails since there is a strong reference to the Rc held in `some_rust_type`
let counter = Rc::try_unwrap(counter).err().unwrap();
ffi::increment_some_owned_opaque_rust_type(some_rust_type, 10);
// Unwrap succeeds since Swift freed the owned `some_rust_type` along with it's Rc at the end of
// the `increment_some_rust_type` function.
let counter = Rc::try_unwrap(counter).unwrap();
assert_eq!(counter.take(), 15);
}

func testRustFnCallsWithFnWithOwnedOpaqueArg() throws {
test_call_swift_fn_with_owned_opaque_rust_arg()
}


Here's where SwiftBridgeCore.swift is generated

let core_swift_out = out_dir.join("SwiftBridgeCore.swift");
let mut swift = core_swift();
swift += "\n";
swift += &RUST_STRING_SWIFT;
swift += "\n";
swift += &SWIFT_CALLBACK_SUPPORT_NO_ARGS_NO_RETURN;
swift += "\n";
swift += &SWIFT_RUST_RESULT;
swift += "\n";
swift += &swift_option_primitive_support();
std::fs::write(core_swift_out, swift).unwrap();

@chinedufn chinedufn added the good first issue Good for newcomers label Apr 15, 2024
@chinedufn chinedufn changed the title Support implementing Rust Send+Sync on Swift Sendable types Support #[swift_bridge(Sendable)] to implement Rust Send+Sync for Swift Sendable types Apr 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

1 participant