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

Introduce strongly-typed system primitives #1561

Merged
merged 16 commits into from
May 7, 2020

Conversation

kubkon
Copy link
Member

@kubkon kubkon commented Apr 20, 2020

This commit does a lot of reshuffling and even some more. It introduces
strongly-typed system primitives which are: OsFile, OsDir, Stdio,
and OsOther. Those primitives are separate structs now, each implementing
a subset of Handle methods, rather than all being an enumeration of some
supertype such as OsHandle. To summarise the structs:

  • OsFile represents a regular file, and implements fd-ops
    of Handle trait
  • OsDir represents a directory, and primarily implements path-ops, plus
    readdir and some common fd-ops such as fdstat, etc.
  • Stdio represents a stdio handle, and implements a subset of fd-ops
    such as fdstat and read_ and write_vectored calls
  • OsOther currently represents anything else and implements a set similar
    to that implemented by Stdio

This commit is effectively an experiment and an excercise into better
understanding what's going on for each OS resource/type under-the-hood.
It's meant to give us some intuition in order to move on with the idea
of having strongly-typed handles in WASI both in the syscall impl as well
as at the libc level.

Some more minor changes include making OsHandle represent an OS-specific
wrapper for a raw OS handle (Unix fd or Windows handle). Also, since OsDir
is tricky across OSes, we also have a supertype of OsHandle called
OsDirHandle which may store a DIR* stream pointer (mainly BSD). Last but not
least, the Filetype and Rights are now computed when the resource is created,
rather than every time we call Handle::get_file_type and Handle::get_rights.
Finally, in order to facilitate the latter, I've converted EntryRights into
HandleRights and pushed them into each Handle implementor.

@kubkon kubkon requested a review from sunfishcode April 20, 2020 18:48
@github-actions github-actions bot added the wasi Issues pertaining to WASI label Apr 20, 2020
@github-actions
Copy link

Subscribe to Label Action

cc @kubkon

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

Thus the following users have been cc'd because of the following labels:

  • kubkon: wasi

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

Learn more.

@kubkon
Copy link
Member Author

kubkon commented Apr 22, 2020

Hmm, so I reckon this PR is at a stage that's ready for review so I'll mark it as such. Besides the major changes to the way we treat OS handles, there's a couple more things I'm somewhat unclear on:

  • Should OS-specific path, fd accept specific types such as OsFile, OsDir, and std::fs::File for syscalls that are OS handle-independent (e.g., fdstat_get)? This approach is currently presented in this PR, and it makes our intention a bit clearer what handle is expected for this OS-specific syscall to be called on. But then again, should we ever expand on different handle types and make some syscalls that are currently only used by OsFile say, it would be more ergnonomic for the syscalls to simply accept std::fs::File.
  • On the topic of using std::fs::File, the reason we currently have our own type that resembles that of std::fs::File (OsHandle and OsDirHandle) is because this way we can wrap the underlying OS handle (RawFd or RawHandle) in a std::cell::Cell. Interior mutability is required in fd_fdstat_set_flags call on Windows, where adjusting the flags can only be done by reopening the said file (am I correct here @peterhuene?). We could in principle wrap std::fs::File in a std::cell::RefCell but then we are risking a panic somewhere down the line. Perhaps though this risk is worth accepting at the benefit of cleaner codebase?

Anyhow, all thoughts, suggestions are more than welcome!

@kubkon kubkon marked this pull request as ready for review April 22, 2020 08:41
@kubkon kubkon requested a review from alexcrichton April 22, 2020 08:41
@kubkon
Copy link
Member Author

kubkon commented Apr 22, 2020

@alexcrichton even though you might not know all the internals very well, I'd appreciate your opinion on this even if high level!

@kubkon kubkon requested review from pchickey and peterhuene April 22, 2020 08:45
@kubkon kubkon changed the title [WIP]: Introduce strongly-typed system primitives Introduce strongly-typed system primitives Apr 22, 2020
@kubkon
Copy link
Member Author

kubkon commented Apr 22, 2020

OK, so I've gone ahead and done a little bit of refactoring that IMHO should aid readability. Namely, I've delegated defining OsDir struct to OS-specific modules (BSD, Linux, Emscripten, Windows). This way, OsDir can safely re-use OsHandle for raw OS handle storage, and can store some aux data such as an initialized stream ptr in case of BSD. As a result, we can safely
get rid of OsDirHandle which IMHO was causing unnecessary noise and overcomplicating the design. On the other hand, delegating definition of OsDir to OS-specific modules isn't super clean in and of itself either. Perhaps there's a better way of handling this?

@alexcrichton
Copy link
Member

Sorry for the delay, but I think I won't have a ton of time to review this. It sounds reasonable enough but I don't have time to dig into the details right now.

Copy link
Contributor

@pchickey pchickey left a comment

Choose a reason for hiding this comment

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

We just had a chat on zulip about this PR. I wasn't clear on why Stdio and OsOther were distinct types and what exactly was the reasons for their sticking around, versus some unified TTY abstraction.

There's a lot of complexity in src/sys that I haven't fully internalized. Can you add some doc comments about what each of these types are for, and in the cases like OsOther where it exists until we can refactor further, this can just be an explanation of what we need to consider when refactoring further.

crates/wasi-common/src/sys/mod.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/sys/mod.rs Show resolved Hide resolved
crates/wasi-common/src/sys/stdio.rs Outdated Show resolved Hide resolved
@kubkon kubkon requested a review from pchickey April 29, 2020 21:13
Copy link
Contributor

@pchickey pchickey 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!

Copy link
Member

@sunfishcode sunfishcode left a comment

Choose a reason for hiding this comment

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

