From 699fd78e924aa39a1970f15e52cfb06ff9d89dc6 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Tue, 3 Sep 2024 11:45:55 +0100 Subject: [PATCH 01/14] Add documentation for SyncIoBridge with examples and alternatives --- tokio-util/src/io/sync_bridge.rs | 163 +++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 2402207584c..68ce8367129 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -6,6 +6,169 @@ use tokio::io::{ /// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or /// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`]. +/// +/// # Alternatives +/// +/// In many cases, there are better alternatives to using `SyncIoBridge`, especially if you +/// want to avoid blocking the async runtime. Consider the following scenarios: +/// +/// ## Example 1: Hashing Data +/// +/// When hashing data, using `SyncIoBridge` can lead to suboptimal performance and might not fully leverage the async capabilities of the system. +/// +/// ### Why It Matters: +/// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous context by blocking the current thread. However, this can be inefficient because: +/// - **Blocking:** The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. +/// - **Thread Pool Saturation:** If many threads are blocked using `SyncIoBridge`, it can exhaust the async runtime's thread pool, leading to increased latency and reduced throughput. +/// - **Lack of Parallelism:** By blocking on synchronous operations, you may miss out on the benefits of running tasks concurrently, especially in I/O-bound operations where async tasks could be interleaved. +/// +/// Instead, consider reading the data into memory and then hashing it, or processing the data in chunks. +/// +/// ```rust +/// use tokio::io::AsyncReadExt; +/// # mod blake3 { pub fn hash(_: &[u8]) {} } +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// let mut reader = tokio::io::empty(); +/// +/// // Read all data from the reader into a Vec. +/// let mut data = Vec::new(); +/// reader.read_to_end(&mut data).await?; +/// +/// // Hash the data using the blake3 hashing function. +/// let hash = blake3::hash(&data); +/// +/// Ok(()) +/// } +/// ``` +/// +/// Or, for more complex cases: +/// +/// ```rust +/// use tokio::io::AsyncReadExt; +/// # struct Hasher; +/// # impl Hasher { pub fn update(&mut self, _: &[u8]) {} pub fn finalize(&self) {} } +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// let mut reader = tokio::io::empty(); +/// let mut hasher = Hasher; +/// +/// // Create a buffer to read data into, sized for performance. +/// let mut data = vec![0; 64 * 1024]; +/// loop { +/// //Read data from the reader into the buffer. +/// let len = reader.read(&mut data).await?; +/// if len == 0 { break; } // Exit loop if no more data. +/// +/// // Update the hash with the data read. +/// hasher.update(&data[..len]); +/// } +/// +/// // Finalize the hash after all data has been processed. +/// let hash = hasher.finalize(); +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Example 2: Compressing Data +/// +/// When compressing data, avoid using `SyncIoBridge` with non-async compression libraries, as it may lead to inefficient and blocking code. +/// Instead, use an async compression library such as the [`async-compression`](https://docs.rs/async-compression/latest/async_compression/) crate, +/// which is designed to work with asynchronous data streams. +/// +/// ```ignore +/// use async_compression::tokio::write::GzipEncoder; +/// use tokio::io::AsyncReadExt; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// let mut reader = tokio::io::empty(); +/// let mut writer = tokio::io::sink(); +/// +/// // Create a Gzip encoder that wraps the writer. +/// let mut encoder = GzipEncoder::new(writer); +/// +/// // Copy data from the reader to the encoder, compressing it. +/// tokio::io::copy(&mut reader, &mut encoder).await?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Example 3: Parsing `JSON` +/// +/// When parsing serialization formats such as `JSON`, avoid using `SyncIoBridge` with functions that +/// deserialize data from a type implementing `std::io::Read`, such as `serde_json::from_reader`. +/// +/// ```rust,no_run +/// use tokio::io::AsyncReadExt; +/// # mod serde { +/// # pub trait DeserializeOwned: 'static {} +/// # impl DeserializeOwned for T {} +/// # } +/// # mod serde_json { +/// # use super::serde::DeserializeOwned; +/// # pub fn from_slice(_: &[u8]) -> Result { +/// # unimplemented!() +/// # } +/// # } +/// # #[derive(Debug)] struct MyStruct; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// let mut reader = tokio::io::empty(); +/// +/// // Read all data from the reader into a Vec. +/// let mut data = Vec::new(); +/// reader.read_to_end(&mut data).await?; +/// +/// // Deserialize the data from the Vec into a MyStruct instance. +/// let value: MyStruct = serde_json::from_slice(&data)?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Correct Usage of `SyncIoBridge` inside `spawn_blocking` +/// +/// `SyncIoBridge` is mainly useful when you need to interface with synchronous libraries from an asynchronous context. +/// Here is how you can do it correctly: +/// +/// ```rust +/// use tokio::task::spawn_blocking; +/// use tokio_util::io::SyncIoBridge; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// +/// let reader = tokio::io::empty(); +/// +/// // Wrap the async reader with SyncIoBridge to allow synchronous reading. +/// let mut sync_reader = SyncIoBridge::new(reader); +/// +/// // Spawn a blocking task to perform synchronous I/O operations. +/// let result = spawn_blocking(move || { +/// // Create an in-memory buffer to hold the copied data. +/// let mut buffer = Vec::new(); +/// +/// // Copy data from the sync_reader to the buffer. +/// std::io::copy(&mut sync_reader, &mut buffer)?; +/// +/// // Return the buffer containing the copied data. +/// Ok::<_, std::io::Error>(buffer) +/// }) +/// .await??; +/// +/// // You can use `result` here as needed. +/// // `result` contains the data read into the buffer. +/// +/// Ok(()) +/// } +/// ``` +/// #[derive(Debug)] pub struct SyncIoBridge { src: T, From de14fef14f702f72a3ed06033056a13c2ac494b0 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Wed, 4 Sep 2024 10:57:57 +0100 Subject: [PATCH 02/14] Add documentation for SyncIoBridge with examples and alternatives --- tokio-util/src/io/sync_bridge.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 68ce8367129..adc640436ab 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -18,9 +18,9 @@ use tokio::io::{ /// /// ### Why It Matters: /// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous context by blocking the current thread. However, this can be inefficient because: -/// - **Blocking:** The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. -/// - **Thread Pool Saturation:** If many threads are blocked using `SyncIoBridge`, it can exhaust the async runtime's thread pool, leading to increased latency and reduced throughput. -/// - **Lack of Parallelism:** By blocking on synchronous operations, you may miss out on the benefits of running tasks concurrently, especially in I/O-bound operations where async tasks could be interleaved. +/// - Blocking: The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. +/// - Thread Pool Saturation: If many threads are blocked using `SyncIoBridge`, it can exhaust the async runtime's thread pool, leading to increased latency and reduced throughput. +/// - Lack of Parallelism: By blocking on synchronous operations, you may miss out on the benefits of running tasks concurrently, especially in I/O-bound operations where async tasks could be interleaved. /// /// Instead, consider reading the data into memory and then hashing it, or processing the data in chunks. /// From 6bdda2838b4476e30777eed52fa3741cf9df16e1 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Wed, 4 Sep 2024 10:59:45 +0100 Subject: [PATCH 03/14] Add documentation for SyncIoBridge with examples and alternatives --- tokio-util/src/io/sync_bridge.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index adc640436ab..c81fac1c6ad 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -18,9 +18,9 @@ use tokio::io::{ /// /// ### Why It Matters: /// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous context by blocking the current thread. However, this can be inefficient because: -/// - Blocking: The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. -/// - Thread Pool Saturation: If many threads are blocked using `SyncIoBridge`, it can exhaust the async runtime's thread pool, leading to increased latency and reduced throughput. -/// - Lack of Parallelism: By blocking on synchronous operations, you may miss out on the benefits of running tasks concurrently, especially in I/O-bound operations where async tasks could be interleaved. +/// Blocking: The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. +/// Thread Pool Saturation: If many threads are blocked using `SyncIoBridge`, it can exhaust the async runtime's thread pool, leading to increased latency and reduced throughput. +/// Lack of Parallelism: By blocking on synchronous operations, you may miss out on the benefits of running tasks concurrently, especially in I/O-bound operations where async tasks could be interleaved. /// /// Instead, consider reading the data into memory and then hashing it, or processing the data in chunks. /// From 47534ed0862045afa521d9dccaf8805b30933b19 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Wed, 4 Sep 2024 11:12:15 +0100 Subject: [PATCH 04/14] code formatting --- tokio-util/src/io/sync_bridge.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index c81fac1c6ad..2f03431ba6b 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -15,10 +15,10 @@ use tokio::io::{ /// ## Example 1: Hashing Data /// /// When hashing data, using `SyncIoBridge` can lead to suboptimal performance and might not fully leverage the async capabilities of the system. -/// +/// /// ### Why It Matters: /// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous context by blocking the current thread. However, this can be inefficient because: -/// Blocking: The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. +/// Blocking: The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. /// Thread Pool Saturation: If many threads are blocked using `SyncIoBridge`, it can exhaust the async runtime's thread pool, leading to increased latency and reduced throughput. /// Lack of Parallelism: By blocking on synchronous operations, you may miss out on the benefits of running tasks concurrently, especially in I/O-bound operations where async tasks could be interleaved. /// From d9dc4bf784a19a5fc95a8cfe14a91ec286dcfbd4 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Wed, 4 Sep 2024 11:18:26 +0100 Subject: [PATCH 05/14] cargo spell check error fix --- tokio-util/src/io/sync_bridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 2f03431ba6b..4b3c2a90797 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -101,7 +101,7 @@ use tokio::io::{ /// ## Example 3: Parsing `JSON` /// /// When parsing serialization formats such as `JSON`, avoid using `SyncIoBridge` with functions that -/// deserialize data from a type implementing `std::io::Read`, such as `serde_json::from_reader`. +/// `deserialize` data from a type implementing `std::io::Read`, such as `serde_json::from_reader`. /// /// ```rust,no_run /// use tokio::io::AsyncReadExt; From 1830b1443d9b668eb36fa3997ee73c222ecc05e8 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Wed, 11 Sep 2024 23:03:37 +0100 Subject: [PATCH 06/14] Add documentation for SyncIoBridge with examples and alternatives --- tokio-util/src/io/sync_bridge.rs | 254 +++++++++++++++++++++---------- 1 file changed, 171 insertions(+), 83 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 4b3c2a90797..6ab4b9a0adc 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -9,102 +9,164 @@ use tokio::io::{ /// /// # Alternatives /// -/// In many cases, there are better alternatives to using `SyncIoBridge`, especially if you -/// want to avoid blocking the async runtime. Consider the following scenarios: +/// In many cases, there are better alternatives to using `SyncIoBridge`, especially +/// if you want to avoid blocking the async runtime. Consider the following scenarios: /// -/// ## Example 1: Hashing Data -/// -/// When hashing data, using `SyncIoBridge` can lead to suboptimal performance and might not fully leverage the async capabilities of the system. +/// When hashing data, using `SyncIoBridge` can lead to suboptimal performance and +/// might not fully leverage the async capabilities of the system. /// /// ### Why It Matters: -/// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous context by blocking the current thread. However, this can be inefficient because: -/// Blocking: The use of `SyncIoBridge` may block a valuable async runtime thread, which could otherwise be used to handle more tasks concurrently. -/// Thread Pool Saturation: If many threads are blocked using `SyncIoBridge`, it can exhaust the async runtime's thread pool, leading to increased latency and reduced throughput. -/// Lack of Parallelism: By blocking on synchronous operations, you may miss out on the benefits of running tasks concurrently, especially in I/O-bound operations where async tasks could be interleaved. /// -/// Instead, consider reading the data into memory and then hashing it, or processing the data in chunks. +/// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous +/// context by blocking the current thread. However, this can be inefficient because: +/// +/// - **Blocking**: The use of `SyncIoBridge` may block a valuable async runtime +/// thread, which could otherwise be used to handle more tasks concurrently. +/// +/// - **Thread Pool Saturation**: If many threads are blocked using `SyncIoBridge`, +/// it can exhaust the async runtime's thread pool, leading to increased latency +/// and reduced throughput. +/// +/// - **Lack of Parallelism**: By blocking on synchronous operations, you may miss +/// out on the benefits of running tasks concurrently, especially in I/O-bound +/// operations where async tasks could be interleaved. +/// +/// ## Example 1: Hashing Data +/// +/// The use of `SyncIoBridge` is unnecessary when hashing data. Instead, you can +/// process the data asynchronously by reading it into memory, which avoids blocking +/// the async runtime. +/// +/// There are two strategies for avoiding `SyncIoBridge` when hashing data. When +/// the data fits into memory, the easiest is to read the data into a `Vec` +/// and hash it: /// /// ```rust /// use tokio::io::AsyncReadExt; +/// use tokio::io::AsyncRead; +/// use std::io::Cursor; /// # mod blake3 { pub fn hash(_: &[u8]) {} } /// -/// #[tokio::main] -/// async fn main() -> Result<(), Box> { -/// let mut reader = tokio::io::empty(); +/// async fn hash_contents(mut reader: impl AsyncRead + Unpin) -> Result<(), std::io::Error> { +/// // Read all data from the reader into a Vec. +/// let mut data = Vec::new(); +/// reader.read_to_end(&mut data).await?; /// -/// // Read all data from the reader into a Vec. -/// let mut data = Vec::new(); -/// reader.read_to_end(&mut data).await?; +/// // Hash the data using the blake3 hashing function. +/// let hash = blake3::hash(&data); /// -/// // Hash the data using the blake3 hashing function. -/// let hash = blake3::hash(&data); +/// Ok(()) +///} /// -/// Ok(()) +/// #[tokio::main] +/// async fn main() -> Result<(), std::io::Error> { +/// // Example: In-memory data. +/// let data = b"Hello, world!"; // A byte slice. +/// let reader = Cursor::new(data); // Create an in-memory AsyncRead. +/// hash_contents(reader).await /// } /// ``` +/// Explanation: This example demonstrates how to asynchronously read data from a +/// reader into memory and hash it using a synchronous hashing function. The +/// `SyncIoBridge` is avoided, ensuring that the async runtime is not blocked. +/// /// -/// Or, for more complex cases: +/// When the data doesn't fit into memory, the hashing library will usually +/// provide a hasher that you can repeatedly call `update` on to hash the data +/// one chunk at the time. For example: /// /// ```rust /// use tokio::io::AsyncReadExt; +/// use tokio::io::AsyncRead; +/// use std::io::Cursor; /// # struct Hasher; /// # impl Hasher { pub fn update(&mut self, _: &[u8]) {} pub fn finalize(&self) {} } /// -/// #[tokio::main] -/// async fn main() -> Result<(), Box> { -/// let mut reader = tokio::io::empty(); -/// let mut hasher = Hasher; -/// -/// // Create a buffer to read data into, sized for performance. -/// let mut data = vec![0; 64 * 1024]; -/// loop { -/// //Read data from the reader into the buffer. -/// let len = reader.read(&mut data).await?; -/// if len == 0 { break; } // Exit loop if no more data. -/// -/// // Update the hash with the data read. -/// hasher.update(&data[..len]); -/// } -/// -/// // Finalize the hash after all data has been processed. -/// let hash = hasher.finalize(); +/// /// Asynchronously streams data from an async reader, processes it in chunks, +/// /// and hashes the data incrementally. +/// async fn hash_stream(mut reader: impl AsyncRead + Unpin, mut hasher: Hasher) -> Result<(), std::io::Error> { +/// // Create a buffer to read data into, sized for performance. +/// let mut data = vec![0; 64 * 1024]; +/// loop { +/// // Read data from the reader into the buffer. +/// let len = reader.read(&mut data).await?; +/// if len == 0 { break; } // Exit loop if no more data. /// -/// Ok(()) +/// // Update the hash with the data read. +/// hasher.update(&data[..len]); +/// } +/// +/// // Finalize the hash after all data has been processed. +/// let hash = hasher.finalize(); +/// +/// Ok(()) +///} +/// +/// #[tokio::main] +/// async fn main() -> Result<(), std::io::Error> { +/// // Example: In-memory data. +/// let data = b"Hello, world!"; // A byte slice. +/// let reader = Cursor::new(data); // Create an in-memory AsyncRead. +/// let hasher = Hasher; +/// hash_stream(reader, hasher).await /// } /// ``` +/// Explanation: This example demonstrates how to asynchronously stream data in +/// chunks for hashing. Each chunk is read asynchronously, and the hash is updated +/// incrementally. This avoids blocking and improves performance over using +/// `SyncIoBridge`. +/// /// /// ## Example 2: Compressing Data /// -/// When compressing data, avoid using `SyncIoBridge` with non-async compression libraries, as it may lead to inefficient and blocking code. -/// Instead, use an async compression library such as the [`async-compression`](https://docs.rs/async-compression/latest/async_compression/) crate, -/// which is designed to work with asynchronous data streams. +/// When compressing data, avoid using `SyncIoBridge` with non-async compression +/// libraries, as it may lead to inefficient and blocking code. Instead, use an +/// async compression library such as the [`async-compression`](https://docs.rs/async-compression/latest/async_compression/) +/// crate, which is designed to work with asynchronous data streams. /// /// ```ignore /// use async_compression::tokio::write::GzipEncoder; /// use tokio::io::AsyncReadExt; +/// use tokio::io::Cursor; +/// use std::io::AsyncRead; /// -/// #[tokio::main] -/// async fn main() -> Result<(), Box> { -/// let mut reader = tokio::io::empty(); -/// let mut writer = tokio::io::sink(); +/// /// Asynchronously compresses data from an async reader using Gzip and an async encoder. +/// async fn compress_data(mut reader: impl AsyncRead + Unpin) -> Result<(), std::io::Error> { +/// let mut writer = tokio::io::sink(); /// -/// // Create a Gzip encoder that wraps the writer. -/// let mut encoder = GzipEncoder::new(writer); +/// // Create a Gzip encoder that wraps the writer. +/// let mut encoder = GzipEncoder::new(writer); /// -/// // Copy data from the reader to the encoder, compressing it. -/// tokio::io::copy(&mut reader, &mut encoder).await?; +/// // Copy data from the reader to the encoder, compressing it. +/// tokio::io::copy(&mut reader, &mut encoder).await?; /// -/// Ok(()) +/// Ok(()) +///} +/// +/// #[tokio::main] +/// async fn main() -> Result<(), std::io::Error> { +/// // Example: In-memory data. +/// let data = b"Hello, world!"; // A byte slice. +/// let reader = Cursor::new(data); // Create an in-memory AsyncRead. +/// compresses_data(reader).await /// } /// ``` +/// Explanation: This example shows how to asynchronously compress data using an +/// async compression library. By reading and writing asynchronously, it avoids +/// blocking and is more efficient than using `SyncIoBridge` with a non-async +/// compression library. +/// /// /// ## Example 3: Parsing `JSON` /// -/// When parsing serialization formats such as `JSON`, avoid using `SyncIoBridge` with functions that -/// `deserialize` data from a type implementing `std::io::Read`, such as `serde_json::from_reader`. +/// When parsing serialization formats such as `JSON`, avoid using `SyncIoBridge` +/// with functions that `deserialize` data from a type implementing `std::io::Read`, +/// such as `serde_json::from_reader`. /// /// ```rust,no_run +/// use tokio::io::AsyncRead; /// use tokio::io::AsyncReadExt; +/// use std::io::Cursor; /// # mod serde { /// # pub trait DeserializeOwned: 'static {} /// # impl DeserializeOwned for T {} @@ -117,57 +179,83 @@ use tokio::io::{ /// # } /// # #[derive(Debug)] struct MyStruct; /// -/// #[tokio::main] -/// async fn main() -> Result<(), Box> { -/// let mut reader = tokio::io::empty(); /// -/// // Read all data from the reader into a Vec. -/// let mut data = Vec::new(); -/// reader.read_to_end(&mut data).await?; +/// async fn parse_json(mut reader: impl AsyncRead + Unpin) -> Result { +/// // Read all data from the reader into a Vec. +/// let mut data = Vec::new(); +/// reader.read_to_end(&mut data).await?; /// -/// // Deserialize the data from the Vec into a MyStruct instance. -/// let value: MyStruct = serde_json::from_slice(&data)?; +/// // Deserialize the data from the Vec into a MyStruct instance. +/// let value: MyStruct = serde_json::from_slice(&data)?; /// +/// Ok(value) +///} +/// +/// #[tokio::main] +/// async fn main() -> Result<(), std::io::Error> { +/// // Example: In-memory data. +/// let data = b"Hello, world!"; // A byte slice. +/// let reader = Cursor::new(data); // Create an in-memory AsyncRead. +/// parse_json(reader).await?; /// Ok(()) /// } /// ``` +/// Explanation: This example shows how to asynchronously read data into memory +/// and then parse it as JSON. By avoiding `SyncIoBridge`, the asynchronous runtime +/// remains unblocked, leading to better performance when working with asynchronous +/// I/O streams. +/// /// /// ## Correct Usage of `SyncIoBridge` inside `spawn_blocking` /// -/// `SyncIoBridge` is mainly useful when you need to interface with synchronous libraries from an asynchronous context. -/// Here is how you can do it correctly: +/// `SyncIoBridge` is mainly useful when you need to interface with synchronous +/// libraries from an asynchronous context. Here is how you can do it correctly: /// /// ```rust /// use tokio::task::spawn_blocking; /// use tokio_util::io::SyncIoBridge; +/// use tokio::io::AsyncRead; +/// use std::marker::Unpin; +/// use std::io::Cursor; +/// +/// /// Wraps an async reader with `SyncIoBridge` and performs syncrhonous I/O operations in a blocking task. +/// async fn process_sync_io(reader: impl AsyncRead + Unpin + Send + 'static) -> Result, std::io::Error> { +/// // Wrap the async reader with `SyncIoBridge` to allow synchronous reading. +/// let mut sync_reader = SyncIoBridge::new(reader); +/// +/// // Spawn a blocking task to perform synchronous I/O operations. +/// let result = spawn_blocking(move || { +/// // Create an in-memory buffer to hold the copied data. +/// let mut buffer = Vec::new(); +/// +/// // Copy data from the sync_reader to the buffer. +/// std::io::copy(&mut sync_reader, &mut buffer)?; +/// +/// // Return the buffer containing the copied data. +/// Ok::<_, std::io::Error>(buffer) +/// }) +/// .await??; +/// +/// // Return the result from the blocking task. +/// Ok(result) +///} /// /// #[tokio::main] -/// async fn main() -> Result<(), Box> { -/// -/// let reader = tokio::io::empty(); -/// -/// // Wrap the async reader with SyncIoBridge to allow synchronous reading. -/// let mut sync_reader = SyncIoBridge::new(reader); -/// -/// // Spawn a blocking task to perform synchronous I/O operations. -/// let result = spawn_blocking(move || { -/// // Create an in-memory buffer to hold the copied data. -/// let mut buffer = Vec::new(); -/// -/// // Copy data from the sync_reader to the buffer. -/// std::io::copy(&mut sync_reader, &mut buffer)?; -/// -/// // Return the buffer containing the copied data. -/// Ok::<_, std::io::Error>(buffer) -/// }) -/// .await??; +/// async fn main() -> Result<(), std::io::Error> { +/// // Example: In-memory data. +/// let data = b"Hello, world!"; // A byte slice. +/// let reader = Cursor::new(data); // Create an in-memory AsyncRead. +/// let result = process_sync_io(reader).await?; /// /// // You can use `result` here as needed. -/// // `result` contains the data read into the buffer. /// /// Ok(()) /// } /// ``` +/// Explanation: This example shows how to use `SyncIoBridge` inside a `spawn_blocking` +/// task to safely perform synchronous I/O without blocking the async runtime. The +/// `spawn_blocking` ensures that the synchronous code is offloaded to a dedicated +/// thread pool, preventing it from interfering with the async tasks. /// #[derive(Debug)] pub struct SyncIoBridge { From 234f9f36c091d215428b43264d8a831847724ac5 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Wed, 11 Sep 2024 23:09:22 +0100 Subject: [PATCH 07/14] Add documentation for SyncIoBridge with examples and alternatives --- tokio-util/src/io/sync_bridge.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 6ab4b9a0adc..f79e6abb373 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -72,7 +72,7 @@ use tokio::io::{ /// /// /// When the data doesn't fit into memory, the hashing library will usually -/// provide a hasher that you can repeatedly call `update` on to hash the data +/// provide a `hasher` that you can repeatedly call `update` on to hash the data /// one chunk at the time. For example: /// /// ```rust @@ -201,7 +201,7 @@ use tokio::io::{ /// } /// ``` /// Explanation: This example shows how to asynchronously read data into memory -/// and then parse it as JSON. By avoiding `SyncIoBridge`, the asynchronous runtime +/// and then parse it as `JSON`. By avoiding `SyncIoBridge`, the asynchronous runtime /// remains unblocked, leading to better performance when working with asynchronous /// I/O streams. /// From 9e02ebec255c14a9aa6b2771b5d952caf8b480c8 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Sun, 15 Sep 2024 23:31:01 +0100 Subject: [PATCH 08/14] Fix build error for i686-unknown-linux-gnu target in Tokio tests --- tokio-util/src/io/sync_bridge.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index f79e6abb373..5431ec2d75e 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -119,10 +119,10 @@ use tokio::io::{ /// /// ## Example 2: Compressing Data /// -/// When compressing data, avoid using `SyncIoBridge` with non-async compression -/// libraries, as it may lead to inefficient and blocking code. Instead, use an -/// async compression library such as the [`async-compression`](https://docs.rs/async-compression/latest/async_compression/) -/// crate, which is designed to work with asynchronous data streams. +/// When compressing data, the use of `SyncIoBridge` is unnecessary as it introduces +/// blocking and inefficient code. Instead, you can utilize an async compression library +/// such as the [`async-compression`](https://docs.rs/async-compression/latest/async_compression/) +/// crate, which is built to handle asynchronous data streams efficiently. /// /// ```ignore /// use async_compression::tokio::write::GzipEncoder; @@ -159,9 +159,10 @@ use tokio::io::{ /// /// ## Example 3: Parsing `JSON` /// -/// When parsing serialization formats such as `JSON`, avoid using `SyncIoBridge` -/// with functions that `deserialize` data from a type implementing `std::io::Read`, -/// such as `serde_json::from_reader`. +/// +/// `SyncIoBridge` is not ideal when parsing data formats such as `JSON`, as it +/// blocks async operations. A more efficient approach is to read data asynchronously +/// into memory and then `deserialize` it, avoding unnecessary synchronization overhead. /// /// ```rust,no_run /// use tokio::io::AsyncRead; From 4f8c220e3b6f55fa423bbaa5d7e665763d4301c3 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Sun, 15 Sep 2024 23:34:04 +0100 Subject: [PATCH 09/14] cargo fmt error fix --- tokio-util/src/io/sync_bridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 5431ec2d75e..a58e59c0e31 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -159,7 +159,7 @@ use tokio::io::{ /// /// ## Example 3: Parsing `JSON` /// -/// +/// /// `SyncIoBridge` is not ideal when parsing data formats such as `JSON`, as it /// blocks async operations. A more efficient approach is to read data asynchronously /// into memory and then `deserialize` it, avoding unnecessary synchronization overhead. From af07465bcd9d30673b587604283a9115bb5597ca Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Sun, 15 Sep 2024 23:41:10 +0100 Subject: [PATCH 10/14] cargo spell check error fix --- tokio-util/src/io/sync_bridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index a58e59c0e31..050b7cf54b8 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -162,7 +162,7 @@ use tokio::io::{ /// /// `SyncIoBridge` is not ideal when parsing data formats such as `JSON`, as it /// blocks async operations. A more efficient approach is to read data asynchronously -/// into memory and then `deserialize` it, avoding unnecessary synchronization overhead. +/// into memory and then `deserialize` it, avoiding unnecessary synchronization overhead. /// /// ```rust,no_run /// use tokio::io::AsyncRead; From 9cac1e6b7b66e55f6b6ec5cf8d3f0bff92445cb6 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Sat, 25 Jan 2025 16:55:17 +0100 Subject: [PATCH 11/14] more changes --- tokio-util/src/io/sync_bridge.rs | 50 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 050b7cf54b8..a5c0a21a393 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -41,6 +41,9 @@ use tokio::io::{ /// the data fits into memory, the easiest is to read the data into a `Vec` /// and hash it: /// +/// Explanation: This example demonstrates how to asynchronously read data from a +/// reader into memory and hash it using a synchronous hashing function. The +/// `SyncIoBridge` is avoided, ensuring that the async runtime is not blocked. /// ```rust /// use tokio::io::AsyncReadExt; /// use tokio::io::AsyncRead; @@ -66,14 +69,15 @@ use tokio::io::{ /// hash_contents(reader).await /// } /// ``` -/// Explanation: This example demonstrates how to asynchronously read data from a -/// reader into memory and hash it using a synchronous hashing function. The -/// `SyncIoBridge` is avoided, ensuring that the async runtime is not blocked. -/// /// /// When the data doesn't fit into memory, the hashing library will usually /// provide a `hasher` that you can repeatedly call `update` on to hash the data -/// one chunk at the time. For example: +/// one chunk at the time. +/// +/// Explanation: This example demonstrates how to asynchronously stream data in +/// chunks for hashing. Each chunk is read asynchronously, and the hash is updated +/// incrementally. This avoids blocking and improves performance over using +/// `SyncIoBridge`. /// /// ```rust /// use tokio::io::AsyncReadExt; @@ -111,10 +115,6 @@ use tokio::io::{ /// hash_stream(reader, hasher).await /// } /// ``` -/// Explanation: This example demonstrates how to asynchronously stream data in -/// chunks for hashing. Each chunk is read asynchronously, and the hash is updated -/// incrementally. This avoids blocking and improves performance over using -/// `SyncIoBridge`. /// /// /// ## Example 2: Compressing Data @@ -124,6 +124,11 @@ use tokio::io::{ /// such as the [`async-compression`](https://docs.rs/async-compression/latest/async_compression/) /// crate, which is built to handle asynchronous data streams efficiently. /// +/// Explanation: This example shows how to asynchronously compress data using an +/// async compression library. By reading and writing asynchronously, it avoids +/// blocking and is more efficient than using `SyncIoBridge` with a non-async +/// compression library. +/// /// ```ignore /// use async_compression::tokio::write::GzipEncoder; /// use tokio::io::AsyncReadExt; @@ -151,19 +156,20 @@ use tokio::io::{ /// compresses_data(reader).await /// } /// ``` -/// Explanation: This example shows how to asynchronously compress data using an -/// async compression library. By reading and writing asynchronously, it avoids -/// blocking and is more efficient than using `SyncIoBridge` with a non-async -/// compression library. /// /// -/// ## Example 3: Parsing `JSON` +/// ## Example 3: Parsing Data Formats /// /// /// `SyncIoBridge` is not ideal when parsing data formats such as `JSON`, as it /// blocks async operations. A more efficient approach is to read data asynchronously /// into memory and then `deserialize` it, avoiding unnecessary synchronization overhead. /// +/// Explanation: This example shows how to asynchronously read data into memory +/// and then parse it as `JSON`. By avoiding `SyncIoBridge`, the asynchronous runtime +/// remains unblocked, leading to better performance when working with asynchronous +/// I/O streams. +/// /// ```rust,no_run /// use tokio::io::AsyncRead; /// use tokio::io::AsyncReadExt; @@ -201,16 +207,16 @@ use tokio::io::{ /// Ok(()) /// } /// ``` -/// Explanation: This example shows how to asynchronously read data into memory -/// and then parse it as `JSON`. By avoiding `SyncIoBridge`, the asynchronous runtime -/// remains unblocked, leading to better performance when working with asynchronous -/// I/O streams. -/// /// /// ## Correct Usage of `SyncIoBridge` inside `spawn_blocking` /// /// `SyncIoBridge` is mainly useful when you need to interface with synchronous -/// libraries from an asynchronous context. Here is how you can do it correctly: +/// libraries from an asynchronous context. +/// +/// Explanation: This example shows how to use `SyncIoBridge` inside a `spawn_blocking` +/// task to safely perform synchronous I/O without blocking the async runtime. The +/// `spawn_blocking` ensures that the synchronous code is offloaded to a dedicated +/// thread pool, preventing it from interfering with the async tasks. /// /// ```rust /// use tokio::task::spawn_blocking; @@ -253,10 +259,6 @@ use tokio::io::{ /// Ok(()) /// } /// ``` -/// Explanation: This example shows how to use `SyncIoBridge` inside a `spawn_blocking` -/// task to safely perform synchronous I/O without blocking the async runtime. The -/// `spawn_blocking` ensures that the synchronous code is offloaded to a dedicated -/// thread pool, preventing it from interfering with the async tasks. /// #[derive(Debug)] pub struct SyncIoBridge { From a12075a09c0abb4973b1aaa713be2258a19f67c0 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Sun, 26 Jan 2025 14:03:40 +0100 Subject: [PATCH 12/14] fix spellcheck --- tokio-util/src/io/sync_bridge.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index a5c0a21a393..1f178eb2eba 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -19,14 +19,11 @@ use tokio::io::{ /// /// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous /// context by blocking the current thread. However, this can be inefficient because: -/// /// - **Blocking**: The use of `SyncIoBridge` may block a valuable async runtime /// thread, which could otherwise be used to handle more tasks concurrently. -/// /// - **Thread Pool Saturation**: If many threads are blocked using `SyncIoBridge`, /// it can exhaust the async runtime's thread pool, leading to increased latency /// and reduced throughput. -/// /// - **Lack of Parallelism**: By blocking on synchronous operations, you may miss /// out on the benefits of running tasks concurrently, especially in I/O-bound /// operations where async tasks could be interleaved. @@ -234,10 +231,8 @@ use tokio::io::{ /// let result = spawn_blocking(move || { /// // Create an in-memory buffer to hold the copied data. /// let mut buffer = Vec::new(); -/// /// // Copy data from the sync_reader to the buffer. /// std::io::copy(&mut sync_reader, &mut buffer)?; -/// /// // Return the buffer containing the copied data. /// Ok::<_, std::io::Error>(buffer) /// }) From 960ae04e3b1334edad0aa6146ee1f506d459bee3 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Mon, 27 Jan 2025 13:01:48 +0100 Subject: [PATCH 13/14] more changes --- tokio-util/src/io/sync_bridge.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 1f178eb2eba..62fd29214b7 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -17,16 +17,17 @@ use tokio::io::{ /// /// ### Why It Matters: /// -/// `SyncIoBridge` allows you to use synchronous I/O operations in an asynchronous +/// `SyncIoBridge` allows you to use asynchronous I/O operations in an synchronous /// context by blocking the current thread. However, this can be inefficient because: -/// - **Blocking**: The use of `SyncIoBridge` may block a valuable async runtime -/// thread, which could otherwise be used to handle more tasks concurrently. -/// - **Thread Pool Saturation**: If many threads are blocked using `SyncIoBridge`, -/// it can exhaust the async runtime's thread pool, leading to increased latency -/// and reduced throughput. -/// - **Lack of Parallelism**: By blocking on synchronous operations, you may miss -/// out on the benefits of running tasks concurrently, especially in I/O-bound -/// operations where async tasks could be interleaved. +/// - **Inefficient Resource Usage**: `SyncIoBridge` takes up an entire OS thread, +/// which is inefficient compared to asynchronous code that can multiplex many +/// tasks on a single thread. +/// - **Thread Pool Saturation**: Excessive use of `SyncIoBridge` can exhaust the +/// async runtime's thread pool, reducing the number of threads available for +/// other tasks and impacting overall performance. +/// - **Missed Concurrency Benefits**: By using synchronous operations with +/// `SyncIoBridge`, you lose the ability to interleave tasks efficiently, +/// which is a key advantage of asynchronous programming. /// /// ## Example 1: Hashing Data /// From 2f69f18283c7ce9e6a87c908ac08925d6b92eaf2 Mon Sep 17 00:00:00 2001 From: Nathy-bajo Date: Mon, 27 Jan 2025 13:31:14 +0100 Subject: [PATCH 14/14] more changes --- tokio-util/src/io/sync_bridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs index 62fd29214b7..50ac023ac99 100644 --- a/tokio-util/src/io/sync_bridge.rs +++ b/tokio-util/src/io/sync_bridge.rs @@ -223,7 +223,7 @@ use tokio::io::{ /// use std::marker::Unpin; /// use std::io::Cursor; /// -/// /// Wraps an async reader with `SyncIoBridge` and performs syncrhonous I/O operations in a blocking task. +/// /// Wraps an async reader with `SyncIoBridge` and performs synchronous I/O operations in a blocking task. /// async fn process_sync_io(reader: impl AsyncRead + Unpin + Send + 'static) -> Result, std::io::Error> { /// // Wrap the async reader with `SyncIoBridge` to allow synchronous reading. /// let mut sync_reader = SyncIoBridge::new(reader);