Repository: tokio-rs/tokio (Rust)

Issue:
Add a way to shutdown SyncIoBridge
**Is your feature request related to a problem? Please describe.**
`SyncIoBridge`(https://github.com/tokio-rs/tokio/pull/4146) does not provide a way to call the `shutdown()` function of its internal `tokio::io::AsyncWrite` instance. 

**Describe the solution you'd like**
Add a `shutdown()` function to `SyncIoBridge`, which invoke the shutdown() function of `src` field

**Describe alternatives you've considered**
n/a

**Additional context**
Take `tokio::io::DuplexStream` as en example. Its `shutdown()` can be used to close the writer of stream, but in the following example, I have no way to shutdown:
```rust
let (s1, _s2) = tokio::io::duplex(1024);
let (rh, wh) = tokio::io::split(s1);
tokio::task::spawn_blocking(move || {
    let mut wh = SyncIoBridge::new(wh);
    // wh.write_all() ...
    // Now I need to shutdown the write half
    wh.shutdown(); // I need this function
});
```



Discussion:
@Darksonn What do you think about the proposal to add `SyncIoBridge::shutdown()`? If appropriate, I can create a PR for this.
Adding a method for this sounds good to me.



Patch:
diff --git a/tokio-util/src/io/sync_bridge.rs b/tokio-util/src/io/sync_bridge.rs
index 5587175b947..5682258c3b3 100644
--- a/tokio-util/src/io/sync_bridge.rs
+++ b/tokio-util/src/io/sync_bridge.rs
@@ -66,6 +66,21 @@ impl<T: AsyncWrite> SyncIoBridge<T> {
     }
 }
 
+impl<T: AsyncWrite + Unpin> SyncIoBridge<T> {
+    /// Shutdown this writer. This method provides a way to call the [`AsyncWriteExt::shutdown`]
+    /// function of the inner [`tokio::io::AsyncWrite`] instance.
+    ///
+    /// # Errors
+    ///
+    /// This method returns the same errors as [`AsyncWriteExt::shutdown`].
+    ///
+    /// [`AsyncWriteExt::shutdown`]: tokio::io::AsyncWriteExt::shutdown
+    pub fn shutdown(&mut self) -> std::io::Result<()> {
+        let src = &mut self.src;
+        self.rt.block_on(src.shutdown())
+    }
+}
+
 impl<T: Unpin> SyncIoBridge<T> {
     /// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
     /// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].


Test patch:
diff --git a/tokio-util/tests/io_sync_bridge.rs b/tokio-util/tests/io_sync_bridge.rs
index e22631e48c9..76bbd0b4e53 100644
--- a/tokio-util/tests/io_sync_bridge.rs
+++ b/tokio-util/tests/io_sync_bridge.rs
@@ -2,8 +2,8 @@
 #![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads
 
 use std::error::Error;
-use std::io::{Cursor, Read, Result as IoResult};
-use tokio::io::AsyncRead;
+use std::io::{Cursor, Read, Result as IoResult, Write};
+use tokio::io::{AsyncRead, AsyncReadExt};
 use tokio_util::io::SyncIoBridge;
 
 async fn test_reader_len(
@@ -42,3 +42,21 @@ async fn test_async_write_to_sync() -> Result<(), Box<dyn Error>> {
     assert_eq!(dest.as_slice(), src);
     Ok(())
 }
+
+#[tokio::test]
+async fn test_shutdown() -> Result<(), Box<dyn Error>> {
+    let (s1, mut s2) = tokio::io::duplex(1024);
+    let (_rh, wh) = tokio::io::split(s1);
+    tokio::task::spawn_blocking(move || -> std::io::Result<_> {
+        let mut wh = SyncIoBridge::new(wh);
+        wh.write_all(b"hello")?;
+        wh.shutdown()?;
+        assert!(wh.write_all(b" world").is_err());
+        Ok(())
+    })
+    .await??;
+    let mut buf = vec![];
+    s2.read_to_end(&mut buf).await?;
+    assert_eq!(buf, b"hello");
+    Ok(())
+}