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

Add Sendable to all async return types in tests #312

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

colinmarc
Copy link

@colinmarc colinmarc commented Jan 27, 2025

Without this fix I get errors like the following under Swift 6:

/home/colinmarc/dev/swift-bridge/integration-tests/Sources/SharedLib/Generated/SharedLib.swift:4159:26: error: sending 'rustFnRetVal' risks causing data races
4157 |     return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation<AsyncResultOkEnum, Error>) in
4158 |         let callback = { rustFnRetVal in
4159 |             continuation.resume(with: rustFnRetVal)
     |                          |- error: sending 'rustFnRetVal' risks causing data races
     |                          `- note: task-isolated 'rustFnRetVal' is passed as a 'sending' parameter; Uses in callee may race with later task-isolated uses
4160 |         }
4161 |

I avoided adding Sendable for RustString since that's being discussed in #296 and elsewhere. Instead, I commented it out and left a TODO.

Fixes #311.

Copy link
Owner

@chinedufn chinedufn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor feedback around documentation and changing a String to a u32, then we can land this

@@ -41,5 +43,7 @@ extension ResultTransparentStruct: Error {}
extension SameEnum: @unchecked Sendable {}
extension SameEnum: Error {}

extension AsyncResultOkEnum: @unchecked Sendable {}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm we'll want to move away from these @unchecked Sendable {}s in favor of enabling swift-bridge to emit Sendable extension.

In the meantime, you can make this @unchecked Sendable safe by replacing the inner String with a primitive such as u32.

enum AsyncResultOkEnum {
NoFields,
UnnamedFields(i32, String),
NamedFields { value: u8 },
}

Let's also leave a TODO in this file to ditch @unchecked Sendable in favor of proper Sendable support. Can link to #269

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference, I think you linked the wrong issue by accident - the relevant one is #150.

func val() -> UInt32 {
self.num
}
}

extension AsyncRustFnReturnStruct: @unchecked Sendable {}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently safe since this struct only contains a primitive

#[swift_bridge(swift_repr = "struct")]
struct AsyncRustFnReturnStruct {
field: u8,
}

But we'll still eventually want to replace all of these @unchecked Sendable with proper swift-bridge support for emitting Sendable impls as described in #269

Comment on lines 16 to 17
// TODO: this is broken because RustString is not Sendable.
// async fn rust_async_reflect_string(string: String) -> String;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TODO describes the problem but not what we should do about it.

Do we need to do research? Do we already know the solution? Is the problem unsolvable? etc.
Let's make it more clear here what the next step is on this TODO.

@colinmarc colinmarc force-pushed the async-test-sendable branch from 48f774b to 859fd09 Compare February 2, 2025 09:55
@colinmarc
Copy link
Author

I think I addressed your comments.

Copy link
Owner

@chinedufn chinedufn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good just one request.

Can you look over #309 (comment) and confirm that that reasoning is sound?

If so, please add the String code back that you commented out in this PR.

Comment on lines +25 to +28
// TODO: this is broken because RustString is not Sendable.
// Work around making String and other opaque types Sendable is tracked
// here: https://github.com/chinedufn/swift-bridge/issues/150
// async fn rust_async_reflect_string(string: String) -> String;
Copy link
Owner

@chinedufn chinedufn Feb 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RustString should be safe to implement Sendable for today, so long as the Swift code does not make multiple copies of the RustString class.

It will be fully safe once we start using ~Copyable to guarantee that the Swift code cannot copy the RustString (copy as in, copy the class RustString, which is a pointer to the underlying Rust std::string::String.).

So, this code can be added back.


Safety claims are here #309 (comment)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uncommenting this line makes it fail to compile on Swift 6, and I think changing core code in this PR would be out of scope, no?

@colinmarc
Copy link
Author

Hm, I don't think RustStr should be made sendable until the work in #155 lands. I would merge this as-is and then follow up reenabling the String-based tests after it does.

@chinedufn
Copy link
Owner

chinedufn commented Feb 27, 2025

We should make it Sendable and, for now, put the onus on the user to maintain the safety rules outlined in the book.
We shouldn't prevent people from returning String's simply because it's possible to misuse a String.
That's unnecessarily limiting.

If users aren't following the book's safety rules then they're going to hit UB regardless of whether we implement Sendable.

If in the future we're able to guarantee safety at compile time, even better. For now, Swift is fairly limited in this regard.


Swift does not yet make it possible for a fully safe FFI boundary. We need to accept that and work with it, rather than wishing that the Swift side had Rust's level of memory safety.

In time we hope that it will get easier and easier to make the Swift side safer and safer.
For now users are responsible for some kinds of memory safety.

@colinmarc
Copy link
Author

Regardless of how you want to proceed on the RustStr: Sendable stuff, that needs a fix before I can uncomment the line here. I think I can proceed on #307 without this, however, as long as I don't port the async tests over to the new harness yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Async test return values aren't Sendable, so tests fail to compile
2 participants