Skip to content

Commit 03773e4

Browse files
committed
perf: avoid relocking response stream references
1 parent c9d619c commit 03773e4

8 files changed

Lines changed: 58 additions & 54 deletions

File tree

src/client.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ pub struct Connection<T, B: Buf = Bytes> {
237237
#[must_use = "futures do nothing unless polled"]
238238
pub struct ResponseFuture {
239239
inner: proto::OpaqueStreamRef,
240+
body: Option<proto::OpaqueStreamRef>,
240241
push_promise_consumed: bool,
241242
}
242243

@@ -517,15 +518,16 @@ where
517518
self.inner
518519
.send_request(request, end_of_stream, self.pending.as_ref())
519520
.map_err(Into::into)
520-
.map(|(stream, is_full)| {
521+
.map(|(stream, response, body, is_full)| {
521522
if stream.is_pending_open() && is_full {
522523
// Only prevent sending another request when the request queue
523524
// is not full.
524525
self.pending = Some(stream.clone_to_opaque());
525526
}
526527

527528
let response = ResponseFuture {
528-
inner: stream.clone_to_opaque(),
529+
inner: response,
530+
body: Some(body),
529531
push_promise_consumed: false,
530532
};
531533

@@ -1470,18 +1472,18 @@ impl Future for ResponseFuture {
14701472

14711473
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
14721474
let (parts, _) = ready!(self.inner.poll_response(cx))?.into_parts();
1473-
let body = RecvStream::new(FlowControl::new(self.inner.clone()));
1475+
let body = RecvStream::new(FlowControl::new(
1476+
self.body
1477+
.take()
1478+
.expect("ResponseFuture polled after completion"),
1479+
));
14741480

14751481
Poll::Ready(Ok(Response::from_parts(parts, body)))
14761482
}
14771483
}
14781484

14791485
impl ResponseFuture {
14801486
/// Returns the stream ID of the response stream.
1481-
///
1482-
/// # Panics
1483-
///
1484-
/// If the lock on the stream store has been poisoned.
14851487
pub fn stream_id(&self) -> crate::StreamId {
14861488
crate::StreamId::from_internal(self.inner.stream_id())
14871489
}
@@ -1532,10 +1534,11 @@ impl PushPromises {
15321534
cx: &mut Context<'_>,
15331535
) -> Poll<Option<Result<PushPromise, crate::Error>>> {
15341536
match self.inner.poll_pushed(cx) {
1535-
Poll::Ready(Some(Ok((request, response)))) => {
1537+
Poll::Ready(Some(Ok((request, response, body)))) => {
15361538
let response = PushedResponseFuture {
15371539
inner: ResponseFuture {
15381540
inner: response,
1541+
body: Some(body),
15391542
push_promise_consumed: false,
15401543
},
15411544
};
@@ -1589,10 +1592,6 @@ impl Future for PushedResponseFuture {
15891592

15901593
impl PushedResponseFuture {
15911594
/// Returns the stream ID of the response stream.
1592-
///
1593-
/// # Panics
1594-
///
1595-
/// If the lock on the stream store has been poisoned.
15961595
pub fn stream_id(&self) -> crate::StreamId {
15971596
self.inner.stream_id()
15981597
}

src/proto/streams/store.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ pub(crate) struct Key {
2929
stream_id: StreamId,
3030
}
3131

32+
impl Key {
33+
pub(crate) fn stream_id(self) -> StreamId {
34+
self.stream_id
35+
}
36+
}
37+
3238
// We can never have more than `StreamId::MAX` streams in the store,
3339
// so we can save a smaller index (u32 vs usize).
3440
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

src/proto/streams/streams.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ where
264264
mut request: Request<()>,
265265
end_of_stream: bool,
266266
pending: Option<&OpaqueStreamRef>,
267-
) -> Result<(StreamRef<B>, bool), SendError> {
267+
) -> Result<(StreamRef<B>, OpaqueStreamRef, OpaqueStreamRef, bool), SendError> {
268268
use super::stream::ContentLength;
269269
use http::Method;
270270

@@ -344,14 +344,18 @@ where
344344

345345
// TODO: ideally, OpaqueStreamRefs::new would do this, but we're holding
346346
// the lock, so it can't.
347-
me.refs += 1;
347+
me.refs += 3;
348348

349349
let is_full = me.counts.next_send_stream_will_reach_capacity();
350+
let response = OpaqueStreamRef::new(self.inner.clone(), &mut stream);
351+
let body = OpaqueStreamRef::new(self.inner.clone(), &mut stream);
350352
Ok((
351353
StreamRef {
352354
opaque: OpaqueStreamRef::new(self.inner.clone(), &mut stream),
353355
send_buffer: self.send_buffer.clone(),
354356
},
357+
response,
358+
body,
355359
is_full,
356360
))
357361
}
@@ -1472,7 +1476,7 @@ impl OpaqueStreamRef {
14721476
pub fn poll_pushed(
14731477
&mut self,
14741478
cx: &Context,
1475-
) -> Poll<Option<Result<(Request<()>, OpaqueStreamRef), proto::Error>>> {
1479+
) -> Poll<Option<Result<(Request<()>, OpaqueStreamRef, OpaqueStreamRef), proto::Error>>> {
14761480
let mut me = self.inner.lock().unwrap();
14771481
let me = &mut *me;
14781482

@@ -1481,10 +1485,11 @@ impl OpaqueStreamRef {
14811485
.recv
14821486
.poll_pushed(cx, &mut stream)
14831487
.map_ok(|(h, key)| {
1484-
me.refs += 1;
1485-
let opaque_ref =
1486-
OpaqueStreamRef::new(self.inner.clone(), &mut me.store.resolve(key));
1487-
(h, opaque_ref)
1488+
me.refs += 2;
1489+
let stream = &mut me.store.resolve(key);
1490+
let response = OpaqueStreamRef::new(self.inner.clone(), stream);
1491+
let body = OpaqueStreamRef::new(self.inner.clone(), stream);
1492+
(h, response, body)
14881493
})
14891494
}
14901495

@@ -1557,7 +1562,7 @@ impl OpaqueStreamRef {
15571562
}
15581563

15591564
pub fn stream_id(&self) -> StreamId {
1560-
self.inner.lock().unwrap().store[self.key].id
1565+
self.key.stream_id()
15611566
}
15621567
}
15631568

src/server.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,10 +1297,6 @@ impl<B: Buf> SendResponse<B> {
12971297
}
12981298

12991299
/// Returns the stream ID of the response stream.
1300-
///
1301-
/// # Panics
1302-
///
1303-
/// If the lock on the stream store has been poisoned.
13041300
pub fn stream_id(&self) -> crate::StreamId {
13051301
crate::StreamId::from_internal(self.inner.stream_id())
13061302
}
@@ -1369,10 +1365,6 @@ impl<B: Buf> SendPushedResponse<B> {
13691365
}
13701366

13711367
/// Returns the stream ID of the response stream.
1372-
///
1373-
/// # Panics
1374-
///
1375-
/// If the lock on the stream store has been poisoned.
13761368
pub fn stream_id(&self) -> crate::StreamId {
13771369
self.inner.stream_id()
13781370
}

src/share.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,6 @@ impl<B: Buf> SendStream<B> {
373373
}
374374

375375
/// Returns the stream ID of this `SendStream`.
376-
///
377-
/// # Panics
378-
///
379-
/// If the lock on the stream store has been poisoned.
380376
pub fn stream_id(&self) -> StreamId {
381377
StreamId::from_internal(self.inner.stream_id())
382378
}
@@ -451,10 +447,6 @@ impl RecvStream {
451447
}
452448

453449
/// Returns the stream ID of this stream.
454-
///
455-
/// # Panics
456-
///
457-
/// If the lock on the stream store has been poisoned.
458450
pub fn stream_id(&self) -> StreamId {
459451
self.inner.stream_id()
460452
}

tests/h2-tests/tests/client_request.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use futures::future::{ready, Either};
1+
use futures::future::{poll_fn, ready, Either};
22
use futures::stream::FuturesUnordered;
33
use futures::StreamExt;
44
use h2_support::prelude::*;
@@ -47,12 +47,12 @@ async fn client_other_thread() {
4747
.uri("https://http2.akamai.com/")
4848
.body(())
4949
.unwrap();
50-
let _res = client
51-
.send_request(request, true)
52-
.unwrap()
53-
.0
50+
let mut response = client.send_request(request, true).unwrap().0;
51+
let stream_id = response.stream_id();
52+
let _res = poll_fn(|cx| Pin::new(&mut response).poll(cx))
5453
.await
5554
.expect("request");
55+
assert_eq!(response.stream_id(), stream_id);
5656
});
5757
h2.await.expect("h2");
5858
};

tests/h2-tests/tests/informational_responses.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use futures::{future::poll_fn, StreamExt};
44
use h2_support::prelude::*;
55
use http::{Response, StatusCode};
6+
use std::{pin::Pin, task::Poll};
67

78
#[tokio::test]
89
async fn send_100_continue() {
@@ -297,8 +298,14 @@ async fn client_poll_informational_responses_none() {
297298
sync_sender.send(()).unwrap();
298299

299300
// Get the final response
300-
let response = response_future.await.expect("response error");
301+
let response = poll_fn(|cx| Pin::new(&mut response_future).poll(cx))
302+
.await
303+
.expect("response error");
301304
assert_eq!(response.status(), StatusCode::OK);
305+
assert!(matches!(
306+
poll_fn(|cx| Poll::Ready(response_future.poll_informational(cx))).await,
307+
Poll::Pending
308+
));
302309
let (_hdr, mut recv_stream) = response.into_parts();
303310
let data = recv_stream.data().await.unwrap().unwrap();
304311
assert_eq!("request body", data);

tests/h2-tests/tests/push_promise.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use futures::{StreamExt, TryStreamExt};
1+
use futures::{future::poll_fn, StreamExt, TryStreamExt};
22
use h2_support::prelude::*;
3+
use std::pin::Pin;
34

45
#[tokio::test]
56
async fn recv_push_works() {
@@ -32,27 +33,29 @@ async fn recv_push_works() {
3233
.body(())
3334
.unwrap();
3435
let (mut resp, _) = client.send_request(request, true).unwrap();
35-
let pushed = resp.push_promises();
36-
let check_resp_status = async move {
37-
let resp = resp.await.unwrap();
38-
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
39-
};
40-
let check_pushed_response = async move {
36+
let check_responses = async move {
37+
let response = poll_fn(|cx| Pin::new(&mut resp).poll(cx)).await.unwrap();
38+
assert_eq!(response.status(), StatusCode::NOT_FOUND);
39+
40+
let pushed = resp.push_promises();
4141
let p = pushed.and_then(|headers| async move {
42-
let (request, response) = headers.into_parts();
42+
let (request, mut response) = headers.into_parts();
4343
assert_eq!(request.into_parts().0.method, Method::GET);
44-
let resp = response.await.unwrap();
44+
let stream_id = response.stream_id();
45+
let resp = poll_fn(|cx| Pin::new(&mut response).poll(cx))
46+
.await
47+
.unwrap();
48+
assert_eq!(response.stream_id(), stream_id);
4549
assert_eq!(resp.status(), StatusCode::OK);
4650
let b = util::concat(resp.into_body()).await.unwrap();
4751
assert_eq!(b, "promised_data");
4852
Ok(())
4953
});
5054
let ps: Vec<_> = p.collect().await;
51-
assert_eq!(1, ps.len())
55+
assert_eq!(1, ps.len());
5256
};
5357

54-
h2.drive(join(check_resp_status, check_pushed_response))
55-
.await;
58+
h2.drive(check_responses).await;
5659
};
5760

5861
join(mock, h2).await;

0 commit comments

Comments
 (0)