-
Notifications
You must be signed in to change notification settings - Fork 0
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
Language interop #7
Comments
We've also been discussing FFI for the message passing, or using sockets. I experimented with both and found a lot of annoyances with unix sockets:
|
Also, sockets are really byte streams and that's not a super pleasant interface for just trying to pass messages in. I enjoyed using channels a lot more, once I actually got them working (with lots of input from @Lonami). Android startup code (yes i know we should use a Service not an activity) We can see evidence of this running in the logs: The next relevant thing that happens is the button press handler: This is the key part - UI trying to send a message to the async backend. So what does sendMsg do? But we can see that we do send a message - "heyyoooo".
|
Using OnceCell for this is also a bit questionable. lonami suggests maybe we actually want mutex<option>. |
@expectocode While typing this i realised i should really be looking at the primitive types, array of byte |
WRT ignoring arguments, isn't the |
@Lonami The way to take But in any case, I think we should actually pass a byte array from the Kotlin side since we'll want to serialise arbitrary data into these messages |
Alright, I've now figured out how to actually use the Kotlin message contents in sendMsg. Resources that were helpful here were the jni-rs chat room (gitter/matrix), where I found a reference to a project using serialisation a lot: https://github.com/exonum/exonum-java-binding/blob/a6627aa00d10bc8175d68392e9156fbf99f48bfe/exonum-java-binding/core/rust/src/testkit/mod.rs#L79. This led me to jbyteArray and env.convert_byte_array, which made the rest of the work pretty simple. |
Next up is communication going in the other direction. This could work using long polling for receiving - UI continually calls blocking recv() call in a short-lived thread. Sending should be pretty trivial as Rust just needs to put something into a channel. |
credit due, none of this is my design. i'm just implementing it |
We also still need to think about higher-level problems, like how UI makes backend calls that will return data needed for UI updates. example: User taps on a chat participant to see their profile. our solution here is going to have to be heavily informed by the way Jetpack Compose works. |
@expectocode single activity navigation in jetpack, compose-specific information about the above for showing a user profile you'd |
see also https://developer.android.com/jetpack/compose/state on how compose and viewmodels tie together |
I've been thinking about this long and hard, and after studying a few systems this is what I've come up with. Type system, mostly stolen from Thrift? means inclusion is TBD/not an initial goal
Traits and trait objectsThis is the primary mechanism for languages to call into each other. Call it a "Foreign Object Interface" if you will. This is what really sets this idea apart from other RPC systems, where you define singleton services with "static" methods, and references to "remote" objects are not first class.
Other details:
Memory layout of encoded typesThese rules apply for data that's passed over the pipe. Languages that can make direct use of these representations should go ahead and do that, otherwise conversion is required, not a big deal. Hopefully this should at least avoid serializing/marshaling data only to deserialize it immediately, only one conversion should be necessary.
Why roll our own solution?
Anything else?I've probably forgotten something important, feel free to comment and criticize. |
[My responses are in quotes, to deal with GFM not supporting lifting quote levels while maintaining list levels.]
I've been thinking about this long and hard, and after studying a few systems this is what I've come up with. We need an IDL to describe the boundary between both languages. Syntax should be fairly straightforward, can take inspiration from other IDLs and also programming languages in general).
Type system, mostly stolen from Thrift? means inclusion is TBD/not an initial goal
Traits and trait objectsThis is the primary mechanism for languages to call into each other. Call it a "Foreign Object Interface" if you will. This is what really sets this idea apart from other RPC systems, where you define singleton services with "static" methods, and references to "remote" objects are not first class. Traits are basically Rust traits, aka interfaces in Kotlin/Java, though a bit simplified. A trait can define a number of methods, and a language-native struct/class can implement an IDL trait for the other side to own instances of it. Trait object life-cycle:
Other details:
Memory layout of encoded typesThese rules apply for data that's passed over the pipe. Languages that can make direct use of these representations should go ahead and do that, otherwise conversion is required, not a big deal. Hopefully this should at least avoid serializing/marshaling data only to deserialize it immediately, only one conversion should be necessary. This is designed with single process interop or shared memory IPC in mind, rather than sending data over the network. Also we drop deterministic data layout to take advantage of the C ABI instead, because the intended use is for co-developed programs running on the same machine to interface together. Maintaining ABI stability is still feasible but out of scope for now, and we don't have to worry about differences between platforms.
Why roll our own solution?
|
@bb010g has motivated me to look further into it, and it looks like capnproto pretty much does what we need! We may need to mess around with the Rust implementation a bit, and the Java implementation should work to start with (though it's missing any RPC support), and a Kotlin/Multiplatform port shouldn't be too difficult to implement. |
Things I've been looking at:
|
We've been discussing this a lot!
A few things we've been thinking about revolve around how the UI and the async network "backend" should speak to each other.
One way would be to have a lot of FFI methods that the (kotlin) UI can call into the Rust (still on the UI side, maybe in a short-lived thread), which would then push a message onto a queue for the backend to process.
Another way would be to have the UI side serialize the message in Kotlin, send this serialised message to one single Rust method (still on the UI side) that de-serializes the message and pushes a message to the backend as before.
Hand-drawn diagram, legibility not guaranteed.
The text was updated successfully, but these errors were encountered: