Skip to content

Commit c3cb780

Browse files
authored
feat: impl Async Read/Write Rent for File (#297)
Signed-off-by: lzzzt <[email protected]>
1 parent 43fba29 commit c3cb780

File tree

19 files changed

+1414
-746
lines changed

19 files changed

+1414
-746
lines changed

monoio/src/buf/io_buf.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,19 @@ where
204204
}
205205
}
206206

207+
unsafe impl<T> IoBuf for std::mem::ManuallyDrop<T>
208+
where
209+
T: IoBuf,
210+
{
211+
fn read_ptr(&self) -> *const u8 {
212+
<T as IoBuf>::read_ptr(self)
213+
}
214+
215+
fn bytes_init(&self) -> usize {
216+
<T as IoBuf>::bytes_init(self)
217+
}
218+
}
219+
207220
/// A mutable `io_uring` compatible buffer.
208221
///
209222
/// The `IoBufMut` trait is implemented by buffer types that can be passed to
@@ -359,6 +372,23 @@ unsafe impl IoBufMut for bytes::BytesMut {
359372
}
360373
}
361374

375+
unsafe impl<T> IoBufMut for std::mem::ManuallyDrop<T>
376+
where
377+
T: IoBufMut,
378+
{
379+
fn write_ptr(&mut self) -> *mut u8 {
380+
<T as IoBufMut>::write_ptr(self)
381+
}
382+
383+
fn bytes_total(&mut self) -> usize {
384+
<T as IoBufMut>::bytes_total(self)
385+
}
386+
387+
unsafe fn set_init(&mut self, pos: usize) {
388+
<T as IoBufMut>::set_init(self, pos)
389+
}
390+
}
391+
362392
fn parse_range(range: impl ops::RangeBounds<usize>, end: usize) -> (usize, usize) {
363393
use core::ops::Bound;
364394

@@ -378,6 +408,8 @@ fn parse_range(range: impl ops::RangeBounds<usize>, end: usize) -> (usize, usize
378408

379409
#[cfg(test)]
380410
mod tests {
411+
use std::mem::ManuallyDrop;
412+
381413
use super::*;
382414

383415
#[test]
@@ -516,4 +548,32 @@ mod tests {
516548
assert_eq!(slice.bytes_init(), 5);
517549
assert_eq!(slice.into_inner().len(), 6);
518550
}
551+
552+
#[test]
553+
fn io_buf_manually_drop() {
554+
let mut buf = Vec::with_capacity(10);
555+
buf.extend_from_slice(b"0123");
556+
let mut buf = ManuallyDrop::new(buf);
557+
let ptr = buf.as_ptr();
558+
559+
assert_eq!(buf.write_ptr(), ptr as _);
560+
assert_eq!(buf.read_ptr(), ptr);
561+
assert_eq!(buf.bytes_init(), 4);
562+
unsafe { buf.set_init(3) };
563+
assert_eq!(buf.bytes_init(), 3);
564+
assert_eq!(buf.bytes_total(), 10);
565+
566+
let slice = buf.slice(1..3);
567+
assert_eq!((slice.begin(), slice.end()), (1, 3));
568+
assert_eq!(slice.read_ptr(), unsafe { ptr.add(1) });
569+
assert_eq!(slice.bytes_init(), 2);
570+
let buf = ManuallyDrop::into_inner(slice.into_inner());
571+
572+
let mut slice = buf.slice_mut(1..8);
573+
assert_eq!((slice.begin(), slice.end()), (1, 8));
574+
assert_eq!(slice.bytes_total(), 7);
575+
unsafe { slice.set_init(5) };
576+
assert_eq!(slice.bytes_init(), 5);
577+
assert_eq!(slice.into_inner().len(), 6);
578+
}
519579
}

monoio/src/driver/op/read.rs

Lines changed: 84 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@ use {crate::syscall_u32, std::os::unix::prelude::AsRawFd};
77
#[cfg(all(windows, any(feature = "legacy", feature = "poll-io")))]
88
use {
99
std::ffi::c_void,
10-
windows_sys::Win32::{
11-
Foundation::TRUE,
12-
Networking::WinSock::{WSAGetLastError, WSARecv, WSAESHUTDOWN},
13-
Storage::FileSystem::{ReadFile, SetFilePointer, FILE_BEGIN, INVALID_SET_FILE_POINTER},
14-
},
10+
windows_sys::Win32::{Foundation::TRUE, Storage::FileSystem::ReadFile},
1511
};
1612

1713
use super::{super::shared_fd::SharedFd, Op, OpAble};
@@ -42,7 +38,18 @@ impl<T: IoBufMut> Op<Read<T>> {
4238
})
4339
}
4440

