I'm implementing AsyncRead for a struct, that should decompress data.
I'm using pin project and my problem is, that whenever I call let this = self.project(), it consumes self and whenever I call this.reader.poll_read(..) afterwards, it consumes this.reader so I can't poll again and I can't project again, but I need to call it multiple times, in order to discover how many bytes need to be read next.
just returning with Ok is not possible, because per definition if poll_read(..) doesn't fill at least one byte it means EOF, but in our case we can't fill one byte, but need to read more bytes from the reader.
returning with Pending has to wake the task again, but we actually need to run it again immediately.
So I though of calling the waker and returning Pending, should run my poll_read(..) again.
cx.waker().wake_by_ref(); // call me again?
return Poll::Pending;
That actually works, but is it really the correct way?
Sample code from the poll_read(..):
#[pin_project]
pub struct AsyncDecoder<R> {
#[pin]
reader: R,
state: DecoderState,
}
impl<R> AsyncRead for AsyncDecoder<R> where R: AsyncRead {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let this = self.project();
// ..
// 1. read file header (4 bytes)
this.reader.poll_read(cx, ..)?;
// ..
// 2. read block header (n bytes)
this.reader.poll_read(cx, ..)?;
// ..
// 3. read compressed data block (n bytes)
this.reader.poll_read(cx, ..)?;
// ..
// repeat 2. + 3. until EOF
}
}
Using the waker works but adds unnecessary overhead. The way to replicate a Pin<&mut T> is by using Pin::as_mut():
impl<R> AsyncRead for AsyncDecoder<R>
where
R: AsyncRead,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
// let this = self.project();
// ..
// 1. read file header (4 bytes)
self.as_mut().project().reader.poll_read(cx, ...)?;
// ..
// 2. read block header (n bytes)
self.as_mut().project().reader.poll_read(cx, ...)?;
// ..
// 3. read compressed data block (n bytes)
self.project().reader.poll_read(cx, ...)?;
// ..
// repeat 2. + 3. until EOF
}
}
Yes, it should be fine.
An async runtime has to support the cx being woken before a poller returning, because the cx may have been sent to another thread that will have no idea whether or not the original thread has returned or not.
Related
I'm using a copy of the stdlib's raw vec to build my own data structure. I'd like to reaad a chunk of a file directly into my data structure (no extra copies). RawVec has a *const u8 as it's underlying storage and I'd like to read the file directly into that.
// Goal:
// Takes a file, a pointer to read bytes into, a number of free bytes # that pointer
// and returns the number of bytes read
fn read_into_ptr(file: &mut File, ptr: *mut u8, free_space: usize) -> usize {
// read up to free_space bytes into ptr
todo!()
}
// What I have now requires an extra copy. First I read into my read buffer
// Then move copy into my destination where I actually want to data.
// How can I remove this extra copy?
fn read_into_ptr(file: &mut File, ptr: *mut u8, read_buf: &mut[u8; 4096]) -> usize {
let num_bytes = file.read(read_buf).unwrap();
unsafe {
ptr::copy_nonoverlapping(...)
}
num_bytes
}
``
Create a slice from the pointer, and read into it:
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, free_space) };
file.read(slice).unwrap()
I met this problem while implementing AsyncRead over a synchronized read to adjust to the async world in Rust.
The sync read implementation I'm handling is a wrapper over a raw C sync implementation, much like the std::fs::File::read; therefore I would use std::io::Read for simplicity hereafter.
Here's the code:
use futures::{AsyncRead, Future};
use std::task::{Context, Poll};
use std::pin::Pin;
use tokio::task;
use std::fs::File;
use std::io::Read;
use std::io::Result;
struct FileAsyncRead {
path: String
}
impl AsyncRead for FileAsyncRead {
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>> {
let path = self.path.to_owned();
let buf_len = buf.len();
let mut handle = task::spawn_blocking(move || {
let mut vec = vec![0u8; buf_len];
let mut file = File::open(path).unwrap();
let len = file.read(vec.as_mut_slice());
(vec, len)
});
match Pin::new(&mut handle).poll(cx) {
Poll::Ready(l) => {
let v_l = l.unwrap();
let _c_l = v_l.0.as_slice().read(buf);
Poll::Ready(v_l.1)
}
Poll::Pending => Poll::Pending
}
}
}
The current implementation is creating a new vector of the same size with the outer buf: &mut [u8] each time because of :
`buf` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
buf: &mut [u8],
| --------- this data with an anonymous lifetime `'_`...
My question is:
Is that possible to avoid the vector creation in spwan_blocking and mutate the buf in poll_read? To avoid vector allocation as well as copying?
Is there a better way to express this "wrapper" logic instead of spawn_blocking as well as Pin::new(&mut handle).poll(cx)? What's the more idiomatic way to do this in Rust?
Something is odd about this code:
If this code is called once, it will likely return Poll::Pending, because spawn_blocking takes time to even start a task.
If this is called multiple times, then it creates multiple unrelated tasks reading the same part of the file and potentially ignoring the result due to (1), which is probably not what you want.
What you could do to fix this is to remember the task inside the FileAsyncRead struct first time you create it, and then on the next call only start a new task if needed, and poll the existing task.
With this API you have it doesn't seem possible to avoid double buffering, because since your API is blocking, and the ReadBuf buffer is not shared, you need to do a blocking read into some other buffer, and then copy the data over when a new non-blocking call poll_read() arrives.
Rust's future uses poll_read (poll_read) to poll for available data:
fn poll_read(
&mut self,
cx: &mut Context,
buf: &mut [u8]
) -> Result<Async<usize>, Error>
Obviously, when called, the poll_read function fills buf with data and returns Poll::Ready(n) where n is the quantity written to the buffer. If there's no data available at the moment, it returns Poll::Pending. The future caller can poll_read again, or not. In the case where it does not poll again, it is giving the poll_read function the chance to tell when it has to be polled. It does that by calling cx.waker().wake() when there is available data.
So, for example, I can implement poll_read for my struct:
fn poll_read(
&mut self,
cx: &mut Context,
buf: &mut [u8]
) -> Result<Async<usize>, Error> {
let my_buffer = get_buffer_from_internet();
if my_buffer.len() <= buf.capacity() {
buf.put_slice(my_buffer);
return Poll::Ready(my_buffer.len());
} else {
//we can only fill buf partially
buf.put_slice(my_buffer);
//let's store the remaining in our struct
self.remaining.store(&my_buffer[buf.len()..]);
//How can I make the future caller know it needs to call poll_read again to read the remaining data? Can I call cx.waker().wake() while inside the poll_read?
return Poll::Ready(buf.len());
}
}
you see that in the case where there was no sufficient space on buf, I copied just the needed data and then stored the remaining on our struct. So we can enhance this poll_read by making it check if there is remaining data to be written, before getting new data from the internet. But as you see in my comment: How can I make the future caller know it needs to call poll_read again to read the remaining data? Can I call cx.waker().wake() while inside the poll_read?
You do not need to do anything. The documentation reads:
If no data is available for reading, the method returns Poll::Pending and arranges for the current task to receive a notification when the object becomes readable or is closed.
It does not need to arrange to be woken up if it returns data. The implementation for Cursor<T>, for example, just defers to io::Read and ignores the Context entirely, regardless whether the buffer is big enough or not.
The poller should know that there still may be data to read until poll_read returns Poll::Ready(Ok(0)).
My use case involves two mutable vectors a and b and a usize parameter x. I want to make the following change:
take the elements b[0..x] and append them to the end of a (changing capacity of a as required)
transform b into b[x..], without changing the original capacity of b
Currently I do the following:
while a.len() < x && !b.is_empty() {
a.push(b.pop_front().unwrap());
// here `b` is a VecDeque but I am happy to use a Vec if pop_front was not required
}
Obviously this seems a very slow operation, checking two conditions and calling unwrap at every iteration. It would be great if there was a rev_split_off operation such that:
let mut front = b.rev_split_off(x);
a.append(&mut front);
Here rev_split_off returns a newly allocated vector for the slice b[0..x] and transforms b into the remaining slice with unchanged capacity.
Question: How to perform my use case efficiently, with or without using such a thing as rev_split_off?
Well, I think you will have to implement the rev_split_off yourself (even though I would probably call it split_off_back but it's the same).
Here is how I would implement it:
/// Moves the `i` first elements of `vec` at the end of `buffer`.
fn split_off_back<T>(vec: &mut Vec<T>, i: usize, buffer: &mut Vec<T>) {
// We have to make sure vec has enough elements.
// You could make the function unsafe and ask the caller to ensure
// this condition.
assert!(vec.len() >= i);
// Reserve enough memory in the target buffer
buffer.reserve(i);
// Now we know `buffer.capacity() >= buffer.len() + i`.
unsafe {
// SAFETY:
// * `vec` and `buffer` are two distinct vectors (they come from mutable references)
// so their allocations cannot overlap.
// * `vec` is valid for reads because we have an exclusive reference to it and we
// checked the value of `i`.
// * `buffer` is valid for writes because we ensured we had enough memory to store
// `i` additional elements.
std::ptr::copy_nonoverlapping(vec.as_ptr(), buffer.as_mut_ptr().add(buffer.len()), i);
// Now the memory is moved.
// we are not allowed to use it again from the `vec` vector.
// We just extanded `buffer`, we need to update its length.
// SAFEFY:
// * We ensured that the new length is less than the capacity (with `Vec::reserved`)
// * The vector is initialized for this new length (we moved the values).
buffer.set_len(buffer.len() + i);
// Now we need to update the first vector. The values from index `i` to its end
// need to be moved at the begining of the vector.
// SAFETY:
// * We have an exclusive reference to the vector. It is both valid for reads and writes.
std::ptr::copy(vec.as_ptr().add(i), vec.as_mut_ptr(), i);
// And update the length of `vec`.
// SAFETY: This subtraction is safe because we previously checked that `vec.len() >= i`.
vec.set_len(vec.len() - i);
}
}
Note that I put buffer in the parameters of the function to avoid allocating a vector. If you want the same semantic as split_off, you can just do the following.
fn split_of_back<T>(vec: &mut Vec<T>, i: usize) -> Vec<T> {
assert!(vec.len() >= i);
let mut buffer = Vec::with_capacity(i);
unsafe { /* same thing */ }
buffer
}
This is very simple using drain and extend.
a.extend(b.drain(..x));
If your values are Copy, then you can get optimal speed using as_slice. IIUC, using extend_from_slice should be optional due to specialization, but aids clarity.
a.extend_from_slice(b.drain(..x).as_slice());
I have added some benchmarks to show that the unsafe version of this code is significantly faster.
#![feature(test)]
extern crate test;
#[cfg(test)]
mod tests {
extern crate test;
use test::{black_box, Bencher};
/// Moves the `i` first elements of `vec` at the end of `buffer`.
fn split_off_back<T>(vec: &mut Vec<T>, i: usize, buffer: &mut Vec<T>) {
assert!(vec.len() >= i);
buffer.reserve(i);
unsafe {
std::ptr::copy_nonoverlapping(vec.as_ptr(), buffer.as_mut_ptr().add(buffer.len()), i);
buffer.set_len(buffer.len() + i);
std::ptr::copy(vec.as_ptr().add(i), vec.as_mut_ptr(), i);
vec.set_len(vec.len() - i);
}
}
fn split_off_back_two<T>(vec: &mut Vec<T>, i: usize, buffer: &mut Vec<T>) {
buffer.extend(vec.drain(..i));
}
const VEC_SIZE: usize = 100000;
const SPLIT_POINT: usize = 20000;
#[bench]
fn run_v1(b: &mut Bencher) {
b.iter(|| {
let mut a = black_box(vec![0; VEC_SIZE]);
let mut b = black_box(vec![0; VEC_SIZE]);
split_off_back(&mut a, SPLIT_POINT, &mut b);
});
}
#[bench]
fn run_v2(b: &mut Bencher) {
b.iter(|| {
let mut a = black_box(vec![0; VEC_SIZE]);
let mut b = black_box(vec![0; VEC_SIZE]);
split_off_back_two(&mut a, SPLIT_POINT, &mut b);
});
}
}
This is the output of cargo bench on my machine:
running 2 tests
test tests::run_v1 ... bench: 98,863 ns/iter (+/- 2,058)
test tests::run_v2 ... bench: 230,665 ns/iter (+/- 6,093)
test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured; 0 filtered out; finished in 0.48s
This question already has answers here:
How to idiomatically / efficiently pipe data from Read+Seek to Write?
(1 answer)
Why does the usage of by_ref().take() differ between the Iterator and Read traits?
(2 answers)
Closed 4 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
I was trying to write a function that copies a user-specified number of bytes from a reader to a writer, and I came up with this:
fn io_copy(
reader: &mut std::io::Read,
writer: &mut std::io::Write,
byte_count: usize,
) -> std::io::Result<()> {
let mut buffer: [u8; 16384] = unsafe { std::mem::uninitialized() };
let mut remaining = byte_count;
while remaining > 0 {
let to_read = if remaining > 16384 { 16384 } else { remaining };
reader.read_exact(&mut buffer[0..to_read])?;
remaining -= to_read;
writer.write(&buffer[0..to_read])?;
}
Ok(())
}
It works fine, but I wanted to do it without an arbitrarily sized intermediate buffer, and I wondered if such a function already existed. I found std::io::copy, but that copies the whole stream, and I only want to copy a limited amount. I figured I could use take on the reader, but I'm having trouble getting rid of errors. This is what I have so far:
fn io_copy<R>(reader: &mut R, writer: &mut std::io::Write, byte_count: usize) -> std::io::Result<()>
where
R: std::io::Read + Sized,
{
let mut r = reader.by_ref().take(byte_count as u64);
std::io::copy(&mut r, writer)?;
Ok(())
}
This gives me an error:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:6:21
|
6 | let mut r = reader.by_ref().take(byte_count as u64);
| ^^^^^^^^^^^^^^^ cannot move out of borrowed content
I don't understand how to get around this.
I don't think you're going to get much better than that with just the generic Read/Write interfaces (except you probably shouldn't use read_exact in the case where you're ok with filling the entire buffer, that might lead to blocking unnecessarily).
You might be able to specialise it for specific Reads and Writes though, for example you might be able to use Kernel functions (like sendfile when reading from and writing to file descriptors in Linux) which might allow you to avoid having to copy things through userspace unnecessarily.
My implementation of copy_n would look like this (playground)
pub fn copy_n<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W, count: usize) -> io::Result<()>
where R: Read, W: Write
{
let mut buf = vec![];
unsafe {
buf.reserve(count);
buf.set_len(count);
}
reader.read_exact(&mut buf)?;
writer.write_all(&buf)?;
Ok(())
}
This solution just uses read_exact and write_all, which guarantee, that the complete buffer is read/written, or an error occurs, so it should be fine.