1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-08 09:42:06 +03:00

Send windowed stream limit updates

Summary:
Implement sending stream limit updates in a windowed fashion, so that as a peer exhausts its streams we will grant it additional credit. This is implemented by having the stream manager check if an update is needed on removing streams, and the api layer potentially sending an update after it initiates the check for closed streams.

This also makes some driveby changes to use `std::lower_bound` instead of `std::find` for the sorted collections in the stream manager.

Reviewed By: yangchi

Differential Revision: D16808229

fbshipit-source-id: f6e3460d43e4d165e362164be00c0cec27cf1e79
This commit is contained in:
Matt Joras
2019-09-18 11:29:26 -07:00
committed by Facebook Github Bot
parent 7fa7277089
commit 72e677df33
20 changed files with 460 additions and 124 deletions

View File

@@ -1979,6 +1979,7 @@ void QuicTransportBase::checkForClosedStream() {
stream->totalHolbTime.count(),
stream->holbCount);
conn_->streamManager->removeClosedStream(*itr);
maybeSendStreamLimitUpdates(*conn_);
readCallbacks_.erase(*itr);
peekCallbacks_.erase(*itr);
itr = conn_->streamManager->closedStreams().erase(itr);
@@ -2288,7 +2289,14 @@ void QuicTransportBase::cancelDeliveryCallbacks(
void QuicTransportBase::setTransportSettings(
TransportSettings transportSettings) {
// If we've already encoded the transport parameters, silently return as
// setting the transport settings again would be buggy.
// TODO should we throw or return Expected here?
if (conn_->transportParametersEncoded) {
return;
}
conn_->transportSettings = std::move(transportSettings);
conn_->streamManager->refreshTransportSettings(conn_->transportSettings);
setCongestionControl(transportSettings.defaultCongestionController);
if (conn_->transportSettings.pacingEnabled) {
conn_->pacer = std::make_unique<DefaultPacer>(

View File

@@ -1110,4 +1110,15 @@ WriteDataReason hasNonAckDataToWrite(const QuicConnectionStateBase& conn) {
}
return WriteDataReason::NO_WRITE;
}
void maybeSendStreamLimitUpdates(QuicConnectionStateBase& conn) {
auto update = conn.streamManager->remoteBidirectionalStreamLimitUpdate();
if (update) {
sendSimpleFrame(conn, (MaxStreamsFrame(*update, true)));
}
update = conn.streamManager->remoteUnidirectionalStreamLimitUpdate();
if (update) {
sendSimpleFrame(conn, (MaxStreamsFrame(*update, false)));
}
}
} // namespace quic

View File

@@ -234,4 +234,6 @@ uint64_t writeProbingDataToSocket(
HeaderBuilder LongHeaderBuilder(LongHeader::Types packetType);
HeaderBuilder ShortHeaderBuilder();
void maybeSendStreamLimitUpdates(QuicConnectionStateBase& conn);
} // namespace quic

View File

@@ -392,7 +392,7 @@ TEST_F(QuicPacketSchedulerTest, WriteOnlyOutstandingPacketsTest) {
bool present = false;
/* the next four frames should not be written */
present |= boost::get<ConnectionCloseFrame>(&frame) ? true : false;
present |= boost::get<MaxStreamsFrame>(&frame) ? true : false;
present |= boost::get<QuicSimpleFrame>(&frame) ? true : false;
present |= boost::get<PingFrame>(&frame) ? true : false;
present |= boost::get<WriteAckFrame>(&frame) ? true : false;
ASSERT_FALSE(present);

View File

@@ -430,17 +430,6 @@ void QuicClientTransport::processPacketData(
*stream, streamWindowUpdate.maximumData, packetNum);
}
},
[&](MaxStreamsFrame& maxStreamsFrame) {
VLOG(10) << "Client received max streams frame stream="
<< maxStreamsFrame.maxStreams << *this;
if (maxStreamsFrame.isForBidirectionalStream()) {
conn_->streamManager->setMaxLocalBidirectionalStreams(
maxStreamsFrame.maxStreams);
} else {
conn_->streamManager->setMaxLocalUnidirectionalStreams(
maxStreamsFrame.maxStreams);
}
},
[&](DataBlockedFrame&) {
VLOG(10) << "Client received blocked " << *this;
pktHasRetransmittableData = true;
@@ -874,6 +863,7 @@ void QuicClientTransport::startCryptoHandshake() {
conn_->transportSettings.ackDelayExponent,
conn_->transportSettings.maxRecvPacketSize,
customTransportParameters_);
conn_->transportParametersEncoded = true;
auto handshakeLayer = clientConn_->clientHandshakeLayer;
handshakeLayer->connect(
ctx_,

View File

@@ -74,7 +74,8 @@ struct QuicClientConnectionState : public QuicConnectionStateBase {
// We shouldn't normally need to set this until we're starting the
// transport, however writing unit tests is much easier if we set this here.
updateFlowControlStateWithSettings(flowControlState, transportSettings);
streamManager = std::make_unique<QuicStreamManager>(*this, this->nodeType);
streamManager = std::make_unique<QuicStreamManager>(
*this, this->nodeType, transportSettings);
}
};

View File

@@ -502,6 +502,31 @@ TEST_P(QuicClientTransportIntegrationTest, NetworkTestConnected) {
sendRequestAndResponseAndWait(*expected, data->clone(), streamId, &readCb);
}
TEST_P(QuicClientTransportIntegrationTest, SetTransportSettingsAfterStart) {
expectTransportCallbacks();
auto qLogger = std::make_shared<FileQLogger>();
client->getNonConstConn().qLogger = qLogger;
TransportSettings settings;
settings.connectUDP = true;
client->setTransportSettings(settings);
client->start(&clientConnCallback);
EXPECT_CALL(clientConnCallback, onTransportReady()).WillOnce(Invoke([&] {
CHECK(client->getConn().oneRttWriteCipher);
eventbase_.terminateLoopSoon();
}));
eventbase_.loopForever();
auto streamId = client->createBidirectionalStream().value();
auto data = IOBuf::copyBuffer("hello");
auto expected = std::shared_ptr<IOBuf>(IOBuf::copyBuffer("echo "));
expected->prependChain(data->clone());
sendRequestAndResponseAndWait(*expected, data->clone(), streamId, &readCb);
settings.connectUDP = false;
client->setTransportSettings(settings);
EXPECT_TRUE(client->getTransportSettings().connectUDP);
}
TEST_P(QuicClientTransportIntegrationTest, TestZeroRttSuccess) {
auto cachedPsk = setupZeroRttOnClientCtx(*clientCtx, hostname, getVersion());
pskCache_->putPsk(hostname, cachedPsk);

View File

@@ -434,6 +434,23 @@ size_t writeSimpleFrame(
}
// no space left in packet
return size_t(0);
},
[&](MaxStreamsFrame& maxStreamsFrame) {
auto frameType = maxStreamsFrame.isForBidirectionalStream()
? FrameType::MAX_STREAMS_BIDI
: FrameType::MAX_STREAMS_UNI;
QuicInteger intFrameType(static_cast<FrameTypeType>(frameType));
QuicInteger streamCount(maxStreamsFrame.maxStreams);
auto maxStreamsFrameSize =
intFrameType.getSize() + streamCount.getSize();
if (packetSpaceCheck(spaceLeft, maxStreamsFrameSize)) {
builder.write(intFrameType);
builder.write(streamCount);
builder.appendFrame(maxStreamsFrame);
return maxStreamsFrameSize;
}
// no space left in packet
return size_t(0);
});
}
@@ -520,23 +537,6 @@ size_t writeFrame(QuicWriteFrame&& frame, PacketBuilderInterface& builder) {
// no space left in packet
return size_t(0);
},
[&](MaxStreamsFrame& maxStreamsFrame) {
auto frameType = maxStreamsFrame.isForBidirectionalStream()
? FrameType::MAX_STREAMS_BIDI
: FrameType::MAX_STREAMS_UNI;
QuicInteger intFrameType(static_cast<FrameTypeType>(frameType));
QuicInteger streamCount(maxStreamsFrame.maxStreams);
auto maxStreamsFrameSize =
intFrameType.getSize() + streamCount.getSize();
if (packetSpaceCheck(spaceLeft, maxStreamsFrameSize)) {
builder.write(intFrameType);
builder.write(streamCount);
builder.appendFrame(std::move(maxStreamsFrame));
return maxStreamsFrameSize;
}
// no space left in packet
return size_t(0);
},
[&](DataBlockedFrame& blockedFrame) {
QuicInteger intFrameType(static_cast<uint8_t>(FrameType::DATA_BLOCKED));
QuicInteger dataLimit(blockedFrame.dataLimit);

View File

@@ -564,7 +564,8 @@ using QuicSimpleFrame = boost::variant<
ExpiredStreamDataFrame,
PathChallengeFrame,
PathResponseFrame,
NewConnectionIdFrame>;
NewConnectionIdFrame,
MaxStreamsFrame>;
// Types of frames that can be read.
using QuicFrame = boost::variant<
@@ -574,7 +575,6 @@ using QuicFrame = boost::variant<
ApplicationCloseFrame,
MaxDataFrame,
MaxStreamDataFrame,
MaxStreamsFrame,
PingFrame,
DataBlockedFrame,
StreamDataBlockedFrame,
@@ -594,11 +594,10 @@ using QuicWriteFrame = boost::variant<
ApplicationCloseFrame,
MaxDataFrame,
MaxStreamDataFrame,
MaxStreamsFrame,
StreamsBlockedFrame,
PingFrame,
DataBlockedFrame,
StreamDataBlockedFrame,
StreamsBlockedFrame,
WriteAckFrame,
WriteStreamFrame,
WriteCryptoFrame,

View File

@@ -130,7 +130,8 @@ TEST_F(QuicPacketRebuilderTest, RebuildPacket) {
EXPECT_EQ("The sun is in the sky.", closeFrame.reasonPhrase);
EXPECT_EQ(FrameType::ACK, closeFrame.closingFrameType);
},
[](const MaxStreamsFrame& maxStreamFrame) {
[](const QuicSimpleFrame& simpleFrame) {
auto maxStreamFrame = boost::get<MaxStreamsFrame>(simpleFrame);
EXPECT_EQ(4321, maxStreamFrame.maxStreams);
},
[](const PingFrame& ping) { EXPECT_EQ(PingFrame(), ping); },

View File

@@ -968,13 +968,14 @@ TEST_F(QuicWriteCodecTest, WriteMaxStreamId) {
auto streamCountSize = i < 64 ? 1 : 2;
// 1 byte for the type and up to 2 bytes for the stream count.
EXPECT_EQ(1 + streamCountSize, bytesWritten);
auto resultMaxStreamIdFrame =
boost::get<MaxStreamsFrame>(regularPacket.frames[0]);
auto resultMaxStreamIdFrame = boost::get<MaxStreamsFrame>(
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
EXPECT_EQ(i, resultMaxStreamIdFrame.maxStreams);
auto wireBuf = std::move(builtOut.second);
folly::io::Cursor cursor(wireBuf.get());
auto wireStreamsFrame = boost::get<MaxStreamsFrame>(parseQuicFrame(cursor));
auto wireStreamsFrame = boost::get<MaxStreamsFrame>(
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
EXPECT_EQ(i, wireStreamsFrame.maxStreams);
EXPECT_TRUE(cursor.isAtEnd());
}
@@ -994,13 +995,14 @@ TEST_F(QuicWriteCodecTest, WriteUniMaxStreamId) {
auto streamCountSize = i < 64 ? 1 : 2;
// 1 byte for the type and up to 2 bytes for the stream count.
EXPECT_EQ(1 + streamCountSize, bytesWritten);
auto resultMaxStreamIdFrame =
boost::get<MaxStreamsFrame>(regularPacket.frames[0]);
auto resultMaxStreamIdFrame = boost::get<MaxStreamsFrame>(
boost::get<QuicSimpleFrame>(regularPacket.frames[0]));
EXPECT_EQ(i, resultMaxStreamIdFrame.maxStreams);
auto wireBuf = std::move(builtOut.second);
folly::io::Cursor cursor(wireBuf.get());
auto wireStreamsFrame = boost::get<MaxStreamsFrame>(parseQuicFrame(cursor));
auto wireStreamsFrame = boost::get<MaxStreamsFrame>(
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
EXPECT_EQ(i, wireStreamsFrame.maxStreams);
EXPECT_TRUE(cursor.isAtEnd());
}
@@ -1268,12 +1270,13 @@ TEST_F(QuicWriteCodecTest, WriteStreamIdNeeded) {
auto builtOut = std::move(pktBuilder).buildPacket();
auto regularPacket = builtOut.first;
EXPECT_EQ(bytesWritten, 3);
EXPECT_NO_THROW(boost::get<MaxStreamsFrame>(regularPacket.frames[0]));
EXPECT_NO_THROW(boost::get<MaxStreamsFrame>(
boost::get<QuicSimpleFrame>(regularPacket.frames[0])));
auto wireBuf = std::move(builtOut.second);
folly::io::Cursor cursor(wireBuf.get());
auto writeStreamIdBlocked =
boost::get<MaxStreamsFrame>(parseQuicFrame(cursor));
auto writeStreamIdBlocked = boost::get<MaxStreamsFrame>(
boost::get<QuicSimpleFrame>(parseQuicFrame(cursor)));
EXPECT_EQ(writeStreamIdBlocked.maxStreams, blockedStreamId);
EXPECT_TRUE(cursor.isAtEnd());
}

View File

@@ -24,8 +24,8 @@ class QuicFlowControlTest : public Test {
public:
void SetUp() override {
transportInfoCb_ = std::make_unique<MockQuicStats>();
conn_.streamManager =
std::make_unique<QuicStreamManager>(conn_, conn_.nodeType);
conn_.streamManager = std::make_unique<QuicStreamManager>(
conn_, conn_.nodeType, conn_.transportSettings);
conn_.infoCallback = transportInfoCb_.get();
}
std::unique_ptr<MockQuicStats> transportInfoCb_;

View File

@@ -494,6 +494,7 @@ void onServerReadDataFromOpen(
conn.transportSettings.maxRecvPacketSize,
conn.transportSettings.partialReliabilityEnabled,
token));
conn.transportParametersEncoded = true;
QuicFizzFactory fizzFactory;
FizzCryptoFactory cryptoFactory(&fizzFactory);
conn.readCodec = std::make_unique<QuicReadCodec>(QuicNodeType::Server);
@@ -870,18 +871,6 @@ void onServerReadDataFromOpen(
*stream, streamWindowUpdate.maximumData, packetNum);
}
},
[&](MaxStreamsFrame& maxStreamsFrame) {
VLOG(10) << "Server received max streams frame stream="
<< maxStreamsFrame.maxStreams << " " << conn;
isNonProbingPacket = true;
if (maxStreamsFrame.isForBidirectionalStream()) {
conn.streamManager->setMaxLocalBidirectionalStreams(
maxStreamsFrame.maxStreams);
} else {
conn.streamManager->setMaxLocalUnidirectionalStreams(
maxStreamsFrame.maxStreams);
}
},
[&](DataBlockedFrame&) {
VLOG(10) << "Server received blocked " << conn;
pktHasRetransmittableData = true;

View File

@@ -132,7 +132,8 @@ struct QuicServerConnectionState : public QuicConnectionStateBase {
pendingZeroRttData =
std::make_unique<std::vector<ServerEvents::ReadData>>();
pendingOneRttData = std::make_unique<std::vector<ServerEvents::ReadData>>();
streamManager = std::make_unique<QuicStreamManager>(*this, this->nodeType);
streamManager = std::make_unique<QuicStreamManager>(
*this, this->nodeType, transportSettings);
}
};

View File

@@ -109,8 +109,8 @@ void QuicStreamManager::setMaxLocalBidirectionalStreams(
"Attempt to set maxStreams beyond the max allowed.",
TransportErrorCode::STREAM_LIMIT_ERROR);
}
StreamId maxStreamId =
maxStreams * detail::kStreamIncrement + initialBidirectionalStreamId_;
StreamId maxStreamId = maxStreams * detail::kStreamIncrement +
initialLocalBidirectionalStreamId_;
if (force || maxStreamId > maxLocalBidirectionalStreamId_) {
maxLocalBidirectionalStreamId_ = maxStreamId;
}
@@ -124,21 +124,68 @@ void QuicStreamManager::setMaxLocalUnidirectionalStreams(
"Attempt to set maxStreams beyond the max allowed.",
TransportErrorCode::STREAM_LIMIT_ERROR);
}
StreamId maxStreamId =
maxStreams * detail::kStreamIncrement + initialUnidirectionalStreamId_;
StreamId maxStreamId = maxStreams * detail::kStreamIncrement +
initialLocalUnidirectionalStreamId_;
if (force || maxStreamId > maxLocalUnidirectionalStreamId_) {
maxLocalUnidirectionalStreamId_ = maxStreamId;
}
}
void QuicStreamManager::setMaxRemoteBidirectionalStreams(uint64_t maxStreams) {
setMaxRemoteBidirectionalStreamsInternal(maxStreams, false);
}
void QuicStreamManager::setMaxRemoteUnidirectionalStreams(uint64_t maxStreams) {
setMaxRemoteUnidirectionalStreamsInternal(maxStreams, false);
}
void QuicStreamManager::setMaxRemoteBidirectionalStreamsInternal(
uint64_t maxStreams,
bool force) {
if (maxStreams > kMaxMaxStreams) {
throw QuicTransportException(
"Attempt to set maxStreams beyond the max allowed.",
TransportErrorCode::STREAM_LIMIT_ERROR);
}
StreamId maxStreamId = maxStreams * detail::kStreamIncrement +
initialRemoteBidirectionalStreamId_;
if (force || maxStreamId > maxRemoteBidirectionalStreamId_) {
maxRemoteBidirectionalStreamId_ = maxStreamId;
}
}
void QuicStreamManager::setMaxRemoteUnidirectionalStreamsInternal(
uint64_t maxStreams,
bool force) {
if (maxStreams > kMaxMaxStreams) {
throw QuicTransportException(
"Attempt to set maxStreams beyond the max allowed.",
TransportErrorCode::STREAM_LIMIT_ERROR);
}
StreamId maxStreamId = maxStreams * detail::kStreamIncrement +
initialRemoteUnidirectionalStreamId_;
if (force || maxStreamId > maxRemoteUnidirectionalStreamId_) {
maxRemoteUnidirectionalStreamId_ = maxStreamId;
}
}
void QuicStreamManager::refreshTransportSettings(
const TransportSettings& settings) {
transportSettings_ = &settings;
setMaxRemoteBidirectionalStreamsInternal(
transportSettings_->advertisedInitialMaxStreamsBidi, true);
setMaxRemoteUnidirectionalStreamsInternal(
transportSettings_->advertisedInitialMaxStreamsUni, true);
}
// We create local streams lazily. If a local stream was created
// but not allocated yet, this will allocate a stream.
// This will return nullptr if a stream is closed or un-opened.
QuicStreamState* FOLLY_NULLABLE
QuicStreamManager::getOrCreateOpenedLocalStream(StreamId streamId) {
auto streamIdx =
std::find(openLocalStreams_.begin(), openLocalStreams_.end(), streamId);
if (streamIdx != openLocalStreams_.end()) {
auto streamIdx = std::lower_bound(
openLocalStreams_.begin(), openLocalStreams_.end(), streamId);
if (streamIdx != openLocalStreams_.end() && *streamIdx == streamId) {
// Open a lazily created stream.
auto it = streams_.emplace(
std::piecewise_construct,
@@ -217,9 +264,12 @@ QuicStreamManager::getOrCreatePeerStream(StreamId streamId) {
if (peerStream != streams_.end()) {
return &peerStream->second;
}
auto streamIdx =
std::find(openPeerStreams_.begin(), openPeerStreams_.end(), streamId);
if (streamIdx != openPeerStreams_.end()) {
auto& openPeerStreams = isUnidirectionalStream(streamId)
? openUnidirectionalPeerStreams_
: openBidirectionalPeerStreams_;
auto streamIdx = std::lower_bound(
openPeerStreams.begin(), openPeerStreams.end(), streamId);
if (streamIdx != openPeerStreams.end() && *streamIdx == streamId) {
// Stream was already open, create the state for it lazily.
auto it = streams_.emplace(
std::piecewise_construct,
@@ -229,7 +279,7 @@ QuicStreamManager::getOrCreatePeerStream(StreamId streamId) {
return &it.first->second;
}
auto previousPeerStreams = openPeerStreams_;
auto previousPeerStreams = folly::copy(openPeerStreams);
auto& nextAcceptableStreamId = isUnidirectionalStream(streamId)
? nextAcceptablePeerUnidirectionalStreamId_
: nextAcceptablePeerBidirectionalStreamId_;
@@ -237,7 +287,7 @@ QuicStreamManager::getOrCreatePeerStream(StreamId streamId) {
? maxRemoteUnidirectionalStreamId_
: maxRemoteBidirectionalStreamId_;
auto openedResult = openStreamIfNotClosed(
streamId, openPeerStreams_, nextAcceptableStreamId, maxStreamId);
streamId, openPeerStreams, nextAcceptableStreamId, maxStreamId);
if (openedResult == LocalErrorCode::CREATING_EXISTING_STREAM) {
// Stream could be closed here.
return nullptr;
@@ -248,8 +298,8 @@ QuicStreamManager::getOrCreatePeerStream(StreamId streamId) {
// Copy over the new streams.
std::set_difference(
openPeerStreams_.begin(),
openPeerStreams_.end(),
openPeerStreams.begin(),
openPeerStreams.end(),
std::make_move_iterator(previousPeerStreams.begin()),
std::make_move_iterator(previousPeerStreams.end()),
std::back_inserter(newPeerStreams_));
@@ -331,14 +381,49 @@ void QuicStreamManager::removeClosedStream(StreamId streamId) {
}
streams_.erase(it);
QUIC_STATS(conn_.infoCallback, onQuicStreamClosed);
auto streamItr =
std::find(openPeerStreams_.begin(), openPeerStreams_.end(), streamId);
if (streamItr != openPeerStreams_.end()) {
openPeerStreams_.erase(streamItr);
if (isRemoteStream(nodeType_, streamId)) {
auto& openPeerStreams = isUnidirectionalStream(streamId)
? openUnidirectionalPeerStreams_
: openBidirectionalPeerStreams_;
auto streamItr = std::lower_bound(
openPeerStreams.begin(), openPeerStreams.end(), streamId);
if (streamItr != openPeerStreams.end() && *streamItr == streamId) {
openPeerStreams.erase(streamItr);
// Check if we should send a stream limit update. We need to send an
// update every time we've closed a number of streams >= the set windowing
// fraction.
uint64_t initialStreamLimit = isUnidirectionalStream(streamId)
? transportSettings_->advertisedInitialMaxStreamsUni
: transportSettings_->advertisedInitialMaxStreamsBidi;
uint64_t streamWindow =
initialStreamLimit / streamLimitWindowingFraction_;
uint64_t openableRemoteStreams = isUnidirectionalStream(streamId)
? openableRemoteUnidirectionalStreams()
: openableRemoteBidirectionalStreams();
// The "credit" here is how much available stream space we have based on
// what the initial stream limit was set to.
uint64_t streamCredit =
initialStreamLimit - openableRemoteStreams - openPeerStreams.size();
if (streamCredit >= streamWindow) {
if (isUnidirectionalStream(streamId)) {
uint64_t maxStreams = (maxRemoteUnidirectionalStreamId_ -
initialRemoteUnidirectionalStreamId_) /
detail::kStreamIncrement;
setMaxRemoteUnidirectionalStreams(maxStreams + streamCredit);
remoteUnidirectionalStreamLimitUpdate_ = maxStreams + streamCredit;
} else {
streamItr =
std::find(openLocalStreams_.begin(), openLocalStreams_.end(), streamId);
if (streamItr != openLocalStreams_.end()) {
uint64_t maxStreams = (maxRemoteBidirectionalStreamId_ -
initialRemoteBidirectionalStreamId_) /
detail::kStreamIncrement;
setMaxRemoteBidirectionalStreams(maxStreams + streamCredit);
remoteBidirectionalStreamLimitUpdate_ = maxStreams + streamCredit;
}
}
}
} else {
auto streamItr = std::lower_bound(
openLocalStreams_.begin(), openLocalStreams_.end(), streamId);
if (streamItr != openLocalStreams_.end() && *streamItr == streamId) {
openLocalStreams_.erase(streamItr);
}
}

View File

@@ -11,6 +11,7 @@
#include <quic/QuicConstants.h>
#include <quic/codec/Types.h>
#include <quic/state/StreamData.h>
#include <quic/state/TransportSettings.h>
#include <deque>
#include <map>
#include <numeric>
@@ -28,8 +29,11 @@ class QuicStreamManager {
public:
explicit QuicStreamManager(
QuicConnectionStateBase& conn,
QuicNodeType nodeType)
: conn_(conn), nodeType_(nodeType) {
QuicNodeType nodeType,
const TransportSettings& transportSettings)
: conn_(conn),
nodeType_(nodeType),
transportSettings_(&transportSettings) {
if (nodeType == QuicNodeType::Server) {
nextAcceptablePeerBidirectionalStreamId_ = 0x00;
nextAcceptablePeerUnidirectionalStreamId_ = 0x02;
@@ -37,8 +41,10 @@ class QuicStreamManager {
nextAcceptableLocalUnidirectionalStreamId_ = 0x03;
nextBidirectionalStreamId_ = 0x01;
nextUnidirectionalStreamId_ = 0x03;
initialBidirectionalStreamId_ = 0x01;
initialUnidirectionalStreamId_ = 0x03;
initialLocalBidirectionalStreamId_ = 0x01;
initialLocalUnidirectionalStreamId_ = 0x03;
initialRemoteBidirectionalStreamId_ = 0x00;
initialRemoteUnidirectionalStreamId_ = 0x02;
} else {
nextAcceptablePeerBidirectionalStreamId_ = 0x01;
nextAcceptablePeerUnidirectionalStreamId_ = 0x03;
@@ -46,9 +52,12 @@ class QuicStreamManager {
nextAcceptableLocalUnidirectionalStreamId_ = 0x02;
nextBidirectionalStreamId_ = 0x00;
nextUnidirectionalStreamId_ = 0x02;
initialBidirectionalStreamId_ = 0x00;
initialUnidirectionalStreamId_ = 0x02;
initialLocalBidirectionalStreamId_ = 0x00;
initialLocalUnidirectionalStreamId_ = 0x02;
initialRemoteBidirectionalStreamId_ = 0x01;
initialRemoteUnidirectionalStreamId_ = 0x03;
}
refreshTransportSettings(transportSettings);
}
/*
* Create the state for a stream if it does not exist and return it. Note this
@@ -117,7 +126,13 @@ class QuicStreamManager {
*/
bool streamExists(StreamId streamId) {
return std::binary_search(
openPeerStreams_.begin(), openPeerStreams_.end(), streamId) ||
openBidirectionalPeerStreams_.begin(),
openBidirectionalPeerStreams_.end(),
streamId) ||
std::binary_search(
openUnidirectionalPeerStreams_.begin(),
openUnidirectionalPeerStreams_.end(),
streamId) ||
std::binary_search(
openLocalStreams_.begin(), openLocalStreams_.end(), streamId);
}
@@ -158,7 +173,8 @@ class QuicStreamManager {
*/
void clearOpenStreams() {
openLocalStreams_.clear();
openPeerStreams_.clear();
openBidirectionalPeerStreams_.clear();
openUnidirectionalPeerStreams_.clear();
streams_.clear();
}
@@ -277,6 +293,54 @@ class QuicStreamManager {
uint64_t maxStreams,
bool force = false);
/*
* Set the max number of remote bidirectional streams. Can only be increased
* unless force is true.
*/
void setMaxRemoteBidirectionalStreams(uint64_t maxStreams);
/*
* Set the max number of remote unidirectional streams. Can only be increased
* unless force is true.
*/
void setMaxRemoteUnidirectionalStreams(uint64_t maxStreams);
void refreshTransportSettings(const TransportSettings& settings);
/*
* Sets the "window-by" fraction for sending stream limit updates. E.g.
* setting the fraction to two when the initial stream limit was 100 will
* cause the stream manager to update the relevant stream limit update when
* 50 streams have been closed.
*/
void setStreamLimitWindowingFraction(uint64_t fraction) {
if (fraction > 0) {
streamLimitWindowingFraction_ = fraction;
}
}
/*
* The next value that should be sent in a bidirectional max streams frame,
* if any. This is potentially updated every time a bidirectional stream is
* closed. Calling this function "consumes" the update.
*/
folly::Optional<uint64_t> remoteBidirectionalStreamLimitUpdate() {
auto ret = remoteBidirectionalStreamLimitUpdate_;
remoteBidirectionalStreamLimitUpdate_ = folly::none;
return ret;
}
/*
* The next value that should be sent in a unidirectional max streams frame,
* if any. This is potentially updated every time a unidirectional stream is
* closed. Calling this function "consumes" the update.
*/
folly::Optional<uint64_t> remoteUnidirectionalStreamLimitUpdate() {
auto ret = remoteUnidirectionalStreamLimitUpdate_;
remoteUnidirectionalStreamLimitUpdate_ = folly::none;
return ret;
}
/*
* Returns a const reference to the underlying stream window updates
* container.
@@ -488,10 +552,20 @@ class QuicStreamManager {
// TODO figure out a better interface here.
/*
* Returns a mutable reference to the underlying open peer streams container.
* Returns a mutable reference to the underlying open bidirectional peer
* streams container.
*/
auto& openPeerStreams() {
return openPeerStreams_;
auto& openBidirectionalPeerStreams() {
return openBidirectionalPeerStreams_;
}
// TODO figure out a better interface here.
/*
* Returns a mutable reference to the underlying open peer unidirectional
* streams container.
*/
auto& openUnidirectionalPeerStreams() {
return openUnidirectionalPeerStreams_;
}
// TODO figure out a better interface here.
@@ -579,6 +653,13 @@ class QuicStreamManager {
QuicStreamState* FOLLY_NULLABLE getOrCreatePeerStream(StreamId streamId);
void setMaxRemoteBidirectionalStreamsInternal(
uint64_t maxStreams,
bool force);
void setMaxRemoteUnidirectionalStreamsInternal(
uint64_t maxStreams,
bool force);
QuicConnectionStateBase& conn_;
QuicNodeType nodeType_;
@@ -608,18 +689,39 @@ class QuicStreamManager {
StreamId maxLocalUnidirectionalStreamId_{0};
StreamId maxRemoteBidirectionalStreamId_{kMaxStreamId};
StreamId maxRemoteBidirectionalStreamId_{0};
StreamId maxRemoteUnidirectionalStreamId_{kMaxStreamId};
StreamId maxRemoteUnidirectionalStreamId_{0};
StreamId initialBidirectionalStreamId_{0};
StreamId initialLocalBidirectionalStreamId_{0};
StreamId initialUnidirectionalStreamId_{0};
StreamId initialLocalUnidirectionalStreamId_{0};
StreamId initialRemoteBidirectionalStreamId_{0};
StreamId initialRemoteUnidirectionalStreamId_{0};
// The fraction to determine the window by which we will signal the need to
// send stream limit updates
uint64_t streamLimitWindowingFraction_{2};
// Contains the value of a stream window update that should be sent for
// remote bidirectional streams.
folly::Optional<uint64_t> remoteBidirectionalStreamLimitUpdate_;
// Contains the value of a stream window update that should be sent for
// remote bidirectional streams.
folly::Optional<uint64_t> remoteUnidirectionalStreamLimitUpdate_;
uint64_t numControlStreams_{0};
// Streams that are opened by the peer on the connection. Ordered by id.
std::deque<StreamId> openPeerStreams_;
// Bidirectional streams that are opened by the peer on the connection.
// Ordered by id.
std::deque<StreamId> openBidirectionalPeerStreams_;
// Unidirectional streams that are opened by the peer on the connection.
// Ordered by id.
std::deque<StreamId> openUnidirectionalPeerStreams_;
// Streams that are opened locally on the connection. Ordered by id.
std::deque<StreamId> openLocalStreams_;
@@ -668,6 +770,8 @@ class QuicStreamManager {
// Record whether or not we are app-idle.
bool isAppIdle_{false};
const TransportSettings* transportSettings_;
};
} // namespace quic

View File

@@ -63,6 +63,9 @@ folly::Optional<QuicSimpleFrame> updateSimpleFrameOnPacketClone(
[&](const NewConnectionIdFrame& frame)
-> folly::Optional<QuicSimpleFrame> {
return QuicSimpleFrame(frame);
},
[&](const MaxStreamsFrame& frame) -> folly::Optional<QuicSimpleFrame> {
return QuicSimpleFrame(frame);
});
}
@@ -124,6 +127,9 @@ void updateSimpleFrameOnPacketLoss(
},
[&](const NewConnectionIdFrame& frame) {
conn.pendingEvents.frames.push_back(frame);
},
[&](const MaxStreamsFrame& frame) {
conn.pendingEvents.frames.push_back(frame);
});
}
@@ -176,6 +182,16 @@ bool updateSimpleFrameOnPacketReceived(
[&](const NewConnectionIdFrame&) {
// TODO junqiw
return false;
},
[&](const MaxStreamsFrame& maxStreamsFrame) {
if (maxStreamsFrame.isForBidirectionalStream()) {
conn.streamManager->setMaxLocalBidirectionalStreams(
maxStreamsFrame.maxStreams);
} else {
conn.streamManager->setMaxLocalUnidirectionalStreams(
maxStreamsFrame.maxStreams);
}
return true;
});
}

View File

@@ -594,6 +594,9 @@ struct QuicConnectionStateBase {
// Settings for transports.
TransportSettings transportSettings;
// Whether we've set the transporot parameters from transportSettings yet.
bool transportParametersEncoded{false};
// Value of the negotiated ack delay exponent.
uint64_t peerAckDelayExponent{kDefaultAckDelayExponent};

View File

@@ -876,19 +876,19 @@ TEST_F(QuicServerStreamFunctionsTest, GetOrCreateClientOutOfOrderStream) {
EXPECT_TRUE(conn.streamManager->streamExists(outOfOrderStream));
// peer stream starts from 0x00
EXPECT_EQ(
conn.streamManager->openPeerStreams().size(),
conn.streamManager->openBidirectionalPeerStreams().size(),
((outOfOrderStream) / kStreamIncrement) + 1);
conn.streamManager->getStream(existingStream);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_TRUE(conn.streamManager->streamExists(outOfOrderStream));
EXPECT_EQ(
conn.streamManager->openPeerStreams().size(),
conn.streamManager->openBidirectionalPeerStreams().size(),
((outOfOrderStream) / kStreamIncrement) + 1);
conn.streamManager->openPeerStreams().erase(std::find(
conn.streamManager->openPeerStreams().begin(),
conn.streamManager->openPeerStreams().end(),
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
closedStream));
EXPECT_EQ(conn.streamManager->getStream(closedStream), nullptr);
}
@@ -916,9 +916,9 @@ TEST_F(QuicServerStreamFunctionsTest, GetOrCreateClosedClientStream) {
StreamId outOfOrderStream1 = 100;
StreamId closedStream = 48;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openPeerStreams().erase(std::find(
conn.streamManager->openPeerStreams().begin(),
conn.streamManager->openPeerStreams().end(),
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
closedStream));
EXPECT_EQ(conn.streamManager->getStream(closedStream), nullptr);
}
@@ -929,13 +929,13 @@ TEST_F(
StreamId outOfOrderStream1 = 96;
StreamId outOfOrderStream2 = 100;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openPeerStreams().erase(std::find(
conn.streamManager->openPeerStreams().begin(),
conn.streamManager->openPeerStreams().end(),
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
outOfOrderStream1));
conn.streamManager->getStream(outOfOrderStream2);
EXPECT_EQ(
conn.streamManager->openPeerStreams().size(),
conn.streamManager->openBidirectionalPeerStreams().size(),
(outOfOrderStream2) / kStreamIncrement);
}
@@ -943,13 +943,13 @@ TEST_F(QuicStreamFunctionsTest, GetOrCreateServerStreamAfterClosingLastStream) {
StreamId outOfOrderStream1 = 97;
StreamId outOfOrderStream2 = 101;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openPeerStreams().erase(std::find(
conn.streamManager->openPeerStreams().begin(),
conn.streamManager->openPeerStreams().end(),
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
outOfOrderStream1));
conn.streamManager->getStream(outOfOrderStream2);
EXPECT_EQ(
conn.streamManager->openPeerStreams().size(),
conn.streamManager->openBidirectionalPeerStreams().size(),
(outOfOrderStream2 + 1) / kStreamIncrement);
}
@@ -957,9 +957,9 @@ TEST_F(QuicStreamFunctionsTest, GetOrCreateClosedServerStream) {
StreamId outOfOrderStream1 = 97;
StreamId closedStream = 49;
conn.streamManager->getStream(outOfOrderStream1);
conn.streamManager->openPeerStreams().erase(std::find(
conn.streamManager->openPeerStreams().begin(),
conn.streamManager->openPeerStreams().end(),
conn.streamManager->openBidirectionalPeerStreams().erase(std::find(
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
closedStream));
EXPECT_EQ(conn.streamManager->getStream(closedStream), nullptr);
}
@@ -1537,13 +1537,13 @@ TEST_F(QuicServerStreamFunctionsTest, ServerGetClientQuicStream) {
std::deque<StreamId> newStreams = {0x0, 0x4, 0x8, 0xc, 0x10};
EXPECT_EQ(conn.streamManager->getStream(clientStream)->id, clientStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->newPeerStreams(), newStreams);
StreamId clientStream2 = 0x4;
EXPECT_EQ(conn.streamManager->getStream(clientStream2)->id, clientStream2);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->newPeerStreams().size(), 5);
EXPECT_EQ(conn.streamManager->newPeerStreams(), newStreams);
@@ -1551,7 +1551,7 @@ TEST_F(QuicServerStreamFunctionsTest, ServerGetClientQuicStream) {
newStreams = {0x0, 0x2, 0x4, 0x6, 0x8, 0xc, 0x10};
EXPECT_EQ(conn.streamManager->getStream(clientStream3)->id, clientStream3);
EXPECT_EQ(conn.streamManager->streamCount(), 3);
EXPECT_EQ(conn.streamManager->openPeerStreams().size(), 7);
EXPECT_EQ(conn.streamManager->openUnidirectionalPeerStreams().size(), 2);
std::sort(
conn.streamManager->newPeerStreams().begin(),
conn.streamManager->newPeerStreams().end());
@@ -1636,12 +1636,12 @@ TEST_F(QuicStreamFunctionsTest, ClientGetServerQuicStream) {
StreamId serverStream = 0x09;
EXPECT_EQ(conn.streamManager->getStream(serverStream)->id, serverStream);
EXPECT_EQ(conn.streamManager->streamCount(), 1);
EXPECT_EQ(conn.streamManager->openPeerStreams().size(), 3);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 3);
StreamId serverStream2 = 0x05;
EXPECT_EQ(conn.streamManager->getStream(serverStream2)->id, serverStream2);
EXPECT_EQ(conn.streamManager->streamCount(), 2);
EXPECT_EQ(conn.streamManager->openPeerStreams().size(), 3);
EXPECT_EQ(conn.streamManager->openBidirectionalPeerStreams().size(), 3);
}
TEST_F(QuicStreamFunctionsTest, ClientGetClientQuicStream) {
@@ -1692,10 +1692,10 @@ TEST_F(QuicStreamFunctionsTest, StreamExists) {
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened));
auto it = std::find(
conn.streamManager->openPeerStreams().begin(),
conn.streamManager->openPeerStreams().end(),
conn.streamManager->openBidirectionalPeerStreams().begin(),
conn.streamManager->openBidirectionalPeerStreams().end(),
peerAutoOpened);
conn.streamManager->openPeerStreams().erase(it);
conn.streamManager->openBidirectionalPeerStreams().erase(it);
conn.streamManager->removeClosedStream(peerStream);
@@ -1704,6 +1704,34 @@ TEST_F(QuicStreamFunctionsTest, StreamExists) {
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened2));
}
TEST_F(QuicStreamFunctionsTest, StreamLimitUpdates) {
StreamId peerStream = 13;
StreamId peerAutoOpened = 5;
StreamId peerAutoOpened2 = 9;
StreamId notOpenedPeer = 17;
conn.streamManager->setStreamLimitWindowingFraction(
conn.transportSettings.advertisedInitialMaxStreamsBidi);
conn.streamManager->getStream(peerStream)->send.state =
StreamSendStates::Closed{};
conn.streamManager->getStream(peerStream)->recv.state =
StreamReceiveStates::Closed{};
EXPECT_FALSE(conn.streamManager->streamExists(notOpenedPeer));
EXPECT_TRUE(conn.streamManager->streamExists(peerStream));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened));
conn.streamManager->removeClosedStream(peerStream);
EXPECT_FALSE(conn.streamManager->streamExists(peerStream));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened));
EXPECT_TRUE(conn.streamManager->streamExists(peerAutoOpened2));
auto update = conn.streamManager->remoteBidirectionalStreamLimitUpdate();
ASSERT_TRUE(update);
EXPECT_EQ(
update.value(),
conn.transportSettings.advertisedInitialMaxStreamsBidi + 1);
}
TEST_F(QuicStreamFunctionsTest, AllBytesTillFinAcked) {
StreamId id = 3;
QuicStreamState stream(id, conn);

View File

@@ -178,5 +178,75 @@ TEST_F(QuicStreamManagerTest, TestAppIdleCloseControlStream) {
manager.removeClosedStream(stream->id);
EXPECT_TRUE(manager.isAppIdle());
}
TEST_F(QuicStreamManagerTest, StreamLimitWindowedUpdate) {
auto& manager = *conn.streamManager;
conn.transportSettings.advertisedInitialMaxStreamsBidi = 100;
conn.transportSettings.advertisedInitialMaxStreamsUni = 100;
manager.refreshTransportSettings(conn.transportSettings);
manager.setStreamLimitWindowingFraction(4);
for (int i = 0; i < 100; i++) {
manager.getStream(i * detail::kStreamIncrement);
manager.getStream(2 + i * detail::kStreamIncrement);
}
for (int i = 0; i < 25; i++) {
auto stream = manager.getStream(i * detail::kStreamIncrement);
stream->send.state = StreamSendStates::Closed();
stream->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream->id);
stream = manager.getStream(2 + i * detail::kStreamIncrement);
stream->send.state = StreamSendStates::Closed();
stream->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream->id);
}
auto update = manager.remoteBidirectionalStreamLimitUpdate();
ASSERT_TRUE(update);
EXPECT_EQ(update.value(), 125);
EXPECT_FALSE(manager.remoteBidirectionalStreamLimitUpdate());
update = manager.remoteUnidirectionalStreamLimitUpdate();
ASSERT_TRUE(update);
EXPECT_EQ(update.value(), 125);
EXPECT_FALSE(manager.remoteUnidirectionalStreamLimitUpdate());
}
TEST_F(QuicStreamManagerTest, StreamLimitNoWindowedUpdate) {
auto& manager = *conn.streamManager;
conn.transportSettings.advertisedInitialMaxStreamsBidi = 100;
manager.refreshTransportSettings(conn.transportSettings);
manager.setStreamLimitWindowingFraction(4);
for (int i = 0; i < 100; i++) {
manager.getStream(i * detail::kStreamIncrement);
}
for (int i = 0; i < 24; i++) {
auto stream = manager.getStream(i * detail::kStreamIncrement);
stream->send.state = StreamSendStates::Closed();
stream->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream->id);
}
auto update = manager.remoteBidirectionalStreamLimitUpdate();
EXPECT_FALSE(update);
}
TEST_F(QuicStreamManagerTest, StreamLimitManyWindowedUpdate) {
auto& manager = *conn.streamManager;
conn.transportSettings.advertisedInitialMaxStreamsBidi = 100;
manager.refreshTransportSettings(conn.transportSettings);
manager.setStreamLimitWindowingFraction(4);
for (int i = 0; i < 100; i++) {
manager.getStream(i * detail::kStreamIncrement);
}
for (int i = 0; i < 50; i++) {
auto stream = manager.getStream(i * detail::kStreamIncrement);
stream->send.state = StreamSendStates::Closed();
stream->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream->id);
}
auto update = manager.remoteBidirectionalStreamLimitUpdate();
ASSERT_TRUE(update);
EXPECT_EQ(update.value(), 150);
EXPECT_FALSE(manager.remoteBidirectionalStreamLimitUpdate());
EXPECT_FALSE(manager.remoteUnidirectionalStreamLimitUpdate());
}
} // namespace test
} // namespace quic