-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
io: add a copy_bidirectional utility #3572
Merged
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
830e21b
io: add a copy_bidirectional utility
464afdb
fix warnings
c7f5609
Merge remote-tracking branch 'upstream/master' into bidi_copy
conblem 3858936
refactor against issue comments
conblem 6fe69cd
use short hand drop
conblem f9dded1
Merge remote-tracking branch 'upstream/master' into bidi_copy
conblem f614bc0
refactor
64417f0
run fmt
conblem b8ee2a6
fix: comment according to feedback
conblem a47e936
io: add a copy_bidirectional utility
6bf25e4
fix warnings
4e22792
refactor against issue comments
conblem 0ef7cf0
use short hand drop
conblem 35560aa
refactor
eaf4616
run fmt
conblem d9dc8b8
fix: comment according to feedback
conblem b5c4a86
Merge remote-tracking branch 'origin/bidi_copy' into bidi_copy
conblem a6a46c2
fix: comment according to feedback
conblem 3d6651c
Merge remote-tracking branch 'upstream/master' into bidi_copy
conblem File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
use super::copy::CopyBuffer; | ||
|
||
use crate::io::{AsyncRead, AsyncWrite}; | ||
|
||
use std::future::Future; | ||
use std::io; | ||
use std::pin::Pin; | ||
use std::task::{Context, Poll}; | ||
|
||
enum TransferState { | ||
Running(CopyBuffer), | ||
ShuttingDown(u64), | ||
Done(u64), | ||
} | ||
|
||
struct CopyBidirectional<'a, A: ?Sized, B: ?Sized> { | ||
a: &'a mut A, | ||
b: &'a mut B, | ||
a_to_b: TransferState, | ||
b_to_a: TransferState, | ||
} | ||
|
||
fn transfer_one_direction<A, B>( | ||
cx: &mut Context<'_>, | ||
state: &mut TransferState, | ||
r: &mut A, | ||
w: &mut B, | ||
) -> Poll<io::Result<u64>> | ||
where | ||
A: AsyncRead + AsyncWrite + Unpin + ?Sized, | ||
B: AsyncRead + AsyncWrite + Unpin + ?Sized, | ||
{ | ||
let mut r = Pin::new(r); | ||
let mut w = Pin::new(w); | ||
|
||
loop { | ||
match state { | ||
TransferState::Running(buf) => { | ||
let count = ready!(buf.poll_copy(cx, r.as_mut(), w.as_mut()))?; | ||
*state = TransferState::ShuttingDown(count); | ||
} | ||
TransferState::ShuttingDown(count) => { | ||
ready!(w.as_mut().poll_shutdown(cx))?; | ||
|
||
*state = TransferState::Done(*count); | ||
} | ||
TransferState::Done(count) => return Poll::Ready(Ok(*count)), | ||
} | ||
} | ||
} | ||
|
||
impl<'a, A, B> Future for CopyBidirectional<'a, A, B> | ||
where | ||
A: AsyncRead + AsyncWrite + Unpin + ?Sized, | ||
B: AsyncRead + AsyncWrite + Unpin + ?Sized, | ||
{ | ||
type Output = io::Result<(u64, u64)>; | ||
|
||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
// Unpack self into mut refs to each field to avoid borrow check issues. | ||
let CopyBidirectional { | ||
a, | ||
b, | ||
a_to_b, | ||
b_to_a, | ||
} = &mut *self; | ||
|
||
let a_to_b = transfer_one_direction(cx, a_to_b, &mut *a, &mut *b)?; | ||
let b_to_a = transfer_one_direction(cx, b_to_a, &mut *b, &mut *a)?; | ||
|
||
// It is not a problem if ready! returns early because transfer_one_direction for the | ||
// other direction will keep returning TransferState::Done(count) in future calls to poll | ||
let a_to_b = ready!(a_to_b); | ||
let b_to_a = ready!(b_to_a); | ||
|
||
Poll::Ready(Ok((a_to_b, b_to_a))) | ||
} | ||
} | ||
|
||
/// Copies data in both directions between `a` and `b`. | ||
/// | ||
/// This function returns a future that will read from both streams, | ||
/// writing any data read to the opposing stream. | ||
/// This happens in both directions concurrently. | ||
/// | ||
/// If an EOF is observed on one stream, [`shutdown()`] will be invoked on | ||
/// the other, and reading from that stream will stop. Copying of data in | ||
/// the other direction will continue. | ||
/// | ||
/// The future will complete successfully once both directions of communication has been shut down. | ||
/// A direction is shut down when the reader reports EOF, | ||
/// at which point [`shutdown()`] is called on the corresponding writer. When finished, | ||
/// it will return a tuple of the number of bytes copied from a to b | ||
/// and the number of bytes copied from b to a, in that order. | ||
/// | ||
/// [`shutdown()`]: crate::io::AsyncWriteExt::shutdown | ||
/// | ||
/// # Errors | ||
/// | ||
/// The future will immediately return an error if any IO operation on `a` | ||
/// or `b` returns an error. Some data read from either stream may be lost (not | ||
/// written to the other stream) in this case. | ||
/// | ||
/// # Return value | ||
/// | ||
/// Returns a tuple of bytes copied `a` to `b` and bytes copied `b` to `a`. | ||
pub async fn copy_bidirectional<A, B>(a: &mut A, b: &mut B) -> Result<(u64, u64), std::io::Error> | ||
where | ||
A: AsyncRead + AsyncWrite + Unpin + ?Sized, | ||
B: AsyncRead + AsyncWrite + Unpin + ?Sized, | ||
{ | ||
CopyBidirectional { | ||
a, | ||
b, | ||
a_to_b: TransferState::Running(CopyBuffer::new()), | ||
b_to_a: TransferState::Running(CopyBuffer::new()), | ||
} | ||
.await | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make sense to try to read more data if the write returns
Pending
whenme.cap < 2048
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will look into this