Skip to content

Pass Fd by reference#1375

Closed
sunfishcode wants to merge 5 commits into
masterfrom
pass-handle-by-ref
Closed

Pass Fd by reference#1375
sunfishcode wants to merge 5 commits into
masterfrom
pass-handle-by-ref

Conversation

@sunfishcode
Copy link
Copy Markdown
Member

This is a draft PR partially illustrating the idea mentioned in #1202 for passing Fd handles by reference.

This provides I/O safety within the WASI implementation code, though it depends on being careful around calling the Fd::from_raw function. It also simplifies the FdPool, by taking advantage of the new EntryTable.

This doesn't build at the moment, because it doesn't change the wiggle code, but it shows how the rest of the code would look.

  • Fd is passed by reference when it's an argument in host code.
  • Fd no longer needs to implement Copy or Clone.
  • File descriptors within structs -- types::SubscriptionU::FdRead and types::SubscriptionU::FdWrite still need to be stored as u32; it can be converted to an Fd by calling the unsafe from_raw

In Wiggle, would it be better to special-case Handle to implement pass-by-reference behavior, or add a new TypePassedBy kind in witx?

@github-actions github-actions Bot added the wasi Issues pertaining to WASI label Mar 20, 2020
@kubkon kubkon force-pushed the pass-handle-by-ref branch from 279b56c to 09c5c44 Compare March 23, 2020 16:24
sunfishcode and others added 3 commits March 23, 2020 18:01
Make handles non-`Copy` and non-`Clone`. Pass them by reference, and
store them instructs as raw `u32` values. And make `Fd::from_raw` unsafe
so that we're careful about where we get handles from.

This also obviates the `Fd` trait. With `EntryTable`, it's now
straightforward to let `FdPool` just operate on `u32` values.
This commit updates `wiggle` to treat handles as non-transparent
types. As a result, if any compound data type includes handle
as a field, it can no longer be `Copy` nor `PartialEq`.

I've also added missing methods to handles `unsafe fn from_raw(u32) -> Self`
and `fn as_raw(&self) -> u32` which replace `From` impls.
@kubkon kubkon force-pushed the pass-handle-by-ref branch from f1f75dd to c8a2b52 Compare March 23, 2020 17:09
@kubkon
Copy link
Copy Markdown
Member

kubkon commented Mar 23, 2020

OK, so the only thing missing is storing handles as part of a struct and passing this struct around. Personally, I think I'm leaning towards the first suggestion of always passing handles by ref.

@kubkon
Copy link
Copy Markdown
Member

kubkon commented Mar 23, 2020

On second thought, this is going to be more tricky than I originally anticipated. wiggle currently is built with types that take an explicit lifetime in mind, so ideally we'd have something like Fd<'a>. In other words, if there was a way for us to hide the ref inside the type, that would be ideal. To make structs hold a ref in wiggle, I guess we'd need to make handles a special-case indeed, and this might perhaps lead to some overly complicated code? I'm just putting it out there. @pchickey @alexcrichton what do y'all reckon?

@alexcrichton
Copy link
Copy Markdown
Member

I think I may be missing the motivation here, it sounds like we have u32 values from wasm, Fd, and Entry. Today Entry is already passed by reference, and this PR looks to be switching Fd, which is currently a thin newtype around u32, to also be passed by reference. Why is Fd being changed to be passed by reference?

I feel like a better eventuality may be something where if you take a handle type in a function it's automatically looked up, e.g. for (type $fd (handle)) that translates to this in the trait:

trait WasiSnapshot {
    fn lookup_fd(&self, idx: u32) -> Result<&Entry>;
}

(or something like that)

And that way APIs don't ever actually deal with Fd or u32, that all happens in the wiggle layer. Even in APIs that return handle we could change those to return Result<Entry> in Rust and that's automatically converted to a file descriptor under the hood.

I guess I'm sort of confused why we want more types passed by reference when we already have a type passed by reference, Entry?

@kubkon
Copy link
Copy Markdown
Member

kubkon commented Mar 23, 2020

Hmm, those are some really good points, thanks @alexcrichton! I too don’t want to make this overly complicated and convoluted, and indeed this somewhat starts to look like it. However, one motivation for handles being non-Copy and non-Clone, and passed by ref, would be to signal that they are something more than just an “index” into a capabilities table. I guess you could think of it as RawFd vs File. Now I’m not saying this is the way to go, but personally I’d welcome it if we managed to come with a wrapper type for handles that could only be passed by reference unless reconstructed using unsafe primitives such as from_raw and as_raw. So that would be the main motivation the way I understand it. Perhaps @sunfishcode could offer some details or his point of view about all this.

@sunfishcode
Copy link
Copy Markdown
Member Author

@alexcrichton I like that idea. If it's feasible to have wiggle generate the get_entry calls, so that the hand-maintained code just gets an &Entry parameter instead of a Fd, that achieves the same goal as passing Fd by reference, but in a simpler way.

@pchickey
Copy link
Copy Markdown
Contributor

pchickey commented Mar 24, 2020

I like the idea of looking up witx handles to get a ref to some other Rust type, and passing that to the trait method. I've been fielding requests for wiggle to be able to convert to and from types defined in the user's code in other contexts. So I wonder if we can come up with a generalized way for wiggle users to specify that a witx type needs to be transformed to/from some externally-defined Rust type before consumption / after production.

The other big use case that has come up is in Fastly's HTTP code, we have a rich (as in thiserror) structured error enum that we want to implement the trait method with. If we return that Err from the trait method, we would like to call another Ctx method to log it appropriately and convert it to a "flat" witx error enum (as in errno). That use case is similar to the GuestErrorType trait we already require. I would like to figure out some way for that mechanism to work for handles, and external enum definitions like Frank has, as well.

This is a pretty complicated design space to figure out so if anyone wants to have a zoom call to chat about it more, lets.

@kubkon
Copy link
Copy Markdown
Member

kubkon commented Mar 24, 2020

This might be a bit more premature to ask, but I guess in a trait suggested by @alexcrichton we'd expect something like this:

trait WasiSnapshot {
    type Entry;
    fn lookup_fd(&self, idx: u32) -> Result<&Self::Entry>;
}

in the sense that we wouldn't tie some specific type to the trait and the lookup method, or would we actually? I'm just trying to get a better sense of how we'd like to achieve this.

@alexcrichton
Copy link
Copy Markdown
Member

That's my rough thinking yeah, although it may not literally work out as such. The idea though would be that each implementation would define what an Entry is, and the methods that operate on all the entries would get that object in the trait. That way lucet/wasmtime could have entirely different representations if they really wanted to (but they both use wasi-common so it likely wouldn't matter)

This may be a bit pie-in-the-sky though, so don't feel like anything needs to be blocked on it.

@github-actions
Copy link
Copy Markdown

Subscribe to Label Action

This issue or pull request has been labeled: "wasi"

Users Subscribed to "wasi"

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

@sunfishcode
Copy link
Copy Markdown
Member Author

Closing this PR to pursue other approaches as discussed above.

@sunfishcode sunfishcode deleted the pass-handle-by-ref branch May 13, 2020 23:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasi Issues pertaining to WASI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants