mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-06 22:22:38 +03:00
Use F14FastMap as the retranmission buffer.
Summary: The retransmission buffer tracks stream frame data we have sent that is currently unacked. We keep this as a sorted `deque`. This isn't so bad for performance, but we can do better if we break ourselves of the requirement that it be sorted (removing a binary search on ACK). To do this we make the buffer a map of offset -> `StreamBuffer`. There were two places that were dependent on the sorted nature of the list. 1. For partial reliablity we call `shrinkBuffers` to remove all unacked buffers less than an offset. For this we now have to do it with a full traversal of the retransmission buffer instead of only having to do an O(offset) search. In the future we could make this better by only lazily deleting from the retransmission buffer on ACK or packet loss. 2. We used the start of the retransmission buffer to determine if a delivery callback could be fired for a given offset. We need some new state to track this. Instead of tracking unacked buffers, we now track acked ranges using the existing `IntervalSet`. This set should be small for the typical case, as we think most ACKs will come in order and just cause existing ranges to merge. Reviewed By: yangchi Differential Revision: D18609467 fbshipit-source-id: 13cd2164352f1183362be9f675c1bdc686426698
This commit is contained in:
committed by
Facebook Github Bot
parent
7e4bcf30cf
commit
b6e134fdee
@@ -98,11 +98,13 @@ void handleNewStreamDataWritten(
|
|||||||
stream.currentWriteOffset += frameLen;
|
stream.currentWriteOffset += frameLen;
|
||||||
auto bufWritten = stream.writeBuffer.split(folly::to<size_t>(frameLen));
|
auto bufWritten = stream.writeBuffer.split(folly::to<size_t>(frameLen));
|
||||||
stream.currentWriteOffset += frameFin ? 1 : 0;
|
stream.currentWriteOffset += frameFin ? 1 : 0;
|
||||||
CHECK(
|
CHECK(stream.retransmissionBuffer
|
||||||
stream.retransmissionBuffer.empty() ||
|
.emplace(
|
||||||
stream.retransmissionBuffer.back().offset < originalOffset);
|
std::piecewise_construct,
|
||||||
stream.retransmissionBuffer.emplace_back(
|
std::forward_as_tuple(originalOffset),
|
||||||
std::move(bufWritten), originalOffset, frameFin);
|
std::forward_as_tuple(
|
||||||
|
std::move(bufWritten), originalOffset, frameFin))
|
||||||
|
.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleRetransmissionWritten(
|
void handleRetransmissionWritten(
|
||||||
@@ -126,17 +128,13 @@ void handleRetransmissionWritten(
|
|||||||
lossBufferIter->offset += frameLen;
|
lossBufferIter->offset += frameLen;
|
||||||
bufWritten = lossBufferIter->data.split(frameLen);
|
bufWritten = lossBufferIter->data.split(frameLen);
|
||||||
}
|
}
|
||||||
stream.retransmissionBuffer.emplace(
|
CHECK(stream.retransmissionBuffer
|
||||||
std::upper_bound(
|
.emplace(
|
||||||
stream.retransmissionBuffer.begin(),
|
std::piecewise_construct,
|
||||||
stream.retransmissionBuffer.end(),
|
std::forward_as_tuple(frameOffset),
|
||||||
frameOffset,
|
std::forward_as_tuple(
|
||||||
[](const auto& offsetIn, const auto& buffer) {
|
std::move(bufWritten), frameOffset, frameFin))
|
||||||
return offsetIn < buffer.offset;
|
.second);
|
||||||
}),
|
|
||||||
std::move(bufWritten),
|
|
||||||
frameOffset,
|
|
||||||
frameFin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -224,7 +224,7 @@ TEST_F(QuicTransportFunctionsTest, TestUpdateConnection) {
|
|||||||
EXPECT_TRUE(conn->outstandingPackets.back().isAppLimited);
|
EXPECT_TRUE(conn->outstandingPackets.back().isAppLimited);
|
||||||
|
|
||||||
EXPECT_EQ(stream1->retransmissionBuffer.size(), 1);
|
EXPECT_EQ(stream1->retransmissionBuffer.size(), 1);
|
||||||
auto& rt1 = stream1->retransmissionBuffer.front();
|
auto& rt1 = stream1->retransmissionBuffer.at(0);
|
||||||
|
|
||||||
EXPECT_EQ(stream1->currentWriteOffset, 5);
|
EXPECT_EQ(stream1->currentWriteOffset, 5);
|
||||||
EXPECT_EQ(stream2->currentWriteOffset, 13);
|
EXPECT_EQ(stream2->currentWriteOffset, 13);
|
||||||
@@ -234,7 +234,7 @@ TEST_F(QuicTransportFunctionsTest, TestUpdateConnection) {
|
|||||||
EXPECT_TRUE(eq(*IOBuf::copyBuffer("hey w"), *rt1.data.front()));
|
EXPECT_TRUE(eq(*IOBuf::copyBuffer("hey w"), *rt1.data.front()));
|
||||||
|
|
||||||
EXPECT_EQ(stream2->retransmissionBuffer.size(), 1);
|
EXPECT_EQ(stream2->retransmissionBuffer.size(), 1);
|
||||||
auto& rt2 = stream2->retransmissionBuffer.front();
|
auto& rt2 = stream2->retransmissionBuffer.at(0);
|
||||||
|
|
||||||
EXPECT_EQ(rt2.offset, 0);
|
EXPECT_EQ(rt2.offset, 0);
|
||||||
EXPECT_TRUE(eq(*buf, *rt2.data.front()));
|
EXPECT_TRUE(eq(*buf, *rt2.data.front()));
|
||||||
@@ -244,9 +244,9 @@ TEST_F(QuicTransportFunctionsTest, TestUpdateConnection) {
|
|||||||
|
|
||||||
// Testing retransmission
|
// Testing retransmission
|
||||||
stream1->lossBuffer.push_back(std::move(rt1));
|
stream1->lossBuffer.push_back(std::move(rt1));
|
||||||
stream1->retransmissionBuffer.pop_front();
|
stream1->retransmissionBuffer.clear();
|
||||||
stream2->lossBuffer.push_back(std::move(rt2));
|
stream2->lossBuffer.push_back(std::move(rt2));
|
||||||
stream2->retransmissionBuffer.pop_front();
|
stream2->retransmissionBuffer.clear();
|
||||||
conn->streamManager->addLoss(stream1->id);
|
conn->streamManager->addLoss(stream1->id);
|
||||||
conn->streamManager->addLoss(stream2->id);
|
conn->streamManager->addLoss(stream2->id);
|
||||||
|
|
||||||
@@ -285,17 +285,17 @@ TEST_F(QuicTransportFunctionsTest, TestUpdateConnection) {
|
|||||||
|
|
||||||
EXPECT_EQ(stream1->lossBuffer.size(), 0);
|
EXPECT_EQ(stream1->lossBuffer.size(), 0);
|
||||||
EXPECT_EQ(stream1->retransmissionBuffer.size(), 2);
|
EXPECT_EQ(stream1->retransmissionBuffer.size(), 2);
|
||||||
auto& rt3 = stream1->retransmissionBuffer.back();
|
auto& rt3 = stream1->retransmissionBuffer.at(5);
|
||||||
EXPECT_TRUE(eq(IOBuf::copyBuffer("hats up"), rt3.data.move()));
|
EXPECT_TRUE(eq(IOBuf::copyBuffer("hats up"), rt3.data.move()));
|
||||||
|
|
||||||
auto& rt4 = stream1->retransmissionBuffer.front();
|
auto& rt4 = stream1->retransmissionBuffer.at(0);
|
||||||
EXPECT_TRUE(eq(*IOBuf::copyBuffer("hey w"), *rt4.data.front()));
|
EXPECT_TRUE(eq(*IOBuf::copyBuffer("hey w"), *rt4.data.front()));
|
||||||
|
|
||||||
// loss buffer should be split into 2. Part in retransmission buffer and
|
// loss buffer should be split into 2. Part in retransmission buffer and
|
||||||
// part remains in loss buffer.
|
// part remains in loss buffer.
|
||||||
EXPECT_EQ(stream2->lossBuffer.size(), 1);
|
EXPECT_EQ(stream2->lossBuffer.size(), 1);
|
||||||
EXPECT_EQ(stream2->retransmissionBuffer.size(), 1);
|
EXPECT_EQ(stream2->retransmissionBuffer.size(), 1);
|
||||||
auto& rt5 = stream2->retransmissionBuffer.front();
|
auto& rt5 = stream2->retransmissionBuffer.at(0);
|
||||||
EXPECT_TRUE(eq(*IOBuf::copyBuffer("hey wh"), *rt5.data.front()));
|
EXPECT_TRUE(eq(*IOBuf::copyBuffer("hey wh"), *rt5.data.front()));
|
||||||
EXPECT_EQ(rt5.offset, 0);
|
EXPECT_EQ(rt5.offset, 0);
|
||||||
EXPECT_EQ(rt5.eof, 0);
|
EXPECT_EQ(rt5.eof, 0);
|
||||||
@@ -434,7 +434,7 @@ TEST_F(QuicTransportFunctionsTest, TestUpdateConnectionFinOnly) {
|
|||||||
EXPECT_TRUE(frame->fin);
|
EXPECT_TRUE(frame->fin);
|
||||||
|
|
||||||
EXPECT_EQ(stream1->retransmissionBuffer.size(), 1);
|
EXPECT_EQ(stream1->retransmissionBuffer.size(), 1);
|
||||||
auto& rt1 = stream1->retransmissionBuffer.front();
|
auto& rt1 = stream1->retransmissionBuffer.at(0);
|
||||||
|
|
||||||
EXPECT_EQ(stream1->currentWriteOffset, 1);
|
EXPECT_EQ(stream1->currentWriteOffset, 1);
|
||||||
EXPECT_EQ(rt1.offset, 0);
|
EXPECT_EQ(rt1.offset, 0);
|
||||||
@@ -481,7 +481,7 @@ TEST_F(QuicTransportFunctionsTest, TestUpdateConnectionAllBytesExceptFin) {
|
|||||||
EXPECT_EQ(stream1->currentWriteOffset, buf->computeChainDataLength());
|
EXPECT_EQ(stream1->currentWriteOffset, buf->computeChainDataLength());
|
||||||
|
|
||||||
EXPECT_EQ(stream1->retransmissionBuffer.size(), 1);
|
EXPECT_EQ(stream1->retransmissionBuffer.size(), 1);
|
||||||
auto& rt1 = stream1->retransmissionBuffer.front();
|
auto& rt1 = stream1->retransmissionBuffer.at(0);
|
||||||
EXPECT_EQ(rt1.offset, 0);
|
EXPECT_EQ(rt1.offset, 0);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
rt1.data.front()->computeChainDataLength(),
|
rt1.data.front()->computeChainDataLength(),
|
||||||
@@ -1217,9 +1217,6 @@ TEST_F(QuicTransportFunctionsTest, WriteQuicDataToSocketRetxBufferSorted) {
|
|||||||
getVersion(*conn),
|
getVersion(*conn),
|
||||||
conn->transportSettings.writeConnectionDataPacketsLimit);
|
conn->transportSettings.writeConnectionDataPacketsLimit);
|
||||||
EXPECT_EQ(2, stream->retransmissionBuffer.size());
|
EXPECT_EQ(2, stream->retransmissionBuffer.size());
|
||||||
EXPECT_GT(
|
|
||||||
stream->retransmissionBuffer.back().offset,
|
|
||||||
stream->retransmissionBuffer.front().offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicTransportFunctionsTest, NothingWritten) {
|
TEST_F(QuicTransportFunctionsTest, NothingWritten) {
|
||||||
|
@@ -242,22 +242,17 @@ void dropPackets(QuicServerConnectionState& conn) {
|
|||||||
}
|
}
|
||||||
auto stream = conn.streamManager->findStream(streamFrame->streamId);
|
auto stream = conn.streamManager->findStream(streamFrame->streamId);
|
||||||
ASSERT_TRUE(stream);
|
ASSERT_TRUE(stream);
|
||||||
auto itr = std::find_if(
|
auto itr = stream->retransmissionBuffer.find(streamFrame->offset);
|
||||||
stream->retransmissionBuffer.begin(),
|
|
||||||
stream->retransmissionBuffer.end(),
|
|
||||||
[&streamFrame](const auto& buffer) {
|
|
||||||
return streamFrame->offset == buffer.offset;
|
|
||||||
});
|
|
||||||
ASSERT_TRUE(itr != stream->retransmissionBuffer.end());
|
ASSERT_TRUE(itr != stream->retransmissionBuffer.end());
|
||||||
stream->lossBuffer.insert(
|
stream->lossBuffer.insert(
|
||||||
std::upper_bound(
|
std::upper_bound(
|
||||||
stream->lossBuffer.begin(),
|
stream->lossBuffer.begin(),
|
||||||
stream->lossBuffer.end(),
|
stream->lossBuffer.end(),
|
||||||
itr->offset,
|
itr->second.offset,
|
||||||
[](const auto& offset, const auto& buffer) {
|
[](const auto& offset, const auto& buffer) {
|
||||||
return offset < buffer.offset;
|
return offset < buffer.offset;
|
||||||
}),
|
}),
|
||||||
std::move(*itr));
|
std::move(itr->second));
|
||||||
stream->retransmissionBuffer.erase(itr);
|
stream->retransmissionBuffer.erase(itr);
|
||||||
if (std::find(
|
if (std::find(
|
||||||
conn.streamManager->lossStreams().begin(),
|
conn.streamManager->lossStreams().begin(),
|
||||||
@@ -313,15 +308,24 @@ void verifyCorrectness(
|
|||||||
// Verify retransmissionBuffer:
|
// Verify retransmissionBuffer:
|
||||||
EXPECT_FALSE(stream->retransmissionBuffer.empty());
|
EXPECT_FALSE(stream->retransmissionBuffer.empty());
|
||||||
IOBufQueue retxBufCombined;
|
IOBufQueue retxBufCombined;
|
||||||
for (auto& retxBuf : stream->retransmissionBuffer) {
|
std::vector<StreamBuffer> rtxCopy;
|
||||||
retxBufCombined.append(retxBuf.data.front()->clone());
|
for (auto& itr : stream->retransmissionBuffer) {
|
||||||
|
rtxCopy.push_back(StreamBuffer(
|
||||||
|
itr.second.data.front()->clone(), itr.second.offset, itr.second.eof));
|
||||||
|
}
|
||||||
|
std::sort(rtxCopy.begin(), rtxCopy.end(), [](auto& s1, auto& s2) {
|
||||||
|
return s1.offset < s2.offset;
|
||||||
|
});
|
||||||
|
for (auto& s : rtxCopy) {
|
||||||
|
retxBufCombined.append(s.data.move());
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(IOBufEqualTo()(expected, *retxBufCombined.move()));
|
EXPECT_TRUE(IOBufEqualTo()(expected, *retxBufCombined.move()));
|
||||||
EXPECT_EQ(finExpected, stream->retransmissionBuffer.back().eof);
|
EXPECT_EQ(finExpected, stream->retransmissionBuffer.at(offsets.back()).eof);
|
||||||
std::vector<uint64_t> retxBufOffsets;
|
std::vector<uint64_t> retxBufOffsets;
|
||||||
for (const auto& b : stream->retransmissionBuffer) {
|
for (const auto& b : stream->retransmissionBuffer) {
|
||||||
retxBufOffsets.push_back(b.offset);
|
retxBufOffsets.push_back(b.second.offset);
|
||||||
}
|
}
|
||||||
|
std::sort(retxBufOffsets.begin(), retxBufOffsets.end());
|
||||||
EXPECT_EQ(offsets, retxBufOffsets);
|
EXPECT_EQ(offsets, retxBufOffsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1196,9 +1200,8 @@ TEST_F(QuicTransportTest, CloneAfterRecvReset) {
|
|||||||
EXPECT_EQ(1, conn.outstandingPackets.size());
|
EXPECT_EQ(1, conn.outstandingPackets.size());
|
||||||
auto stream = conn.streamManager->getStream(streamId);
|
auto stream = conn.streamManager->getStream(streamId);
|
||||||
EXPECT_EQ(1, stream->retransmissionBuffer.size());
|
EXPECT_EQ(1, stream->retransmissionBuffer.size());
|
||||||
EXPECT_EQ(0, stream->retransmissionBuffer.back().offset);
|
EXPECT_EQ(0, stream->retransmissionBuffer.at(0).data.chainLength());
|
||||||
EXPECT_EQ(0, stream->retransmissionBuffer.back().data.chainLength());
|
EXPECT_TRUE(stream->retransmissionBuffer.at(0).eof);
|
||||||
EXPECT_TRUE(stream->retransmissionBuffer.back().eof);
|
|
||||||
EXPECT_TRUE(stream->lossBuffer.empty());
|
EXPECT_TRUE(stream->lossBuffer.empty());
|
||||||
EXPECT_EQ(0, stream->writeBuffer.chainLength());
|
EXPECT_EQ(0, stream->writeBuffer.chainLength());
|
||||||
EXPECT_EQ(1, stream->currentWriteOffset);
|
EXPECT_EQ(1, stream->currentWriteOffset);
|
||||||
@@ -1927,8 +1930,11 @@ TEST_F(QuicTransportTest, InvokeDeliveryCallbacksRetxBuffer) {
|
|||||||
conn.lossState.srtt = 100us;
|
conn.lossState.srtt = 100us;
|
||||||
auto streamState = conn.streamManager->getStream(stream);
|
auto streamState = conn.streamManager->getStream(stream);
|
||||||
streamState->retransmissionBuffer.clear();
|
streamState->retransmissionBuffer.clear();
|
||||||
streamState->retransmissionBuffer.emplace_back(
|
streamState->retransmissionBuffer.emplace(
|
||||||
folly::IOBuf::copyBuffer("But i'm not delivered yet"), 51, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(51),
|
||||||
|
std::forward_as_tuple(
|
||||||
|
folly::IOBuf::copyBuffer("But i'm not delivered yet"), 51, false));
|
||||||
|
|
||||||
folly::SocketAddress addr;
|
folly::SocketAddress addr;
|
||||||
NetworkData emptyData;
|
NetworkData emptyData;
|
||||||
@@ -1970,8 +1976,11 @@ TEST_F(QuicTransportTest, InvokeDeliveryCallbacksLossAndRetxBuffer) {
|
|||||||
auto streamState = conn.streamManager->getStream(stream);
|
auto streamState = conn.streamManager->getStream(stream);
|
||||||
streamState->retransmissionBuffer.clear();
|
streamState->retransmissionBuffer.clear();
|
||||||
streamState->lossBuffer.clear();
|
streamState->lossBuffer.clear();
|
||||||
streamState->retransmissionBuffer.emplace_back(
|
streamState->retransmissionBuffer.emplace(
|
||||||
folly::IOBuf::copyBuffer("But i'm not delivered yet"), 51, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(51),
|
||||||
|
std::forward_as_tuple(
|
||||||
|
folly::IOBuf::copyBuffer("But i'm not delivered yet"), 51, false));
|
||||||
streamState->lossBuffer.emplace_back(
|
streamState->lossBuffer.emplace_back(
|
||||||
folly::IOBuf::copyBuffer("And I'm lost"), 31, false);
|
folly::IOBuf::copyBuffer("And I'm lost"), 31, false);
|
||||||
|
|
||||||
|
@@ -200,23 +200,17 @@ Buf PacketRebuilder::cloneCryptoRetransmissionBuffer(
|
|||||||
* lost packet.
|
* lost packet.
|
||||||
*/
|
*/
|
||||||
DCHECK(frame.len) << "WriteCryptoFrame cloning: frame is empty. " << conn_;
|
DCHECK(frame.len) << "WriteCryptoFrame cloning: frame is empty. " << conn_;
|
||||||
auto iter = std::lower_bound(
|
auto iter = stream.retransmissionBuffer.find(frame.offset);
|
||||||
stream.retransmissionBuffer.begin(),
|
|
||||||
stream.retransmissionBuffer.end(),
|
|
||||||
frame.offset,
|
|
||||||
[](const auto& buffer, const auto& targetOffset) {
|
|
||||||
return buffer.offset < targetOffset;
|
|
||||||
});
|
|
||||||
|
|
||||||
// If the crypto stream is canceled somehow, just skip cloning this frame
|
// If the crypto stream is canceled somehow, just skip cloning this frame
|
||||||
if (iter == stream.retransmissionBuffer.end()) {
|
if (iter == stream.retransmissionBuffer.end()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
DCHECK(iter->offset == frame.offset)
|
DCHECK(iter->second.offset == frame.offset)
|
||||||
<< "WriteCryptoFrame cloning: offset mismatch. " << conn_;
|
<< "WriteCryptoFrame cloning: offset mismatch. " << conn_;
|
||||||
DCHECK(iter->data.chainLength() == frame.len)
|
DCHECK(iter->second.data.chainLength() == frame.len)
|
||||||
<< "WriteCryptoFrame cloning: Len mismatch. " << conn_;
|
<< "WriteCryptoFrame cloning: Len mismatch. " << conn_;
|
||||||
return iter->data.front()->clone();
|
return iter->second.data.front()->clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
Buf PacketRebuilder::cloneRetransmissionBuffer(
|
Buf PacketRebuilder::cloneRetransmissionBuffer(
|
||||||
@@ -236,19 +230,13 @@ Buf PacketRebuilder::cloneRetransmissionBuffer(
|
|||||||
*/
|
*/
|
||||||
DCHECK(stream);
|
DCHECK(stream);
|
||||||
DCHECK(retransmittable(*stream));
|
DCHECK(retransmittable(*stream));
|
||||||
auto iter = std::lower_bound(
|
auto iter = stream->retransmissionBuffer.find(frame.offset);
|
||||||
stream->retransmissionBuffer.begin(),
|
|
||||||
stream->retransmissionBuffer.end(),
|
|
||||||
frame.offset,
|
|
||||||
[](const auto& buffer, const auto& targetOffset) {
|
|
||||||
return buffer.offset < targetOffset;
|
|
||||||
});
|
|
||||||
if (iter != stream->retransmissionBuffer.end()) {
|
if (iter != stream->retransmissionBuffer.end()) {
|
||||||
if (streamFrameMatchesRetransmitBuffer(*stream, frame, *iter)) {
|
if (streamFrameMatchesRetransmitBuffer(*stream, frame, iter->second)) {
|
||||||
DCHECK(!frame.len || !iter->data.empty())
|
DCHECK(!frame.len || !iter->second.data.empty())
|
||||||
<< "WriteStreamFrame cloning: frame is not empty but StreamBuffer has"
|
<< "WriteStreamFrame cloning: frame is not empty but StreamBuffer has"
|
||||||
<< " empty data. " << conn_;
|
<< " empty data. " << conn_;
|
||||||
return (frame.len ? iter->data.front()->clone() : nullptr);
|
return (frame.len ? iter->second.data.front()->clone() : nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@@ -99,12 +99,13 @@ TEST_F(QuicPacketRebuilderTest, RebuildPacket) {
|
|||||||
auto packet1 = std::move(regularBuilder1).buildPacket();
|
auto packet1 = std::move(regularBuilder1).buildPacket();
|
||||||
ASSERT_EQ(8, packet1.packet.frames.size());
|
ASSERT_EQ(8, packet1.packet.frames.size());
|
||||||
stream->retransmissionBuffer.emplace(
|
stream->retransmissionBuffer.emplace(
|
||||||
stream->retransmissionBuffer.begin(), buf->clone(), 0, true);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(buf->clone(), 0, true));
|
||||||
conn.cryptoState->oneRttStream.retransmissionBuffer.emplace(
|
conn.cryptoState->oneRttStream.retransmissionBuffer.emplace(
|
||||||
conn.cryptoState->oneRttStream.retransmissionBuffer.begin(),
|
std::piecewise_construct,
|
||||||
cryptoBuf->clone(),
|
std::forward_as_tuple(0),
|
||||||
0,
|
std::forward_as_tuple(cryptoBuf->clone(), 0, true));
|
||||||
true);
|
|
||||||
|
|
||||||
// rebuild a packet from the built out packet
|
// rebuild a packet from the built out packet
|
||||||
ShortHeader shortHeader2(
|
ShortHeader shortHeader2(
|
||||||
@@ -240,7 +241,9 @@ TEST_F(QuicPacketRebuilderTest, FinOnlyStreamRebuild) {
|
|||||||
writeStreamFrameHeader(regularBuilder1, streamId, 0, 0, 0, true);
|
writeStreamFrameHeader(regularBuilder1, streamId, 0, 0, 0, true);
|
||||||
auto packet1 = std::move(regularBuilder1).buildPacket();
|
auto packet1 = std::move(regularBuilder1).buildPacket();
|
||||||
stream->retransmissionBuffer.emplace(
|
stream->retransmissionBuffer.emplace(
|
||||||
stream->retransmissionBuffer.begin(), nullptr, 0, true);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(nullptr, 0, true));
|
||||||
|
|
||||||
// rebuild a packet from the built out packet
|
// rebuild a packet from the built out packet
|
||||||
ShortHeader shortHeader2(
|
ShortHeader shortHeader2(
|
||||||
@@ -294,7 +297,9 @@ TEST_F(QuicPacketRebuilderTest, RebuildDataStreamAndEmptyCryptoStream) {
|
|||||||
auto packet1 = std::move(regularBuilder1).buildPacket();
|
auto packet1 = std::move(regularBuilder1).buildPacket();
|
||||||
ASSERT_EQ(2, packet1.packet.frames.size());
|
ASSERT_EQ(2, packet1.packet.frames.size());
|
||||||
stream->retransmissionBuffer.emplace(
|
stream->retransmissionBuffer.emplace(
|
||||||
stream->retransmissionBuffer.begin(), buf->clone(), 0, true);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(buf->clone(), 0, true));
|
||||||
// Do not add the buf to crypto stream's retransmission buffer,
|
// Do not add the buf to crypto stream's retransmission buffer,
|
||||||
// imagine it was cleared
|
// imagine it was cleared
|
||||||
|
|
||||||
@@ -390,7 +395,9 @@ TEST_F(QuicPacketRebuilderTest, CannotRebuild) {
|
|||||||
auto packet1 = std::move(regularBuilder1).buildPacket();
|
auto packet1 = std::move(regularBuilder1).buildPacket();
|
||||||
ASSERT_EQ(5, packet1.packet.frames.size());
|
ASSERT_EQ(5, packet1.packet.frames.size());
|
||||||
stream->retransmissionBuffer.emplace(
|
stream->retransmissionBuffer.emplace(
|
||||||
stream->retransmissionBuffer.begin(), buf->clone(), 0, true);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(buf->clone(), 0, true));
|
||||||
|
|
||||||
// new builder has a much smaller writable bytes limit
|
// new builder has a much smaller writable bytes limit
|
||||||
ShortHeader shortHeader2(
|
ShortHeader shortHeader2(
|
||||||
|
@@ -96,13 +96,7 @@ void markPacketLoss(
|
|||||||
if (!stream) {
|
if (!stream) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto bufferItr = std::lower_bound(
|
auto bufferItr = stream->retransmissionBuffer.find(frame.offset);
|
||||||
stream->retransmissionBuffer.begin(),
|
|
||||||
stream->retransmissionBuffer.end(),
|
|
||||||
frame.offset,
|
|
||||||
[](const auto& buffer, const auto& offset) {
|
|
||||||
return buffer.offset < offset;
|
|
||||||
});
|
|
||||||
if (bufferItr == stream->retransmissionBuffer.end()) {
|
if (bufferItr == stream->retransmissionBuffer.end()) {
|
||||||
// It's possible that the stream was reset or data on the stream was
|
// It's possible that the stream was reset or data on the stream was
|
||||||
// skipped while we discovered that its packet was lost so we might
|
// skipped while we discovered that its packet was lost so we might
|
||||||
@@ -111,18 +105,19 @@ void markPacketLoss(
|
|||||||
}
|
}
|
||||||
// The original rxmt offset might have been bumped up after it was
|
// The original rxmt offset might have been bumped up after it was
|
||||||
// shrunk due to egress partially reliable skip.
|
// shrunk due to egress partially reliable skip.
|
||||||
if (!streamFrameMatchesRetransmitBuffer(*stream, frame, *bufferItr)) {
|
if (!streamFrameMatchesRetransmitBuffer(
|
||||||
|
*stream, frame, bufferItr->second)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream->lossBuffer.insert(
|
stream->lossBuffer.insert(
|
||||||
std::upper_bound(
|
std::upper_bound(
|
||||||
stream->lossBuffer.begin(),
|
stream->lossBuffer.begin(),
|
||||||
stream->lossBuffer.end(),
|
stream->lossBuffer.end(),
|
||||||
bufferItr->offset,
|
bufferItr->second.offset,
|
||||||
[](const auto& offset, const auto& buffer) {
|
[](const auto& offset, const auto& buffer) {
|
||||||
return offset < buffer.offset;
|
return offset < buffer.offset;
|
||||||
}),
|
}),
|
||||||
std::move(*bufferItr));
|
std::move(bufferItr->second));
|
||||||
stream->retransmissionBuffer.erase(bufferItr);
|
stream->retransmissionBuffer.erase(bufferItr);
|
||||||
conn.streamManager->updateLossStreams(*stream);
|
conn.streamManager->updateLossStreams(*stream);
|
||||||
break;
|
break;
|
||||||
@@ -136,28 +131,22 @@ void markPacketLoss(
|
|||||||
auto encryptionLevel = protectionTypeToEncryptionLevel(protectionType);
|
auto encryptionLevel = protectionTypeToEncryptionLevel(protectionType);
|
||||||
auto cryptoStream = getCryptoStream(*conn.cryptoState, encryptionLevel);
|
auto cryptoStream = getCryptoStream(*conn.cryptoState, encryptionLevel);
|
||||||
|
|
||||||
auto bufferItr = std::lower_bound(
|
auto bufferItr = cryptoStream->retransmissionBuffer.find(frame.offset);
|
||||||
cryptoStream->retransmissionBuffer.begin(),
|
|
||||||
cryptoStream->retransmissionBuffer.end(),
|
|
||||||
frame.offset,
|
|
||||||
[](const auto& buffer, const auto& offset) {
|
|
||||||
return buffer.offset < offset;
|
|
||||||
});
|
|
||||||
if (bufferItr == cryptoStream->retransmissionBuffer.end()) {
|
if (bufferItr == cryptoStream->retransmissionBuffer.end()) {
|
||||||
// It's possible that the stream was reset while we discovered that
|
// It's possible that the stream was reset while we discovered that
|
||||||
// it's packet was lost so we might not have the offset.
|
// it's packet was lost so we might not have the offset.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DCHECK_EQ(bufferItr->offset, frame.offset);
|
DCHECK_EQ(bufferItr->second.offset, frame.offset);
|
||||||
cryptoStream->lossBuffer.insert(
|
cryptoStream->lossBuffer.insert(
|
||||||
std::upper_bound(
|
std::upper_bound(
|
||||||
cryptoStream->lossBuffer.begin(),
|
cryptoStream->lossBuffer.begin(),
|
||||||
cryptoStream->lossBuffer.end(),
|
cryptoStream->lossBuffer.end(),
|
||||||
bufferItr->offset,
|
bufferItr->second.offset,
|
||||||
[](const auto& offset, const auto& buffer) {
|
[](const auto& offset, const auto& buffer) {
|
||||||
return offset < buffer.offset;
|
return offset < buffer.offset;
|
||||||
}),
|
}),
|
||||||
std::move(*bufferItr));
|
std::move(bufferItr->second));
|
||||||
cryptoStream->retransmissionBuffer.erase(bufferItr);
|
cryptoStream->retransmissionBuffer.erase(bufferItr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -426,9 +426,6 @@ TEST_F(QuicLossFunctionsTest, RetxBufferSortedAfterLoss) {
|
|||||||
markPacketLoss(
|
markPacketLoss(
|
||||||
*conn, packet.packet, false, packet.packet.header.getPacketSequenceNum());
|
*conn, packet.packet, false, packet.packet.header.getPacketSequenceNum());
|
||||||
EXPECT_EQ(2, stream->retransmissionBuffer.size());
|
EXPECT_EQ(2, stream->retransmissionBuffer.size());
|
||||||
EXPECT_GT(
|
|
||||||
stream->retransmissionBuffer.back().offset,
|
|
||||||
stream->retransmissionBuffer.front().offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicLossFunctionsTest, TestMarkCryptoLostAfterCancelRetransmission) {
|
TEST_F(QuicLossFunctionsTest, TestMarkCryptoLostAfterCancelRetransmission) {
|
||||||
|
@@ -1179,12 +1179,7 @@ TEST_F(QuicServerTransportTest, TestOpenAckStreamFrame) {
|
|||||||
if (!frame) {
|
if (!frame) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto it = std::find_if(
|
auto it = stream->retransmissionBuffer.find(frame->offset);
|
||||||
stream->retransmissionBuffer.begin(),
|
|
||||||
stream->retransmissionBuffer.end(),
|
|
||||||
[&](auto& buffer) {
|
|
||||||
return buffer.offset == frame->offset && buffer.eof == frame->fin;
|
|
||||||
});
|
|
||||||
ASSERT_TRUE(it != stream->retransmissionBuffer.end());
|
ASSERT_TRUE(it != stream->retransmissionBuffer.end());
|
||||||
if (currentPacket == packetNum1 && frame->streamId == streamId) {
|
if (currentPacket == packetNum1 && frame->streamId == streamId) {
|
||||||
buffersInPacket1++;
|
buffersInPacket1++;
|
||||||
@@ -1358,8 +1353,10 @@ TEST_F(QuicServerTransportTest, RecvRstStreamFrame) {
|
|||||||
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
||||||
stream->readBuffer.emplace_back(
|
stream->readBuffer.emplace_back(
|
||||||
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
||||||
stream->retransmissionBuffer.emplace_back(
|
stream->retransmissionBuffer.emplace(
|
||||||
IOBuf::copyBuffer(words.at(2)), 0, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(IOBuf::copyBuffer(words.at(2)), 0, false));
|
||||||
writeDataToQuicStream(*stream, IOBuf::copyBuffer(words.at(3)), false);
|
writeDataToQuicStream(*stream, IOBuf::copyBuffer(words.at(3)), false);
|
||||||
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
||||||
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
||||||
@@ -1413,8 +1410,10 @@ TEST_F(QuicServerTransportTest, RecvStopSendingFrame) {
|
|||||||
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
||||||
stream->readBuffer.emplace_back(
|
stream->readBuffer.emplace_back(
|
||||||
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
||||||
stream->retransmissionBuffer.emplace_back(
|
stream->retransmissionBuffer.emplace(
|
||||||
IOBuf::copyBuffer(words.at(2)), 0, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(IOBuf::copyBuffer(words.at(2)), 0, false));
|
||||||
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
||||||
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
||||||
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
||||||
@@ -1455,8 +1454,10 @@ TEST_F(QuicServerTransportTest, RecvStopSendingFrameAfterCloseStream) {
|
|||||||
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
||||||
stream->readBuffer.emplace_back(
|
stream->readBuffer.emplace_back(
|
||||||
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
||||||
stream->retransmissionBuffer.emplace_back(
|
stream->retransmissionBuffer.emplace(
|
||||||
IOBuf::copyBuffer(words.at(2)), 0, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(IOBuf::copyBuffer(words.at(2)), 0, false));
|
||||||
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
||||||
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
||||||
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
||||||
@@ -1496,8 +1497,10 @@ TEST_F(QuicServerTransportTest, RecvInvalidMaxStreamData) {
|
|||||||
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
||||||
stream->readBuffer.emplace_back(
|
stream->readBuffer.emplace_back(
|
||||||
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
||||||
stream->retransmissionBuffer.emplace_back(
|
stream->retransmissionBuffer.emplace(
|
||||||
IOBuf::copyBuffer(words.at(2)), 0, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(IOBuf::copyBuffer(words.at(2)), 0, false));
|
||||||
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
||||||
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
||||||
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
||||||
@@ -1534,8 +1537,10 @@ TEST_F(QuicServerTransportTest, RecvStopSendingFrameAfterHalfCloseRemote) {
|
|||||||
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
stream->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
||||||
stream->readBuffer.emplace_back(
|
stream->readBuffer.emplace_back(
|
||||||
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
||||||
stream->retransmissionBuffer.emplace_back(
|
stream->retransmissionBuffer.emplace(
|
||||||
IOBuf::copyBuffer(words.at(2)), 0, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(IOBuf::copyBuffer(words.at(2)), 0, false));
|
||||||
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
stream->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
||||||
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
stream->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
||||||
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
stream->currentReadOffset = words.at(0).length() + words.at(1).length();
|
||||||
@@ -1606,8 +1611,10 @@ TEST_F(QuicServerTransportTest, RecvStopSendingFrameAfterReset) {
|
|||||||
stream1->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
stream1->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
||||||
stream1->readBuffer.emplace_back(
|
stream1->readBuffer.emplace_back(
|
||||||
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
||||||
stream1->retransmissionBuffer.emplace_back(
|
stream1->retransmissionBuffer.emplace(
|
||||||
IOBuf::copyBuffer(words.at(2)), 0, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(IOBuf::copyBuffer(words.at(2)), 0, false));
|
||||||
stream1->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
stream1->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
||||||
stream1->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
stream1->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
||||||
stream1->currentReadOffset = words.at(0).length() + words.at(1).length();
|
stream1->currentReadOffset = words.at(0).length() + words.at(1).length();
|
||||||
@@ -1615,8 +1622,10 @@ TEST_F(QuicServerTransportTest, RecvStopSendingFrameAfterReset) {
|
|||||||
stream2->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
stream2->readBuffer.emplace_back(IOBuf::copyBuffer(words.at(0)), 0, false);
|
||||||
stream2->readBuffer.emplace_back(
|
stream2->readBuffer.emplace_back(
|
||||||
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
IOBuf::copyBuffer(words.at(1)), words.at(0).length(), false);
|
||||||
stream2->retransmissionBuffer.emplace_back(
|
stream2->retransmissionBuffer.emplace(
|
||||||
IOBuf::copyBuffer(words.at(2)), 0, false);
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(0),
|
||||||
|
std::forward_as_tuple(IOBuf::copyBuffer(words.at(2)), 0, false));
|
||||||
stream2->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
stream2->writeBuffer.append(IOBuf::copyBuffer(words.at(3)));
|
||||||
stream2->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
stream2->currentWriteOffset = words.at(2).length() + words.at(3).length();
|
||||||
stream2->currentReadOffset = words.at(0).length() + words.at(1).length();
|
stream2->currentReadOffset = words.at(0).length() + words.at(1).length();
|
||||||
|
@@ -34,6 +34,29 @@ void shrinkBuffers(std::deque<StreamBuffer>& buffers, uint64_t offset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void shrinkBuffers(
|
||||||
|
folly::F14FastMap<uint64_t, StreamBuffer>& buffers,
|
||||||
|
uint64_t offset) {
|
||||||
|
// Do a linear search of the entire buffer, there can be exactly one trimmed
|
||||||
|
// buffer, since we are changing the offset for that single buffer we need to
|
||||||
|
// change the offset in the StreamBuffer, but keep it keyed on the same
|
||||||
|
// offset as before so we still remove it on ack.
|
||||||
|
for (auto itr = buffers.begin(); itr != buffers.end();) {
|
||||||
|
if (itr->second.offset >= offset) {
|
||||||
|
itr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (itr->second.offset + itr->second.data.chainLength() <= offset) {
|
||||||
|
itr = buffers.erase(itr);
|
||||||
|
} else {
|
||||||
|
uint64_t amount = offset - itr->second.offset;
|
||||||
|
itr->second.data.trimStartAtMost(amount);
|
||||||
|
itr->second.offset += amount;
|
||||||
|
itr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void shrinkRetransmittableBuffers(
|
void shrinkRetransmittableBuffers(
|
||||||
QuicStreamState* stream,
|
QuicStreamState* stream,
|
||||||
uint64_t minimumRetransmittableOffset) {
|
uint64_t minimumRetransmittableOffset) {
|
||||||
|
@@ -382,11 +382,14 @@ uint64_t getLargestWriteOffsetSeen(const QuicStreamState& stream) {
|
|||||||
|
|
||||||
uint64_t getStreamNextOffsetToDeliver(const QuicStreamState& stream) {
|
uint64_t getStreamNextOffsetToDeliver(const QuicStreamState& stream) {
|
||||||
auto minOffsetToDeliver = stream.currentWriteOffset;
|
auto minOffsetToDeliver = stream.currentWriteOffset;
|
||||||
|
// If the acked intervals is not empty, then the furthest acked interval
|
||||||
|
// starting at zero is the next offset. If there is no interval starting at
|
||||||
|
// zero then we cannot deliver any offsets.
|
||||||
minOffsetToDeliver = std::min(
|
minOffsetToDeliver = std::min(
|
||||||
minOffsetToDeliver,
|
minOffsetToDeliver,
|
||||||
stream.retransmissionBuffer.empty()
|
stream.ackedIntervals.empty() || stream.ackedIntervals.front().start != 0
|
||||||
? minOffsetToDeliver
|
? minOffsetToDeliver
|
||||||
: stream.retransmissionBuffer[0].offset);
|
: stream.ackedIntervals.front().end);
|
||||||
minOffsetToDeliver = std::min(
|
minOffsetToDeliver = std::min(
|
||||||
minOffsetToDeliver,
|
minOffsetToDeliver,
|
||||||
stream.lossBuffer.empty() ? minOffsetToDeliver
|
stream.lossBuffer.empty() ? minOffsetToDeliver
|
||||||
@@ -426,16 +429,10 @@ void processCryptoStreamAck(
|
|||||||
QuicCryptoStream& cryptoStream,
|
QuicCryptoStream& cryptoStream,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
uint64_t len) {
|
uint64_t len) {
|
||||||
auto ackedBuffer = std::lower_bound(
|
auto ackedBuffer = cryptoStream.retransmissionBuffer.find(offset);
|
||||||
cryptoStream.retransmissionBuffer.begin(),
|
|
||||||
cryptoStream.retransmissionBuffer.end(),
|
|
||||||
offset,
|
|
||||||
[](const auto& buffer, const auto& offset) {
|
|
||||||
return buffer.offset < offset;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ackedBuffer == cryptoStream.retransmissionBuffer.end() ||
|
if (ackedBuffer == cryptoStream.retransmissionBuffer.end() ||
|
||||||
ackedBuffer->offset != offset || ackedBuffer->data.chainLength() != len) {
|
ackedBuffer->second.offset != offset ||
|
||||||
|
ackedBuffer->second.data.chainLength() != len) {
|
||||||
// It's possible retransmissions of crypto data were canceled.
|
// It's possible retransmissions of crypto data were canceled.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -36,12 +36,22 @@ struct QuicStreamLike {
|
|||||||
// TODO replace with BufQueue
|
// TODO replace with BufQueue
|
||||||
folly::IOBufQueue writeBuffer{folly::IOBufQueue::cacheChainLength()};
|
folly::IOBufQueue writeBuffer{folly::IOBufQueue::cacheChainLength()};
|
||||||
|
|
||||||
// Stores a list of buffers which have been written to the socket and are
|
// Stores a map of buffers which have been written to the socket and are
|
||||||
// currently un-acked. Each one represents one StreamFrame that was written.
|
// currently un-acked. Each one represents one StreamFrame that was written.
|
||||||
// We need to buffer these because these might be retransmitted
|
// We need to buffer these because these might be retransmitted
|
||||||
// in the future.
|
// in the future.
|
||||||
// These are sorted in order of start offset.
|
// These are associated with the starting offset of the buffer.
|
||||||
std::deque<StreamBuffer> retransmissionBuffer;
|
// Note: the offset in the StreamBuffer itself can be >= the offset on which
|
||||||
|
// it is keyed due to partial reliability - when data is skipped the offset
|
||||||
|
// in the StreamBuffer may be incremented, but the keyed offset must remain
|
||||||
|
// the same so it can be removed from the buffer on ACK.
|
||||||
|
folly::F14FastMap<uint64_t, StreamBuffer> retransmissionBuffer;
|
||||||
|
|
||||||
|
// Tracks intervals which we have received ACKs for. E.g. in the case of all
|
||||||
|
// data being acked this would contain one internval from 0 -> the largest
|
||||||
|
// offseet ACKed. This allows us to track which delivery callbacks can be
|
||||||
|
// called.
|
||||||
|
IntervalSet<uint64_t> ackedIntervals;
|
||||||
|
|
||||||
// Stores a list of buffers which have been marked as loss by loss detector.
|
// Stores a list of buffers which have been marked as loss by loss detector.
|
||||||
// Each one represents one StreamFrame that was written.
|
// Each one represents one StreamFrame that was written.
|
||||||
|
@@ -91,28 +91,25 @@ void sendAckSMHandler(
|
|||||||
switch (stream.sendState) {
|
switch (stream.sendState) {
|
||||||
case StreamSendState::Open_E: {
|
case StreamSendState::Open_E: {
|
||||||
// Clean up the acked buffers from the retransmissionBuffer.
|
// Clean up the acked buffers from the retransmissionBuffer.
|
||||||
auto ackedBuffer = std::lower_bound(
|
auto ackedBuffer = stream.retransmissionBuffer.find(ackedFrame.offset);
|
||||||
stream.retransmissionBuffer.begin(),
|
|
||||||
stream.retransmissionBuffer.end(),
|
|
||||||
ackedFrame.offset,
|
|
||||||
[](const auto& buffer, const auto& offset) {
|
|
||||||
return buffer.offset < offset;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ackedBuffer != stream.retransmissionBuffer.end()) {
|
if (ackedBuffer != stream.retransmissionBuffer.end()) {
|
||||||
if (streamFrameMatchesRetransmitBuffer(
|
if (streamFrameMatchesRetransmitBuffer(
|
||||||
stream, ackedFrame, *ackedBuffer)) {
|
stream, ackedFrame, ackedBuffer->second)) {
|
||||||
VLOG(10) << "Open: acked stream data stream=" << stream.id
|
VLOG(10) << "Open: acked stream data stream=" << stream.id
|
||||||
<< " offset=" << ackedBuffer->offset
|
<< " offset=" << ackedBuffer->second.offset
|
||||||
<< " len=" << ackedBuffer->data.chainLength()
|
<< " len=" << ackedBuffer->second.data.chainLength()
|
||||||
<< " eof=" << ackedBuffer->eof << " " << stream.conn;
|
<< " eof=" << ackedBuffer->second.eof << " " << stream.conn;
|
||||||
|
stream.ackedIntervals.insert(
|
||||||
|
ackedBuffer->second.offset,
|
||||||
|
ackedBuffer->second.offset +
|
||||||
|
ackedBuffer->second.data.chainLength());
|
||||||
stream.retransmissionBuffer.erase(ackedBuffer);
|
stream.retransmissionBuffer.erase(ackedBuffer);
|
||||||
} else {
|
} else {
|
||||||
VLOG(10)
|
VLOG(10)
|
||||||
<< "Open: received an ack for already discarded buffer; stream="
|
<< "Open: received an ack for already discarded buffer; stream="
|
||||||
<< stream.id << " offset=" << ackedBuffer->offset
|
<< stream.id << " offset=" << ackedBuffer->second.offset
|
||||||
<< " len=" << ackedBuffer->data.chainLength()
|
<< " len=" << ackedBuffer->second.data.chainLength()
|
||||||
<< " eof=" << ackedBuffer->eof << " " << stream.conn;
|
<< " eof=" << ackedBuffer->second.eof << " " << stream.conn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,8 +32,8 @@ TEST_F(StreamStateFunctionsTests, BasicResetTest) {
|
|||||||
StreamBuffer(folly::IOBuf::copyBuffer(" It is not a hotdog."), 15));
|
StreamBuffer(folly::IOBuf::copyBuffer(" It is not a hotdog."), 15));
|
||||||
writeDataToQuicStream(
|
writeDataToQuicStream(
|
||||||
stream, folly::IOBuf::copyBuffer("What is it then?"), false);
|
stream, folly::IOBuf::copyBuffer("What is it then?"), false);
|
||||||
stream.retransmissionBuffer.emplace_back(
|
stream.retransmissionBuffer.emplace(
|
||||||
folly::IOBuf::copyBuffer("How would I know?"), 34);
|
34, StreamBuffer(folly::IOBuf::copyBuffer("How would I know?"), 34));
|
||||||
auto currentWriteOffset = stream.currentWriteOffset;
|
auto currentWriteOffset = stream.currentWriteOffset;
|
||||||
auto currentReadOffset = stream.currentReadOffset;
|
auto currentReadOffset = stream.currentReadOffset;
|
||||||
EXPECT_TRUE(stream.writable());
|
EXPECT_TRUE(stream.writable());
|
||||||
|
@@ -175,6 +175,69 @@ TEST_F(QuicOpenStateTest, AckStream) {
|
|||||||
ASSERT_EQ(stream->sendState, StreamSendState::Closed_E);
|
ASSERT_EQ(stream->sendState, StreamSendState::Closed_E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(QuicOpenStateTest, AckStreamMulti) {
|
||||||
|
auto conn = createConn();
|
||||||
|
|
||||||
|
auto stream = conn->streamManager->createNextBidirectionalStream().value();
|
||||||
|
folly::Optional<ConnectionId> serverChosenConnId = *conn->clientConnectionId;
|
||||||
|
serverChosenConnId.value().data()[0] ^= 0x01;
|
||||||
|
EventBase evb;
|
||||||
|
auto sock = std::make_unique<folly::test::MockAsyncUDPSocket>(&evb);
|
||||||
|
|
||||||
|
auto buf = IOBuf::copyBuffer("hello");
|
||||||
|
writeQuicPacket(
|
||||||
|
*conn,
|
||||||
|
*conn->clientConnectionId,
|
||||||
|
*serverChosenConnId,
|
||||||
|
*sock,
|
||||||
|
*stream,
|
||||||
|
*buf,
|
||||||
|
false);
|
||||||
|
writeQuicPacket(
|
||||||
|
*conn,
|
||||||
|
*conn->clientConnectionId,
|
||||||
|
*serverChosenConnId,
|
||||||
|
*sock,
|
||||||
|
*stream,
|
||||||
|
*IOBuf::copyBuffer("world"),
|
||||||
|
false);
|
||||||
|
writeQuicPacket(
|
||||||
|
*conn,
|
||||||
|
*conn->clientConnectionId,
|
||||||
|
*serverChosenConnId,
|
||||||
|
*sock,
|
||||||
|
*stream,
|
||||||
|
*IOBuf::copyBuffer("this is bob"),
|
||||||
|
false);
|
||||||
|
|
||||||
|
EXPECT_EQ(stream->retransmissionBuffer.size(), 3);
|
||||||
|
EXPECT_EQ(3, conn->outstandingPackets.size());
|
||||||
|
|
||||||
|
auto& streamFrame3 =
|
||||||
|
*conn->outstandingPackets[2].packet.frames[0].asWriteStreamFrame();
|
||||||
|
|
||||||
|
sendAckSMHandler(*stream, streamFrame3);
|
||||||
|
ASSERT_EQ(stream->sendState, StreamSendState::Open_E);
|
||||||
|
ASSERT_EQ(stream->ackedIntervals.front().start, 10);
|
||||||
|
ASSERT_EQ(stream->ackedIntervals.front().end, 21);
|
||||||
|
|
||||||
|
auto& streamFrame2 =
|
||||||
|
*conn->outstandingPackets[1].packet.frames[0].asWriteStreamFrame();
|
||||||
|
|
||||||
|
sendAckSMHandler(*stream, streamFrame2);
|
||||||
|
ASSERT_EQ(stream->sendState, StreamSendState::Open_E);
|
||||||
|
ASSERT_EQ(stream->ackedIntervals.front().start, 5);
|
||||||
|
ASSERT_EQ(stream->ackedIntervals.front().end, 21);
|
||||||
|
|
||||||
|
auto& streamFrame1 =
|
||||||
|
*conn->outstandingPackets[0].packet.frames[0].asWriteStreamFrame();
|
||||||
|
|
||||||
|
sendAckSMHandler(*stream, streamFrame1);
|
||||||
|
ASSERT_EQ(stream->sendState, StreamSendState::Open_E);
|
||||||
|
ASSERT_EQ(stream->ackedIntervals.front().start, 0);
|
||||||
|
ASSERT_EQ(stream->ackedIntervals.front().end, 21);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(QuicOpenStateTest, RetxBufferSortedAfterAck) {
|
TEST_F(QuicOpenStateTest, RetxBufferSortedAfterAck) {
|
||||||
auto conn = createConn();
|
auto conn = createConn();
|
||||||
auto stream = conn->streamManager->createNextBidirectionalStream().value();
|
auto stream = conn->streamManager->createNextBidirectionalStream().value();
|
||||||
@@ -219,9 +282,6 @@ TEST_F(QuicOpenStateTest, RetxBufferSortedAfterAck) {
|
|||||||
.asWriteStreamFrame();
|
.asWriteStreamFrame();
|
||||||
sendAckSMHandler(*stream, streamFrame);
|
sendAckSMHandler(*stream, streamFrame);
|
||||||
EXPECT_EQ(2, stream->retransmissionBuffer.size());
|
EXPECT_EQ(2, stream->retransmissionBuffer.size());
|
||||||
EXPECT_GT(
|
|
||||||
stream->retransmissionBuffer.back().offset,
|
|
||||||
stream->retransmissionBuffer.front().offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicOpenStateTest, AckStreamAfterSkip) {
|
TEST_F(QuicOpenStateTest, AckStreamAfterSkip) {
|
||||||
@@ -933,7 +993,10 @@ TEST_F(QuicUnidirectionalStreamTest, OpenFinalAckStreamFrame) {
|
|||||||
stream.currentWriteOffset = 2;
|
stream.currentWriteOffset = 2;
|
||||||
auto buf = folly::IOBuf::create(1);
|
auto buf = folly::IOBuf::create(1);
|
||||||
buf->append(1);
|
buf->append(1);
|
||||||
stream.retransmissionBuffer.emplace_back(std::move(buf), 1, false);
|
stream.retransmissionBuffer.emplace(
|
||||||
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(1),
|
||||||
|
std::forward_as_tuple(std::move(buf), 1, false));
|
||||||
sendAckSMHandler(stream, streamFrame);
|
sendAckSMHandler(stream, streamFrame);
|
||||||
EXPECT_EQ(stream.sendState, StreamSendState::Closed_E);
|
EXPECT_EQ(stream.sendState, StreamSendState::Closed_E);
|
||||||
EXPECT_EQ(stream.recvState, StreamRecvState::Invalid_E);
|
EXPECT_EQ(stream.recvState, StreamRecvState::Invalid_E);
|
||||||
|
@@ -92,7 +92,7 @@ TEST_F(QPRFunctionsTest, AdvanceMinimumRetransmittableOffset) {
|
|||||||
auto buf = folly::IOBuf::copyBuffer("aaaaaaaaaa");
|
auto buf = folly::IOBuf::copyBuffer("aaaaaaaaaa");
|
||||||
// case2. has no unacked data below 139
|
// case2. has no unacked data below 139
|
||||||
stream->currentWriteOffset = 150;
|
stream->currentWriteOffset = 150;
|
||||||
stream->retransmissionBuffer.emplace_back(StreamBuffer(buf->clone(), 140));
|
stream->retransmissionBuffer.emplace(140, StreamBuffer(buf->clone(), 140));
|
||||||
result = advanceMinimumRetransmittableOffset(stream, 139);
|
result = advanceMinimumRetransmittableOffset(stream, 139);
|
||||||
EXPECT_TRUE(result.hasValue());
|
EXPECT_TRUE(result.hasValue());
|
||||||
EXPECT_EQ(*result, 139);
|
EXPECT_EQ(*result, 139);
|
||||||
@@ -101,7 +101,7 @@ TEST_F(QPRFunctionsTest, AdvanceMinimumRetransmittableOffset) {
|
|||||||
|
|
||||||
// case3. ExpiredStreamDataFrame is wired
|
// case3. ExpiredStreamDataFrame is wired
|
||||||
stream->minimumRetransmittableOffset = 139;
|
stream->minimumRetransmittableOffset = 139;
|
||||||
stream->retransmissionBuffer.emplace_back(StreamBuffer(buf->clone(), 140));
|
stream->retransmissionBuffer.emplace(140, StreamBuffer(buf->clone(), 140));
|
||||||
result = advanceMinimumRetransmittableOffset(stream, 150);
|
result = advanceMinimumRetransmittableOffset(stream, 150);
|
||||||
EXPECT_TRUE(result.hasValue());
|
EXPECT_TRUE(result.hasValue());
|
||||||
EXPECT_EQ(*result, 150);
|
EXPECT_EQ(*result, 150);
|
||||||
@@ -117,7 +117,7 @@ TEST_F(QPRFunctionsTest, AdvanceMinimumRetransmittableOffset) {
|
|||||||
|
|
||||||
// case4. update existing pending event.
|
// case4. update existing pending event.
|
||||||
stream->minimumRetransmittableOffset = 150;
|
stream->minimumRetransmittableOffset = 150;
|
||||||
stream->retransmissionBuffer.emplace_back(StreamBuffer(buf->clone(), 150));
|
stream->retransmissionBuffer.emplace(150, StreamBuffer(buf->clone(), 150));
|
||||||
stream->conn.pendingEvents.frames.clear();
|
stream->conn.pendingEvents.frames.clear();
|
||||||
stream->conn.pendingEvents.frames.emplace_back(
|
stream->conn.pendingEvents.frames.emplace_back(
|
||||||
ExpiredStreamDataFrame(stream->id, 160));
|
ExpiredStreamDataFrame(stream->id, 160));
|
||||||
@@ -171,8 +171,8 @@ TEST_F(QPRFunctionsTest, RecvMinStreamDataFrameShrinkBuffer) {
|
|||||||
stream->writeBuffer.append(std::move(buf));
|
stream->writeBuffer.append(std::move(buf));
|
||||||
stream->conn.flowControlState.sumCurStreamBufferLen = 20;
|
stream->conn.flowControlState.sumCurStreamBufferLen = 20;
|
||||||
auto writtenBuffer = folly::IOBuf::copyBuffer("cccccccccc");
|
auto writtenBuffer = folly::IOBuf::copyBuffer("cccccccccc");
|
||||||
stream->retransmissionBuffer.emplace_back(
|
stream->retransmissionBuffer.emplace(
|
||||||
StreamBuffer(std::move(writtenBuffer), 90, false));
|
90, StreamBuffer(std::move(writtenBuffer), 90, false));
|
||||||
MinStreamDataFrame shrinkMinStreamDataFrame(
|
MinStreamDataFrame shrinkMinStreamDataFrame(
|
||||||
stream->id, stream->flowControlState.peerAdvertisedMaxOffset, 110);
|
stream->id, stream->flowControlState.peerAdvertisedMaxOffset, 110);
|
||||||
onRecvMinStreamDataFrame(stream, shrinkMinStreamDataFrame, packetNum);
|
onRecvMinStreamDataFrame(stream, shrinkMinStreamDataFrame, packetNum);
|
||||||
|
@@ -1810,7 +1810,8 @@ TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAckedStillRetransmitting) {
|
|||||||
StreamId id = 3;
|
StreamId id = 3;
|
||||||
QuicStreamState stream(id, conn);
|
QuicStreamState stream(id, conn);
|
||||||
stream.finalWriteOffset = 12;
|
stream.finalWriteOffset = 12;
|
||||||
stream.retransmissionBuffer.emplace_back(IOBuf::create(10), 10, false);
|
stream.retransmissionBuffer.emplace(
|
||||||
|
0, StreamBuffer(IOBuf::create(10), 10, false));
|
||||||
EXPECT_FALSE(allBytesTillFinAcked(stream));
|
EXPECT_FALSE(allBytesTillFinAcked(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1882,15 +1883,18 @@ TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliver) {
|
|||||||
TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliverRetxBuffer) {
|
TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliverRetxBuffer) {
|
||||||
QuicStreamState stream(3, conn);
|
QuicStreamState stream(3, conn);
|
||||||
stream.currentWriteOffset = 100;
|
stream.currentWriteOffset = 100;
|
||||||
stream.retransmissionBuffer.emplace_back(buildRandomInputData(10), 50);
|
stream.retransmissionBuffer.emplace(
|
||||||
EXPECT_EQ(50, getStreamNextOffsetToDeliver(stream));
|
50, StreamBuffer(buildRandomInputData(10), 50));
|
||||||
|
stream.ackedIntervals.insert(0, 49);
|
||||||
|
EXPECT_EQ(49, getStreamNextOffsetToDeliver(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliverRetxAndLossBuffer) {
|
TEST_F(QuicStreamFunctionsTest, StreamNextOffsetToDeliverRetxAndLossBuffer) {
|
||||||
QuicStreamState stream(3, conn);
|
QuicStreamState stream(3, conn);
|
||||||
stream.currentWriteOffset = 100;
|
stream.currentWriteOffset = 100;
|
||||||
stream.lossBuffer.emplace_back(buildRandomInputData(10), 30);
|
stream.lossBuffer.emplace_back(buildRandomInputData(10), 30);
|
||||||
stream.retransmissionBuffer.emplace_back(buildRandomInputData(10), 50);
|
stream.retransmissionBuffer.emplace(
|
||||||
|
50, StreamBuffer(buildRandomInputData(10), 50));
|
||||||
EXPECT_EQ(30, getStreamNextOffsetToDeliver(stream));
|
EXPECT_EQ(30, getStreamNextOffsetToDeliver(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1962,8 +1966,8 @@ TEST_F(QuicStreamFunctionsTest, WritableList) {
|
|||||||
|
|
||||||
TEST_F(QuicStreamFunctionsTest, AckCryptoStream) {
|
TEST_F(QuicStreamFunctionsTest, AckCryptoStream) {
|
||||||
auto chlo = IOBuf::copyBuffer("CHLO");
|
auto chlo = IOBuf::copyBuffer("CHLO");
|
||||||
conn.cryptoState->handshakeStream.retransmissionBuffer.emplace_back(
|
conn.cryptoState->handshakeStream.retransmissionBuffer.emplace(
|
||||||
StreamBuffer(chlo->clone(), 0));
|
0, StreamBuffer(chlo->clone(), 0));
|
||||||
processCryptoStreamAck(conn.cryptoState->handshakeStream, 0, chlo->length());
|
processCryptoStreamAck(conn.cryptoState->handshakeStream, 0, chlo->length());
|
||||||
EXPECT_EQ(conn.cryptoState->handshakeStream.retransmissionBuffer.size(), 0);
|
EXPECT_EQ(conn.cryptoState->handshakeStream.retransmissionBuffer.size(), 0);
|
||||||
}
|
}
|
||||||
@@ -1971,8 +1975,7 @@ TEST_F(QuicStreamFunctionsTest, AckCryptoStream) {
|
|||||||
TEST_F(QuicStreamFunctionsTest, AckCryptoStreamOffsetLengthMismatch) {
|
TEST_F(QuicStreamFunctionsTest, AckCryptoStreamOffsetLengthMismatch) {
|
||||||
auto chlo = IOBuf::copyBuffer("CHLO");
|
auto chlo = IOBuf::copyBuffer("CHLO");
|
||||||
auto& cryptoStream = conn.cryptoState->handshakeStream;
|
auto& cryptoStream = conn.cryptoState->handshakeStream;
|
||||||
cryptoStream.retransmissionBuffer.emplace_back(
|
cryptoStream.retransmissionBuffer.emplace(0, StreamBuffer(chlo->clone(), 0));
|
||||||
StreamBuffer(chlo->clone(), 0));
|
|
||||||
processCryptoStreamAck(cryptoStream, 1, chlo->length());
|
processCryptoStreamAck(cryptoStream, 1, chlo->length());
|
||||||
EXPECT_EQ(cryptoStream.retransmissionBuffer.size(), 1);
|
EXPECT_EQ(cryptoStream.retransmissionBuffer.size(), 1);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user