Overall, this is great! Starting to split handles into different distinct types is a great direction to go in here!

crates/wasi-common/src/sys/unix/stdio.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/sys/unix/stdio.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/sys/stdio.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/sys/stdio.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/sys/stdio.rs Show resolved Hide resolved
crates/wasi-common/src/sys/osother.rs Show resolved Hide resolved
crates/wasi-common/src/sys/unix/oshandle.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/sys/unix/oshandle.rs Show resolved Hide resolved
crates/wasi-common/src/ctx.rs Show resolved Hide resolved
@kubkon kubkon requested a review from sunfishcode May 4, 2020 20:30
@kubkon
Copy link
Member Author

kubkon commented May 5, 2020

@sunfishcode do you think we're go green for merging or would you like to take another look at the PR?

Jakub Konka added 2 commits May 6, 2020 19:46
This commit does a lot of reshuffling and even some more. It introduces
strongly-typed system primitives which are: `OsFile`, `OsDir`, `Stdio`,
and `OsOther`. Those primitives are separate structs now, each implementing
a subset of `Handle` methods, rather than all being an enumeration of some
supertype such as `OsHandle`. To summarise the structs:

* `OsFile` represents a regular file, and implements fd-ops
  of `Handle` trait
* `OsDir` represents a directory, and primarily implements path-ops, plus
  `readdir` and some common fd-ops such as `fdstat`, etc.
* `Stdio` represents a stdio handle, and implements a subset of fd-ops
  such as `fdstat` _and_ `read_` and `write_vectored` calls
* `OsOther` currently represents anything else and implements a set similar
  to that implemented by `Stdio`

This commit is effectively an experiment and an excercise into better
understanding what's going on for each OS resource/type under-the-hood.
It's meant to give us some intuition in order to move on with the idea
of having strongly-typed handles in WASI both in the syscall impl as well
as at the libc level.

Some more minor changes include making `OsHandle` represent an OS-specific
wrapper for a raw OS handle (Unix fd or Windows handle). Also, since `OsDir`
is tricky across OSes, we also have a supertype of `OsHandle` called
`OsDirHandle` which may store a `DIR*` stream pointer (mainly BSD). Last but not
least, the `Filetype` and `Rights` are now computed when the resource is created,
rather than every time we call `Handle::get_file_type` and `Handle::get_rights`.
Finally, in order to facilitate the latter, I've converted `EntryRights` into
`HandleRights` and pushed them into each `Handle` implementor.
Jakub Konka and others added 13 commits May 6, 2020 19:46
This cleans up a lot of repeating boilerplate code todo with
dynamic dispatch.
Delegates defining `OsDir` struct to OS-specific modules (BSD, Linux,
Emscripten, Windows). This way, `OsDir` can safely re-use `OsHandle`
for raw OS handle storage, and can store some aux data such as an
initialized stream ptr in case of BSD. As a result, we can safely
get rid of `OsDirHandle` which IMHO was causing unnecessary noise and
overcomplicating the design. On the other hand, delegating definition
of `OsDir` to OS-specific modules isn't super clean in and of itself
either. Perhaps there's a better way of handling this?
It seems prudent to check if the passed in `File` instance is of
type matching that of the requested WASI filetype. In other words,
we'd like to avoid situations where `OsFile` is created from a
pipe.
Return `EBADF` in `AsFile` in case a `Handle` cannot be made into
a `std::fs::File`.
Also, since `RawOsHandle` on *nix doesn't need interior mutability
wrt the inner raw file descriptor, we can safely swap the `RawFd`
for `File` instance.
self
}

/// Add a preopened directory.
pub fn preopened_dir<P: AsRef<Path>>(&mut self, dir: File, guest_path: P) -> &mut Self {
self.preopens.as_mut().unwrap().push((
guest_path.as_ref().to_owned(),
Box::new(OsHandle::from(dir)),
Box::new(OsDir::try_from(dir).expect("valid OsDir handle")),
Copy link
Member

Choose a reason for hiding this comment

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

This expect could fail if the user passes in the wrong kind of handle? That feels like something where should return a Result and forward the error on, rather than panicking.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, right! I was meant to do this and somehow forgot. Thanks for pointing this one out!

Copy link
Member Author

@kubkon kubkon May 7, 2020

Choose a reason for hiding this comment

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

@sunfishcode Fixed in 36f07cf. So in order not to force public API changes, instead of throwing an error directly at the preopen callsite (either preopened_dir or preopened_virt), the creation of the actual instance is now postponed until we build the context object, much like creation of the stdio handles.

crates/wasi-common/src/sys/mod.rs Show resolved Hide resolved
Copy link
Member

@sunfishcode sunfishcode 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!

@sunfishcode sunfishcode merged commit cbf7cbf into bytecodealliance:master May 7, 2020
peterhuene added a commit to peterhuene/wasmtime that referenced this pull request May 21, 2020
A regression in bytecodealliance#1561 caused "invalid argument" errors when `stdin`, `stdout`,
and `stderr` are given a file handle.

This was missed in Wasmtime CI because previously only a pipe handle was being
used.

It was caught in the .NET implementation CI because it uses file handles for
its WASI tests.

Fixes bytecodealliance#1735.
ericflo pushed a commit to ericflo/wasmtime that referenced this pull request May 28, 2020
A regression in bytecodealliance#1561 caused "invalid argument" errors when `stdin`, `stdout`,
and `stderr` are given a file handle.

This was missed in Wasmtime CI because previously only a pipe handle was being
used.

It was caught in the .NET implementation CI because it uses file handles for
its WASI tests.

Fixes bytecodealliance#1735.
@kubkon kubkon deleted the handle-rights branch June 27, 2020 18:41
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.

5 participants