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 support for Rust syscalls/descriptors, and implement pipe()
in Rust
#1068
Conversation
Changed the ordering of the C/Rust binding generation to make it more reliable. Renamed the `cbindings` module to `cshadow` since the contents of `cbindings` are actually Rust bindings for C code. Added some addtional documentation.
This helps to prevent circular dependencies between C headers.
c4d5f81
to
46c99c2
Compare
Codecov Report
@@ Coverage Diff @@
## dev #1068 +/- ##
==========================================
- Coverage 55.15% 55.01% -0.15%
==========================================
Files 129 129
Lines 19358 19471 +113
Branches 4611 4645 +34
==========================================
+ Hits 10677 10712 +35
- Misses 5895 5974 +79
+ Partials 2786 2785 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
46c99c2
to
d1390db
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Just some minor nits
@@ -121,17 +121,17 @@ static void _descriptortable_trimIndicesTail(DescriptorTable* table) { | |||
} | |||
} | |||
|
|||
bool descriptortable_remove(DescriptorTable* table, LegacyDescriptor* descriptor) { | |||
bool descriptortable_remove(DescriptorTable* table, int handle) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional - Maybe worth having a stronger type here, to avoid mixing with other such handles? e.g. struct DescriptorHandle { int val; }
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, the code in "descriptor_table.c" uses the name "index" and the code in "process.c" uses the name "handle", but they both mean the same thing (the "fd"). I renamed this back to "index" to make it more clear.
src/main/host/syscall_handler.c
Outdated
@@ -165,6 +165,12 @@ static void _syscallhandler_post_syscall(SysCallHandler* sys, long number, | |||
scr = syscallhandler_##s(sys, args); \ | |||
_syscallhandler_post_syscall(sys, args->number, #s, &scr); \ | |||
break | |||
#define HANDLE_NEW(s) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throughout, is there a more descriptive tag we can use than new
? maybe rust
? e.g. HANDLE_RUST
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is now HANDLE_RUST
.
src/main/host/descriptor/mod.rs
Outdated
|
||
/// An invocation will fail to compile if the provided type is not Send. | ||
#[cfg(test)] | ||
fn verify_send<T: Send>() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice idea. I experimented with this a little and found a way to do it with an extra trait definition instead. I slightly prefer this way since it's purely static, and makes it easier to put it near the type definition in an idiomatic way. I don't feel super strongly about it vs leaving it as-is though.
trait IsSend : Send {}
trait IsSync : Sync {}
struct Foo {}
// Compiles
impl IsSend for Foo {}
impl IsSync for Foo {}
struct Bar {x: *mut i32}
// Doesn't compile
impl IsSend for Bar {}
impl IsSync for Bar {}
if desired we could #[cfg(test)]
-guard the impl
s as well, though I think no code will end up in the final binary anyway. e.g. https://godbolt.org/z/z5n9rr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool! I definitely like this way better.
There was a problem hiding this 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 - a big move in the right direction :) I'm glad we have good testing in place to give us confidence that things are still working given the number of changes here.
src/main/host/descriptor/epoll.c
Outdated
EpollWatchObject watchObject; | ||
|
||
/* for varible scoping */ | ||
if (true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option is to refactor into a small static function, which helps us avoid too much nesting (which makes things easier to read/follow) and also serves as a form of documentation. FWIW, I usually prefer small functions over bare blocks.
src/main/host/syscall_handler.c
Outdated
#define NATIVE(s) \ | ||
case SYS_##s: \ | ||
debug("native syscall %ld " #s, args->number); \ | ||
scr = (SysCallReturn){.state = SYSCALL_NATIVE}; \ | ||
break | ||
|
||
/* Comment out this line to use the C syscall handlers. */ | ||
#define USE_RUST_SYSCALLS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, do we want this as a command line option instead of #define?
Or maybe a better question is, what is the plan for eventually removing the c syscall handlers? Is it just until we feel the rust versions are "stable"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this may be better as the inverse (ex: USE_C_SYSCALLS
) which defaults to unset, and then can be enabled as a gcc flag with -D USE_C_SYSCALLS
.
Right now the Rust syscall handlers still depend on the C syscall handlers if the descriptor is a "legacy descriptor", so some of the C syscall handler code (for example syscallhandler_read
) will have to stay until all of the descriptor types are implemented in Rust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can now be set with a CLI flag. For example, ./setup build --debug --test --clean --use-c-syscalls
.
3bbaa98
to
61c94da
Compare
Thanks for the reviews! I made some comments (and sorry if there were a lot of emails, I also triggered GitHub's abuse prevention system several times) and also fixed/improved a few other things I found. @sporksmith Can you take a look at 85bbb75 and let me know if those changes look good to you? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, other than that I'd still prefer the increment in compatdescriptor_getPosixFile to be a bit more explicit (see inline comment)
Added a new Rust `Descriptor` type and modified the descriptor table to store `CompatDescriptor` objects, which can store either this new Rust descriptor type or the legacy C descriptor type.
Added a new Rust `PosixFile` object which is referenced by `Descriptor` objects. Also added the structure of the C interface that Shadow will later use to integrate this new Rust type with legacy descriptors such as `Epoll`. This interface is defined but not yet implemented.
Rather than indexing only by the fd, the epoll descriptor 'watching' and 'ready' tables now index their entries by a tuple of the fd and a pointer to the object. This is similar to how Linux implements epoll, and will give us more expected behaviour. It is also on the path to adding support for watching Rust descriptor objects.
The `Epoll` descriptor type can now watch for events on both `LegacyDescriptor` objects and `PosixFileArc` objects.
Shadow syscalls can now block on Rust `PosixFile` objects.
01d63c7
to
18bf02a
Compare
This commit implements a `FilePipe` type which supports the `pipe()` syscall completely in Rust.
Use the `--use-c-syscalls` flag to build Shadow without the Rust syscall handlers. For example: ./setup build --debug --test --clean --use-c-syscalls
All flags were being stored in the descriptor object, but only the FD_CLOEXEC should be. The rest should be stored in the posix file object itself. Also improved the comments.
18bf02a
to
59f9846
Compare
Issue: #1033
The goals of this PR are:
The main changes are the following:
Descriptor
type toLegacyDescriptor
.Descriptor
type.CompatDescriptor
objects, which store either aDescriptor
orLegacyDescriptor
.Descriptor
andPosixFile
objects. This allows the descriptor to be duplicated, and simplifies the integration of Rust descriptors and C descriptors.Epoll
descriptors were modified to work withCompatDescriptor
objects rather thanLegacyDescriptor
objects so that they support RustDescriptor
objects as well.Epoll
descriptor's "watching" and "ready" tables now use the (fd, ptr) tuple as the key rather than just the fd. This is similar to how Linux implements epoll, and will be useful once we add support for duplicating file descriptors.PipeFile
type to replace the CChannel
type. This requires implementing thepipe()
,read()
,write()
, andclose()
syscall handlers in Rust. If these Rust syscall handlers are operating on aLegacyDescriptor
, they switch to the C syscall handler instead.src/main/host/syscall_handler.c
allows you to enable/disable the Rust syscall handlers at build time (enabled by default). This might be useful for benchmarking.pipe()
, and writing/reading to/from these pipes. (These tests don't pass using the C syscalls handlers.)This PR is best reviewed by individual commits.