-
Notifications
You must be signed in to change notification settings - Fork 109
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 the Allowed type to libtock_platform. #222
Conversation
Allowed is the type that will be returned by Platform::allow() in the successful case. It represents a memory buffer shared with the kernel. It uses raw pointers and volatile writes to perform read/write accesses to the memory to avoid encountering undefined behavior. This is the first step towards fixing tock#129 and tock#143.
The Travis failure is due to #225 and is unrelated to this PR; please review this PR when you get a chance. |
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.
This PR contains code that it is very important to get right, as mistakes could lead to very subtle and hard to trace bugs, so I tried to review it closely. In general, everything seems right to me. However, the comments about dropping Allowed
values after replacing them seemed a little misleading to me.
In general, it almost feels like it might be better to disallow placing anything with a Drop
implementation in an Allowed
type, as it is impossible to know whether drop()
will be called or not. But perhaps I am missing something.
core/platform/src/allows/allowed.rs
Outdated
// Sets the value in the buffer. Note that unlike `core::cell::Cell::set`, | ||
// `Allowed::set` will not drop the existing value. To drop the existing | ||
// value, use `replace` and drop the return value. |
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 tested this by modifying replace()
, and it does seem to be generally true.
#[test]
fn replace() {
let dropped1 = core::cell::Cell::new(false);
let dropped2 = core::cell::Cell::new(false);
let dropped3 = core::cell::Cell::new(false);
let mut buffer = DropCheck {
flag: Some(&dropped1),
value: 1,
};
let (allowed, kernel_ptr) = KernelPtr::allow(&mut buffer);
assert_eq!(kernel_ptr.value(), 1);
assert_eq!(dropped1.get(), false);
// Simulate the kernel replacing the value in buffer. We don't drop the
// existing value.
kernel_ptr.set(DropCheck {
flag: Some(&dropped2),
value: 2,
});
let returned = allowed.replace(DropCheck {
flag: Some(&dropped3),
value: 3,
});
assert_eq!(returned.value, 2);
assert_eq!(kernel_ptr.value(), 3);
assert_eq!(dropped1.get(), false);
drop(returned); // <-- Added by me
assert_eq!(dropped1.get(), false); // <-- added by me
assert_eq!(dropped2.get(), true); // <-- modified by me
assert_eq!(dropped3.get(), false);
}
However, this behavior is definitely somewhat confusing, as the caller of replace cannot know whether the destructor will be called for the item originally inserted from the userspace side, or for the item inserted by the kernel.
It feels like it would be a mistake for any userspace implementation to rely on drop being called for values that have been Allowed
, but I read this comment as suggesting that doing so is possible. Perhaps it should be reworded? Alternatively, perhaps I am misunderstanding?
In principle, I agree. Putting a Another thing we could do is to put a ... or I could remove support for What do you think? |
Hmm...I agree that requiring
After thinking about this some more, I think that it is better to just leave things as-is but to document the potential failure of drops directly on If there were a way to disallow |
That only applies to dynamically-sized slices. Arrays are |
I was imagining that it would be common to share a portion of a userspace array with the Kernel (I had in mind a packet buffer in userspace, but only the portion of the packet that the app actually populates with data gets Outside of arrays/slices, I am not sure whether disallowing non- |
Originally, I build Allowed so it can work with non-Copy types, which allows Drop types. The semantics of storing non-Copy and non-Drop types in shared memory, however, are somewhat nonobvious. Based on code review feedback, and being particularly cautious because Allowed uses `unsafe`, I decided to add a Copy constraint to Allowed.
Because |
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 good!
I believe this PR is ready to merge. |
bors r+ |
Build succeeded: |
Allowed is the type that will be returned by
Platform::allow()
in the successful case. It represents a memory buffer shared with the kernel. It uses raw pointers and volatile writes to perform read/write accesses to the memory to avoid encountering undefined behavior.This is the first step towards fixing #129 and #143.