45-
pub(crate) async fn read(self) -> BufResult<usize, T> {
41+
pub(crate) fn read(fd: SharedFd, buf: T) -> io::Result<Op<Read<T>>> {
42+
Op::submit_with(Read {
43+
fd,
44+
buf,
45+
// Refers to https://docs.rs/io-uring/latest/io_uring/opcode/struct.Write.html.
46+
// If `offset` is set to `-1`, the offset will use (and advance) the file position, like
47+
// the read(2) and write(2) system calls.
48+
offset: -1i64 as u64,
49+
})
50+
}
51+
52+
pub(crate) async fn result(self) -> BufResult<usize, T> {
4653
let complete = self.await;
4754

4855
// Convert the operation result to `usize`
@@ -83,54 +90,69 @@ impl<T: IoBufMut> OpAble for Read<T> {
8390
#[cfg(all(any(feature = "legacy", feature = "poll-io"), unix))]
8491
fn legacy_call(&mut self) -> io::Result<u32> {
8592
let fd = self.fd.as_raw_fd();
86-
let seek_offset = libc::off_t::try_from(self.offset)
87-
.map_err(|_| io::Error::new(io::ErrorKind::Other, "offset too big"))?;
88-
#[cfg(not(target_os = "macos"))]
89-
return syscall_u32!(pread64(
90-
fd,
91-
self.buf.write_ptr() as _,
92-
self.buf.bytes_total(),
93-
seek_offset as _
94-
));
9593

96-
#[cfg(target_os = "macos")]
97-
return syscall_u32!(pread(
98-
fd,
99-
self.buf.write_ptr() as _,
100-
self.buf.bytes_total(),
101-
seek_offset
102-
));
94+
let mut seek_offset = -1;
95+
96+
if -1i64 as u64 != self.offset {
97+
seek_offset = libc::off_t::try_from(self.offset)
98+
.map_err(|_| io::Error::new(io::ErrorKind::Other, "offset too big"))?;
99+
}
100+
101+
if seek_offset == -1 {
102+
syscall_u32!(read(fd, self.buf.write_ptr() as _, self.buf.bytes_total()))
103+
} else {
104+
syscall_u32!(pread(
105+
fd,
106+
self.buf.write_ptr() as _,
107+
self.buf.bytes_total(),
108+
seek_offset as _
109+
))
110+
}
103111
}
104112

105113
#[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))]
106114
fn legacy_call(&mut self) -> io::Result<u32> {
115+
use windows_sys::Win32::{
116+
Foundation::{GetLastError, ERROR_HANDLE_EOF},
117+
System::IO::OVERLAPPED,
118+
};
119+
107120
let fd = self.fd.raw_handle() as _;
108-
let seek_offset = libc::off_t::try_from(self.offset)
109-
.map_err(|_| io::Error::new(io::ErrorKind::Other, "offset too big"))?;
121+
let seek_offset = self.offset;
122+
110123
let mut bytes_read = 0;
111124
let ret = unsafe {
112-
// see https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-setfilepointer
113-
if seek_offset != 0 {
114-
// We use `FILE_BEGIN` because this behavior should be the same with unix syscall
115-
// `pwrite`, which uses the offset from the begin of the file.
116-
let r = SetFilePointer(fd, seek_offset, std::ptr::null_mut(), FILE_BEGIN);
117-
if INVALID_SET_FILE_POINTER == r {
118-
return Err(io::Error::last_os_error());
119-
}
120-
}
121125
// see https://learn.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-readfile
122-
ReadFile(
123-
fd,
124-
self.buf.write_ptr().cast::<c_void>(),
125-
self.buf.bytes_total() as u32,
126-
&mut bytes_read,
127-
std::ptr::null_mut(),
128-
)
126+
if seek_offset as i64 != -1 {
127+
let mut overlapped: OVERLAPPED = std::mem::zeroed();
128+
overlapped.Anonymous.Anonymous.Offset = seek_offset as u32; // Lower 32 bits of the offset
129+
overlapped.Anonymous.Anonymous.OffsetHigh = (seek_offset >> 32) as u32; // Higher 32 bits of the offset
130+
131+
ReadFile(
132+
fd,
133+
self.buf.write_ptr().cast::<c_void>(),
134+
self.buf.bytes_total() as u32,
135+
&mut bytes_read,
136+
&overlapped as *const _ as *mut _,
137+
)
138+
} else {
139+
ReadFile(
140+
fd,
141+
self.buf.write_ptr().cast::<c_void>(),
142+
self.buf.bytes_total() as u32,
143+
&mut bytes_read,
144+
std::ptr::null_mut(),
145+
)
146+
}
129147
};
130-
if TRUE == ret {
131-
Ok(bytes_read)
132-
} else {
133-
Err(io::Error::last_os_error())
148+
149+
if ret == TRUE {
150+
return Ok(bytes_read);
151+
}
152+
153+
match unsafe { GetLastError() } {
154+
ERROR_HANDLE_EOF => Ok(bytes_read),
155+
error => Err(io::Error::from_raw_os_error(error as _)),
134156
}
135157
}
136158
}
@@ -140,17 +162,25 @@ pub(crate) struct ReadVec<T> {
140162
/// while the operation is in-flight.
141163
#[allow(unused)]
142164
fd: SharedFd,
165+
offset: u64,
143166

144167
/// Reference to the in-flight buffer.
145168
pub(crate) buf_vec: T,
146169
}
147170

148171
impl<T: IoVecBufMut> Op<ReadVec<T>> {
149172
pub(crate) fn readv(fd: SharedFd, buf_vec: T) -> io::Result<Self> {
150-
Op::submit_with(ReadVec { fd, buf_vec })
173+
Op::submit_with(ReadVec {
174+
fd,
175+
// Refers to https://docs.rs/io-uring/latest/io_uring/opcode/struct.Write.html.
176+
// If `offset` is set to `-1`, the offset will use (and advance) the file position, like
177+
// the readv(2) system calls.
178+
offset: -1i64 as u64,
179+
buf_vec,
180+
})
151181
}
152182

153-
pub(crate) async fn read(self) -> BufResult<usize, T> {
183+
pub(crate) async fn result(self) -> BufResult<usize, T> {
154184
let complete = self.await;
155185
let res = complete.meta.result.map(|v| v as _);
156186
let mut buf_vec = complete.data.buf_vec;
@@ -168,7 +198,9 @@ impl<T: IoVecBufMut> OpAble for ReadVec<T> {
168198
fn uring_op(&mut self) -> io_uring::squeue::Entry {
169199
let ptr = self.buf_vec.write_iovec_ptr() as _;
170200
let len = self.buf_vec.write_iovec_len() as _;
171-
opcode::Readv::new(types::Fd(self.fd.raw_fd()), ptr, len).build()
201+
opcode::Readv::new(types::Fd(self.fd.raw_fd()), ptr, len)
202+
.offset(self.offset)
203+
.build()
172204
}
173205

174206
#[cfg(any(feature = "legacy", feature = "poll-io"))]
@@ -188,6 +220,11 @@ impl<T: IoVecBufMut> OpAble for ReadVec<T> {
188220

189221
#[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))]
190222
fn legacy_call(&mut self) -> io::Result<u32> {
223+
// There is no `readv` like syscall of file on windows, but this will be used to send
224+
// socket message.
225+
226+
use windows_sys::Win32::Networking::WinSock::{WSAGetLastError, WSARecv, WSAESHUTDOWN};
227+
191228
let mut nread = 0;
192229
let mut flags = 0;
193230
let ret = unsafe {

monoio/src/driver/op/recv.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl<T: IoBufMut> Op<Recv<T>> {
6464
}
6565
}
6666

67-
pub(crate) async fn read(self) -> BufResult<usize, T> {
67+
pub(crate) async fn result(self) -> BufResult<usize, T> {
6868
let complete = self.await;
6969
let res = complete.meta.result.map(|v| v as _);
7070
let mut buf = complete.data.buf;

monoio/src/driver/op/send.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl<T: IoBuf> Op<Send<T>> {
4444
}
4545
}
4646

47-
pub(crate) async fn write(self) -> BufResult<usize, T> {
47+
pub(crate) async fn result(self) -> BufResult<usize, T> {
4848
let complete = self.await;
4949
(complete.meta.result.map(|v| v as _), complete.data.buf)
5050
}

monoio/src/driver/op/statx.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,33 +28,33 @@ type FdStatx = Statx<SharedFd>;
2828
impl Op<FdStatx> {
2929
/// submit a statx operation
3030
#[cfg(target_os = "linux")]
31-
pub(crate) fn statx_using_fd(fd: &SharedFd, flags: i32) -> std::io::Result<Self> {
31+
pub(crate) fn statx_using_fd(fd: SharedFd, flags: i32) -> std::io::Result<Self> {
3232
Op::submit_with(Statx {
33-
inner: fd.clone(),
33+
inner: fd,
3434
flags,
3535
statx_buf: Box::new(MaybeUninit::uninit()),
3636
})
3737
}
3838

3939
#[cfg(target_os = "linux")]
40-
pub(crate) async fn statx_result(self) -> std::io::Result<statx> {
40+
pub(crate) async fn result(self) -> std::io::Result<statx> {
4141
let complete = self.await;
4242
complete.meta.result?;
4343

4444
Ok(unsafe { MaybeUninit::assume_init(*complete.data.statx_buf) })
4545
}
4646

4747
#[cfg(target_os = "macos")]
48-
pub(crate) fn statx_using_fd(fd: &SharedFd, follow_symlinks: bool) -> std::io::Result<Self> {
48+
pub(crate) fn statx_using_fd(fd: SharedFd, follow_symlinks: bool) -> std::io::Result<Self> {
4949
Op::submit_with(Statx {
50-
inner: fd.clone(),
50+
inner: fd,
5151
follow_symlinks,
5252
stat_buf: Box::new(MaybeUninit::uninit()),
5353
})
5454
}
5555

5656
#[cfg(target_os = "macos")]
57-
pub(crate) async fn statx_result(self) -> std::io::Result<libc::stat> {
57+
pub(crate) async fn result(self) -> std::io::Result<libc::stat> {
5858
let complete = self.await;
5959
complete.meta.result?;
6060

@@ -130,7 +130,7 @@ impl Op<PathStatx> {
130130
}
131131

132132
#[cfg(target_os = "linux")]
133-
pub(crate) async fn statx_result(self) -> std::io::Result<statx> {
133+
pub(crate) async fn result(self) -> std::io::Result<statx> {
134134
let complete = self.await;
135135
complete.meta.result?;
136136

@@ -151,7 +151,7 @@ impl Op<PathStatx> {
151151
}
152152

153153
#[cfg(target_os = "macos")]
154-
pub(crate) async fn statx_result(self) -> std::io::Result<libc::stat> {
154+
pub(crate) async fn result(self) -> std::io::Result<libc::stat> {
155155
let complete = self.await;
156156
complete.meta.result?;
157157

0 commit comments

Comments
 (0)