Skip to content

Commit

Permalink
copy_both: handle Future cancellation case
Browse files Browse the repository at this point in the history
When a user starts a CopyBoth query a state machine is created that is
responsible for driving the CopyBoth protocol forward regardless of what
the user does with the returned handles. The state machine starts in the
`Setup` phase and only gives control back to the user (in the form of
the returned handles) after having finished with the Setup. Therefore
the code assumed (incorrectly) that the handles will never be dropped
while in Setup mode.

It turns out there is one more way of dropping the handle even while in
the Setup phase, and that is to cancel the entire Future object returned
by the `copy_both` method at just the right time. This resulted in the
state machine observing that the handles were dropped while still in the
Setup phase, which was asserted to never happen.

This PR fixes the problem by handling the case where a handles are
dropped while still in the `Setup` phase.

Signed-off-by: Petros Angelatos <[email protected]>
  • Loading branch information
petrosagg committed Oct 17, 2024
1 parent 23d1615 commit aea9b1b
Showing 1 changed file with 24 additions and 11 deletions.
35 changes: 24 additions & 11 deletions tokio-postgres/src/copy_both.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use std::task::{Context, Poll};
/// The state machine of CopyBothReceiver
///
/// ```ignore
/// Setup
/// |
/// v
/// CopyBoth
/// / \
/// v v
Expand Down Expand Up @@ -189,17 +192,27 @@ impl Stream for CopyBothReceiver {
Poll::Pending => match self.state {
Setup | CopyBoth | CopyIn => match ready!(self.sink_receiver.poll_next_unpin(cx)) {
Some(msg) => Poll::Ready(Some(msg)),
None => {
self.state = match self.state {
CopyBoth => CopyOut,
CopyIn => CopyNone,
_ => unreachable!(),
};

let mut buf = BytesMut::new();
frontend::copy_done(&mut buf);
Poll::Ready(Some(FrontendMessage::Raw(buf.freeze())))
}
None => match self.state {
// The user has cancelled their interest to this CopyBoth query but we're
// still in the Setup phase. From this point the receiver will either enter
// CopyBoth mode or will receive an Error response from PostgreSQL. When
// either of those happens the state machine will terminate the connection
// appropriately.
Setup => Poll::Pending,
CopyBoth => {
self.state = CopyOut;
let mut buf = BytesMut::new();
frontend::copy_done(&mut buf);
Poll::Ready(Some(FrontendMessage::Raw(buf.freeze())))
}
CopyIn => {
self.state = CopyNone;
let mut buf = BytesMut::new();
frontend::copy_done(&mut buf);
Poll::Ready(Some(FrontendMessage::Raw(buf.freeze())))
}
_ => unreachable!(),
},
},
_ => Poll::Pending,
},
Expand Down

0 comments on commit aea9b1b

Please sign in to comment.