diff --git a/.github/workflows/ci.sh b/.github/workflows/ci.sh index b08b843f..20296ceb 100755 --- a/.github/workflows/ci.sh +++ b/.github/workflows/ci.sh @@ -27,8 +27,8 @@ if [ "${NO_RUN}" != "1" ] && [ "${NO_RUN}" != "true" ]; then "${CARGO}" test --target "${TARGET}" --no-default-features --features "async-cancel,bytes,legacy,macros,utils" --release # enable legacy and sync - "${CARGO}" test --target "${TARGET}" --no-default-features --features "async-cancel,bytes,legacy,macros,utils,asyncify-op" - "${CARGO}" test --target "${TARGET}" --no-default-features --features "async-cancel,bytes,legacy,macros,utils,asyncify-op" --release + "${CARGO}" test --target "${TARGET}" --no-default-features --features "async-cancel,bytes,legacy,macros,utils,sync" + "${CARGO}" test --target "${TARGET}" --no-default-features --features "async-cancel,bytes,legacy,macros,utils,sync" --release if [ "${TARGET}" = "x86_64-unknown-linux-gnu" ] || [ "${TARGET}" = "i686-unknown-linux-gnu" ]; then # only enabled uring driver diff --git a/monoio/Cargo.toml b/monoio/Cargo.toml index 1e4f0a25..eb25f186 100644 --- a/monoio/Cargo.toml +++ b/monoio/Cargo.toml @@ -96,8 +96,6 @@ poll-io = ["tokio", "mio"] # signal enables setting ctrl_c handler signal = ["ctrlc", "sync"] signal-termination = ["signal", "ctrlc/termination"] -# asyncify-op will run fs related task in blocking_thread_pool -asyncify-op = ["sync", "legacy"] # by default both iouring and legacy are enabled default = ["async-cancel", "bytes", "iouring", "legacy", "macros", "utils"] diff --git a/monoio/src/builder.rs b/monoio/src/builder.rs index b6a7c9f0..73616b1f 100644 --- a/monoio/src/builder.rs +++ b/monoio/src/builder.rs @@ -32,7 +32,14 @@ pub struct RuntimeBuilder { scoped_thread_local!(pub(crate) static BUILD_THREAD_ID: usize); impl Default for RuntimeBuilder { - /// Create a default runtime builder + /// Create a default runtime builder. + /// + /// # Note + /// + /// When the sync feature is enabled, the default behavior of + /// [monoio::blocking::BlockingStrategy] is to execute tasks on the local thread. In other + /// words, there is no thread pool involved—all blocking I/O operations and heavy computations + /// will block the current thread. #[must_use] fn default() -> Self { RuntimeBuilder::::new() @@ -40,7 +47,14 @@ impl Default for RuntimeBuilder { } impl RuntimeBuilder { - /// Create a default runtime builder + /// Create a default runtime builder. + /// + /// # Note + /// + /// When the sync feature is enabled, the default behavior of + /// [monoio::blocking::BlockingStrategy] is to execute tasks on the local thread. In other + /// words, there is no thread pool involved—all blocking I/O operations and heavy computations + /// will block the current thread. #[must_use] pub fn new() -> Self { Self { @@ -50,7 +64,7 @@ impl RuntimeBuilder { urb: io_uring::IoUring::builder(), #[cfg(feature = "sync")] - blocking_handle: crate::blocking::BlockingStrategy::Panic.into(), + blocking_handle: crate::blocking::BlockingStrategy::ExecuteLocal.into(), _mark: PhantomData, } } diff --git a/monoio/src/driver/op/read.rs b/monoio/src/driver/op/read.rs index f73eaa35..4342dbf6 100644 --- a/monoio/src/driver/op/read.rs +++ b/monoio/src/driver/op/read.rs @@ -6,11 +6,6 @@ use std::os::unix::prelude::AsRawFd; pub(crate) use impls::*; #[cfg(all(target_os = "linux", feature = "iouring"))] use io_uring::{opcode, types}; -#[cfg(all(windows, any(feature = "legacy", feature = "poll-io")))] -use { - std::ffi::c_void, - windows_sys::Win32::{Foundation::TRUE, Storage::FileSystem::ReadFile}, -}; use super::{super::shared_fd::SharedFd, Op, OpAble}; #[cfg(any(feature = "legacy", feature = "poll-io"))] @@ -315,10 +310,12 @@ pub(crate) mod impls { use super::*; use crate::syscall_u32; + /// A wrapper for [`libc::read`] pub(crate) fn read(fd: i32, buf: *mut u8, len: usize) -> io::Result { syscall_u32!(read(fd, buf as _, len)) } + /// A wrapper of [`libc::pread`] pub(crate) fn read_at(fd: i32, buf: *mut u8, len: usize, offset: u64) -> io::Result { let offset = libc::off_t::try_from(offset) .map_err(|_| io::Error::new(io::ErrorKind::Other, "offset too big"))?; @@ -326,10 +323,12 @@ pub(crate) mod impls { syscall_u32!(pread(fd, buf as _, len, offset)) } + /// A wrapper of [`libc::readv`] pub(crate) fn read_vectored(fd: i32, buf_vec: *mut iovec, len: usize) -> io::Result { syscall_u32!(readv(fd, buf_vec as _, len as _)) } + /// A wrapper of [`libc::preadv`] pub(crate) fn read_vectored_at( fd: i32, buf_vec: *mut iovec, @@ -345,13 +344,17 @@ pub(crate) mod impls { #[cfg(all(any(feature = "legacy", feature = "poll-io"), windows))] pub(crate) mod impls { + use std::ffi::c_void; + use windows_sys::Win32::{ - Foundation::{GetLastError, ERROR_HANDLE_EOF}, + Foundation::{GetLastError, ERROR_HANDLE_EOF, TRUE}, + Storage::FileSystem::ReadFile, System::IO::OVERLAPPED, }; use super::*; + /// A wrapper of [`windows_sys::Win32::Storage::FileSystem::ReadFile`] pub(crate) fn read(handle: isize, buf: *mut u8, len: usize) -> io::Result { let mut bytes_read = 0; let ret = unsafe { @@ -374,6 +377,8 @@ pub(crate) mod impls { } } + /// A wrapper of [`windows_sys::Win32::Storage::FileSystem::ReadFile`] and using the + /// [`windows_sys::Win32::System::IO::OVERLAPPED`] to read at specific position. pub(crate) fn read_at(handle: isize, buf: *mut u8, len: usize, offset: u64) -> io::Result { let mut bytes_read = 0; let ret = unsafe { diff --git a/monoio/src/fs/file/mod.rs b/monoio/src/fs/file/mod.rs index f694a876..42443768 100644 --- a/monoio/src/fs/file/mod.rs +++ b/monoio/src/fs/file/mod.rs @@ -162,6 +162,20 @@ impl File { /// - this function will change the file pointer, but the `pos` always start from the begin /// of file. /// + /// Addtionally, + /// + /// - On Unix and Windows (without the `iouring` feature enabled or not support the `iouring`): + /// - If the sync feature is enabled and the thread pool is attached, this operation will be + /// executed on the blocking thread pool, preventing it from blocking the current thread. + /// - If the sync feature is enabled but the thread pool is not attached, or if the sync + /// feature is disabled, the operation will be executed on the local thread, blocking the + /// current thread. + /// + /// - On Linux (with iouring enabled and supported): + /// + /// This operation will use io-uring to execute the task asynchronously. + /// + /// /// # Errors /// /// If this function encounters any form of I/O or other error, an error @@ -297,6 +311,19 @@ impl File { /// underlying file is no longer able to accept bytes and will likely not be /// able to in the future as well, or that the buffer provided is empty. /// + /// # Platform-specific behavior + /// + /// - On Unix and Windows (without the `iouring` feature enabled or not support the `iouring`): + /// - If the sync feature is enabled and the thread pool is attached, this operation will be + /// executed on the blocking thread pool, preventing it from blocking the current thread. + /// - If the sync feature is enabled but the thread pool is not attached, or if the sync + /// feature is disabled, the operation will be executed on the local thread, blocking the + /// current thread. + /// + /// - On Linux (with iouring enabled and supported): + /// + /// This operation will use io-uring to execute the task asynchronously. + /// /// # Errors /// /// Each call to `write` may generate an I/O error indicating that the @@ -628,6 +655,19 @@ impl AsyncReadRent for File { /// It is not an error if `n` is smaller than the buffer size, even if there is enough data in /// the file to fill the buffer. /// + /// # Platform-specific behavior + /// + /// - On Unix and Windows (without the `iouring` feature enabled or not support the `iouring`): + /// - If the sync feature is enabled and the thread pool is attached, this operation will be + /// executed on the blocking thread pool, preventing it from blocking the current thread. + /// - If the sync feature is enabled but the thread pool is not attached, or if the sync + /// feature is disabled, the operation will be executed on the local thread, blocking the + /// current thread. + /// + /// - On Linux (with iouring enabled and supported): + /// + /// This operation will use io-uring to execute the task asynchronously. + /// /// # Errors /// /// If an I/O or other error occurs, an error variant will be returned, and the buffer will also @@ -675,6 +715,18 @@ impl AsyncReadRent for File { /// - due to windows does not have syscall like `readv`, so the implement of this function /// on windows is by internally calling the `ReadFile` syscall to fill each buffer. /// + /// - On Unix and Windows (without the `iouring` feature enabled or not support the `iouring`): + /// - If the sync feature is enabled and the thread pool is attached, this operation will be + /// executed on the blocking thread pool, preventing it from blocking the current thread. + /// - If the sync feature is enabled but the thread pool is not attached, or if the sync + /// feature is disabled, the operation will be executed on the local thread, blocking the + /// current thread. + /// + /// - On Linux (with iouring enabled and supported): + /// + /// This operation will use io-uring to execute the task asynchronously. + /// + /// /// # Errors /// /// If this function encounters any form of I/O or other error, an error diff --git a/monoio/src/fs/file/unix.rs b/monoio/src/fs/file/unix.rs index 38d8f17d..88f57187 100644 --- a/monoio/src/fs/file/unix.rs +++ b/monoio/src/fs/file/unix.rs @@ -4,10 +4,10 @@ use std::{ os::fd::{AsRawFd, IntoRawFd, RawFd}, }; -#[cfg(all(feature = "asyncify-op", not(feature = "iouring")))] +#[cfg(all(not(feature = "iouring"), feature = "sync"))] pub(crate) use asyncified::*; -#[cfg(any(not(feature = "asyncify-op"), feature = "iouring"))] -pub(crate) use uring_or_blocking::*; +#[cfg(any(feature = "iouring", not(feature = "sync")))] +pub(crate) use iouring::*; use super::File; use crate::{ @@ -77,8 +77,8 @@ pub(crate) async fn metadata(fd: SharedFd) -> std::io::Result { op.result().await.map(FileAttr::from).map(Metadata) } -#[cfg(any(not(feature = "asyncify-op"), feature = "iouring"))] -mod uring_or_blocking { +#[cfg(any(feature = "iouring", not(feature = "sync")))] +mod iouring { use super::*; pub(crate) async fn read(fd: SharedFd, buf: T) -> crate::BufResult { @@ -104,7 +104,7 @@ mod uring_or_blocking { } } -#[cfg(all(feature = "asyncify-op", not(feature = "iouring")))] +#[cfg(all(not(feature = "iouring"), feature = "sync"))] mod asyncified { use super::*; use crate::{asyncify_op, driver::op::read}; diff --git a/monoio/src/fs/file/windows.rs b/monoio/src/fs/file/windows.rs index 9d796f08..2dc19a1e 100644 --- a/monoio/src/fs/file/windows.rs +++ b/monoio/src/fs/file/windows.rs @@ -3,9 +3,9 @@ use std::{ os::windows::io::{AsRawHandle, RawHandle}, }; -#[cfg(all(feature = "asyncify-op", not(feature = "iouring")))] +#[cfg(all(not(feature = "iouring"), feature = "sync"))] pub(crate) use asyncified::*; -#[cfg(any(not(feature = "asyncify-op"), feature = "iouring"))] +#[cfg(any(feature = "iouring", not(feature = "sync")))] pub(crate) use blocking::*; use windows_sys::Win32::Networking::WinSock::WSABUF; @@ -74,7 +74,7 @@ pub(crate) async fn write_vectored( (Ok(total_bytes_write), buf_vec) } -#[cfg(any(not(feature = "asyncify-op"), feature = "iouring"))] +#[cfg(any(feature = "iouring", not(feature = "sync")))] mod blocking { use super::*; @@ -143,7 +143,7 @@ mod blocking { } } -#[cfg(all(feature = "asyncify-op", not(feature = "iouring")))] +#[cfg(all(not(feature = "iouring"), feature = "sync"))] mod asyncified { use super::*; use crate::{asyncify_op, driver::op::read, fs::asyncify}; @@ -151,6 +151,11 @@ mod asyncified { asyncify_op!(read(read::read, IoBufMut::write_ptr, IoBufMut::bytes_total)); asyncify_op!(read_at(read::read_at, IoBufMut::write_ptr, IoBufMut::bytes_total, pos: u64)); + /// The `readv` implement on windows. + /// + /// Due to windows does not have syscall like `readv`, so we need to simulate it by ourself. + /// + /// This function is just to fill each buffer by calling the `read` function. pub(crate) async fn read_vectored( fd: SharedFd, mut buf_vec: T, diff --git a/monoio/src/fs/mod.rs b/monoio/src/fs/mod.rs index b5cf26d0..67761169 100644 --- a/monoio/src/fs/mod.rs +++ b/monoio/src/fs/mod.rs @@ -69,8 +69,7 @@ use crate::driver::op::Op; /// - The blocking task returned an error, in which case the error is propagated. /// - The background task failed to complete due to an internal error, in which case an error with /// `io::ErrorKind::Other` is returned. -#[cfg(feature = "asyncify-op")] -#[allow(unused)] +#[cfg(all(feature = "sync", not(feature = "iouring")))] pub(crate) async fn asyncify(f: F) -> io::Result where F: FnOnce() -> io::Result + Send + 'static, @@ -93,7 +92,7 @@ where /// This macro is intended to abstract the process of creating asynchronous functions for /// operations that involve reading or writing buffers, making it easier to create functions /// that perform system-level I/O asynchronously. -#[cfg(feature = "asyncify-op")] +#[cfg(all(feature = "sync", not(feature = "iouring")))] #[macro_export] macro_rules! asyncify_op { ($fn_name:ident<$Trait: ident>($read_op:expr, $buf_ptr_expr:expr, $len_expr:expr $(, $extra_param:ident : $typ: ty)?) $(,)?) => { diff --git a/monoio/tests/fs_file.rs b/monoio/tests/fs_file.rs index beba0078..f3466d9c 100644 --- a/monoio/tests/fs_file.rs +++ b/monoio/tests/fs_file.rs @@ -1,5 +1,3 @@ -#![cfg(not(feature = "asyncify-op"))] - use std::io::{prelude::*, SeekFrom}; #[cfg(unix)] use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; diff --git a/monoio/tests/fs_file_asyncify.rs b/monoio/tests/fs_file_asyncify.rs index 84de8724..8a566d3a 100644 --- a/monoio/tests/fs_file_asyncify.rs +++ b/monoio/tests/fs_file_asyncify.rs @@ -1,5 +1,4 @@ -#![cfg(feature = "asyncify-op")] - +#![cfg(feature = "sync")] use std::io::prelude::*; #[cfg(windows)] use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle as RawFd};