-
Notifications
You must be signed in to change notification settings - Fork 110
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
Brainstorming: what could a Future
-based libtock-rs
look like?
#494
Comments
Idea:
|
@jrvanwhy as far as I know, that is generally sound. I have one soundness concern: shouldn't it be keeping track of whether it's currently The issue with the previous
By making the buffer owned by the pinned object, this guarantees that if the future is forgotten and the unallow never occurs, the buffer also remains inaccessible by surrounding Rust code and exclusively accessed by the kernel or subscribe callbacks. @ComputerDruid does this sound right to you? I'm skeptical that an API that requires all buffers to be wrapped in a |
Double-unallow isn't a soundness issue. Unless some other code had re-allowed a buffer between the two un-allow calls, the second un-allow will just be a no-op. If some other code had re-allowed a buffer, then the second un-allow will just revoke the kernel's access to that other buffer. However, the tests I've done so far (which are very limited) showed that tracking whether the buffer is currently allowed reduces code size, as it can allow the compiler to optimize the second unallow away.
I think there's a path forward that allows the closure-based API and the // Safety invariant: buffer must be safe to share with the kernel for 'buffer.
pub struct AllowRwRef<'buffer> {
buffer: *mut [u8],
// PhantomData goes here.
}
impl<'buffer> AllowRwRef<'buffer> {
pub fn from_closure(buffer: &'buffer mut [u8],
handle: Handle<AllowRw<'buffer, _, _, _>>) -> Self { ... }
pub fn from_pin(allow_rw_buffer: Pin<&'buffer mut AllowRwBuffer>) -> Self { ... }
// Allow and un-allow methods go here, signatures TBD.
} There definitely are some open questions about the design, though. How would it track whether the buffer has been allowed to allow for the optimization I mentioned above? We could add a reference to the is-allowed flag to AllowRwRef, but then AllowRwRef would take two registers to pass into a function. We could put the is-allowed flag into a static and use generics to pass in a type that knows how to find the static, which avoids that overhead, but that's complex/messy and it may be difficult to avoid monomorphization bloat. Oh, and because of #340 (comment), we'll probably want to keep the closure-based API for Subscribe anyway. A |
Here's a Zulip thread discussing many of the same ideas a few years earlier (i.e. something like DMA on an unowned buffer has to be callback-based and can't really use |
This week's discussions have convinced me to take another look at how
libtock-rs
could integrate with the futures ecosystem. Unfortunately, I'm having a hard time seeing how that could work. I decided to open this issue to gather ideas.For example: what should the async equivalent of this API look like?
One idea is to return a type that implements
Future
which completes when the read is finished:However, I don't see a sound way to implement that API. If
read()
calls ReadWriteAllow, then the following invocation causes a use-after-free:We could delay sharing
buffer
with the kernel until the returnedFuture
is polled, but this is unsound as well:I can think of other designs that are probably sound, but impractical and/or unergonomic:
Any other ideas on how this could work?
The text was updated successfully, but these errors were encountered: