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

Separate app-limited and app-idle

Summary:
Current app-limited is defined as the connection doesn't have a no
non-control stream. I'm changing this to be app-idle. And app-limited will be
changed to a state in connection where app isn't sending bytes fast enough to
keep congestion controller probing higher bandwidth.

Reviewed By: mjoras

Differential Revision: D15456199

fbshipit-source-id: e084bc133284ae37ee6da8ebb3c12f505011521e
This commit is contained in:
Yang Chi
2019-05-24 12:11:50 -07:00
committed by Facebook Github Bot
parent 41a6b82538
commit 1dbe39fd76
16 changed files with 233 additions and 106 deletions

View File

@@ -2127,6 +2127,17 @@ void QuicTransportBase::writeSocketData() {
setIdleTimer(); setIdleTimer();
conn_->receivedNewPacketBeforeWrite = false; conn_->receivedNewPacketBeforeWrite = false;
} }
// Check if we are app-limited after finish this round of sending
auto currentSendBufLen = conn_->flowControlState.sumCurStreamBufferLen;
auto lossBufferEmpty = !conn_->streamManager->hasLoss() &&
conn_->cryptoState->initialStream.lossBuffer.empty() &&
conn_->cryptoState->handshakeStream.lossBuffer.empty() &&
conn_->cryptoState->oneRttStream.lossBuffer.empty();
if (conn_->congestionController &&
currentSendBufLen < conn_->udpSendPacketLen && lossBufferEmpty &&
conn_->congestionController->getWritableBytes()) {
conn_->congestionController->setAppLimited();
}
} }
} }
// Writing data could write out an ack which could cause us to cancel // Writing data could write out an ack which could cause us to cancel

View File

@@ -366,7 +366,8 @@ void updateConnection(
packetNum, packetNum,
(uint64_t)encodedSize, (uint64_t)encodedSize,
(int)isHandshake, (int)isHandshake,
(int)pureAck); (int)pureAck,
pkt.isAppLimited);
conn.lossState.largestSent = std::max(conn.lossState.largestSent, packetNum); conn.lossState.largestSent = std::max(conn.lossState.largestSent, packetNum);
if (conn.congestionController && !pureAck) { if (conn.congestionController && !pureAck) {
conn.congestionController->onPacketSent(pkt); conn.congestionController->onPacketSent(pkt);

View File

@@ -1467,26 +1467,26 @@ INSTANTIATE_TEST_CASE_P(
QuicTransportImplTestUniBidi, QuicTransportImplTestUniBidi,
Values(true, false)); Values(true, false));
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTest) { TEST_P(QuicTransportImplTestUniBidi, AppIdleTest) {
auto& conn = transport->getConnectionState(); auto& conn = transport->getConnectionState();
auto mockCongestionController = std::make_unique<MockCongestionController>(); auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get(); auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController); conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(0); EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(0);
auto stream = createStream(transport, GetParam()); auto stream = createStream(transport, GetParam());
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)); EXPECT_CALL(*rawCongestionController, setAppIdle(true, _));
transport->closeStream(stream); transport->closeStream(stream);
} }
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestControlStreams) { TEST_P(QuicTransportImplTestUniBidi, AppIdleTestControlStreams) {
auto& conn = transport->getConnectionState(); auto& conn = transport->getConnectionState();
auto mockCongestionController = std::make_unique<MockCongestionController>(); auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get(); auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController); conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(0); EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(0);
auto stream = createStream(transport, GetParam()); auto stream = createStream(transport, GetParam());
ASSERT_TRUE(stream); ASSERT_TRUE(stream);
@@ -1497,25 +1497,25 @@ TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestControlStreams) {
ASSERT_TRUE(ctrlStream2); ASSERT_TRUE(ctrlStream2);
transport->setControlStream(ctrlStream2); transport->setControlStream(ctrlStream2);
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)); EXPECT_CALL(*rawCongestionController, setAppIdle(true, _));
transport->closeStream(stream); transport->closeStream(stream);
} }
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestOnlyControlStreams) { TEST_P(QuicTransportImplTestUniBidi, AppIdleTestOnlyControlStreams) {
auto& conn = transport->getConnectionState(); auto& conn = transport->getConnectionState();
auto mockCongestionController = std::make_unique<MockCongestionController>(); auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get(); auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController); conn.congestionController = std::move(mockCongestionController);
auto ctrlStream1 = createStream(transport, GetParam()); auto ctrlStream1 = createStream(transport, GetParam());
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)).Times(1); EXPECT_CALL(*rawCongestionController, setAppIdle(true, _)).Times(1);
transport->setControlStream(ctrlStream1); transport->setControlStream(ctrlStream1);
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(1); EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(1);
auto ctrlStream2 = createStream(transport, GetParam()); auto ctrlStream2 = createStream(transport, GetParam());
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)).Times(1); EXPECT_CALL(*rawCongestionController, setAppIdle(true, _)).Times(1);
transport->setControlStream(ctrlStream2); transport->setControlStream(ctrlStream2);
EXPECT_CALL(*rawCongestionController, setAppLimited(_, _)).Times(0); EXPECT_CALL(*rawCongestionController, setAppIdle(_, _)).Times(0);
transport->closeStream(ctrlStream1); transport->closeStream(ctrlStream1);
transport->closeStream(ctrlStream2); transport->closeStream(ctrlStream2);
} }

View File

@@ -358,8 +358,96 @@ TEST_F(QuicTransportTest, WriteDataWithProbing) {
loopForWrites(); loopForWrites();
// Pending numProbePackets is cleared: // Pending numProbePackets is cleared:
EXPECT_EQ(0, conn.pendingEvents.numProbePackets); EXPECT_EQ(0, conn.pendingEvents.numProbePackets);
// both write and onPacketSent will inquire getWritableBytes transport_->close(folly::none);
EXPECT_EQ(onPacketSentCounter + socketWriteCounter, getWritableBytesCounter); }
TEST_F(QuicTransportTest, NotAppLimitedWithLoss) {
auto& conn = transport_->getConnectionState();
// Replace with MockConnectionCallback:
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, getWritableBytes())
.WillRepeatedly(Return(5000));
auto stream = transport_->createBidirectionalStream().value();
auto lossStream = transport_->createBidirectionalStream().value();
conn.streamManager->addLoss(lossStream);
conn.streamManager->getStream(lossStream)
->lossBuffer.emplace_back(
IOBuf::copyBuffer("Mountains may depart"), 0, false);
transport_->writeChain(
stream,
IOBuf::copyBuffer("An elephant sitting still"),
false,
false,
nullptr);
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(0);
loopForWrites();
transport_->close(folly::none);
}
TEST_F(QuicTransportTest, NotAppLimitedWithNoWritableBytes) {
auto& conn = transport_->getConnectionState();
// Replace with MockConnectionCallback:
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, getWritableBytes())
.WillRepeatedly(Invoke([&]() {
if (conn.outstandingPackets.empty()) {
return 5000;
}
return 0;
}));
auto stream = transport_->createBidirectionalStream().value();
transport_->writeChain(
stream,
IOBuf::copyBuffer("An elephant sitting still"),
false,
false,
nullptr);
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(0);
loopForWrites();
transport_->close(folly::none);
}
TEST_F(QuicTransportTest, NotAppLimitedWithLargeBuffer) {
auto& conn = transport_->getConnectionState();
// Replace with MockConnectionCallback:
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, getWritableBytes())
.WillRepeatedly(Return(5000));
auto stream = transport_->createBidirectionalStream().value();
auto buf = buildRandomInputData(100 * 2000);
transport_->writeChain(stream, buf->clone(), false, false, nullptr);
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(0);
loopForWrites();
transport_->close(folly::none);
}
TEST_F(QuicTransportTest, AppLimited) {
auto& conn = transport_->getConnectionState();
// Replace with MockConnectionCallback:
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, getWritableBytes())
.WillRepeatedly(Return(5000));
auto stream = transport_->createBidirectionalStream().value();
transport_->writeChain(
stream,
IOBuf::copyBuffer("An elephant sitting still"),
false,
false,
nullptr);
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(1);
loopForWrites();
transport_->close(folly::none); transport_->close(folly::none);
} }

View File

@@ -308,7 +308,10 @@ std::chrono::microseconds Copa::getPacingInterval() const noexcept {
void Copa::setMinimalPacingInterval(std::chrono::microseconds) noexcept {} void Copa::setMinimalPacingInterval(std::chrono::microseconds) noexcept {}
void Copa::setAppLimited(bool, TimePoint) noexcept { /* unsupported */ void Copa::setAppIdle(bool, TimePoint) noexcept { /* unsupported */
}
void Copa::setAppLimited() { /* unsupported */
} }
bool Copa::isAppLimited() const noexcept { bool Copa::isAppLimited() const noexcept {

View File

@@ -43,7 +43,8 @@ class Copa : public CongestionController {
uint64_t getBytesInFlight() const noexcept; uint64_t getBytesInFlight() const noexcept;
void setConnectionEmulation(uint8_t) noexcept override; void setConnectionEmulation(uint8_t) noexcept override;
void setAppLimited(bool, TimePoint) noexcept override; void setAppIdle(bool, TimePoint) noexcept override;
void setAppLimited() override;
// pacing related functions, not implemented for copa // pacing related functions, not implemented for copa
bool canBePaced() const noexcept override; bool canBePaced() const noexcept override;

View File

@@ -158,7 +158,10 @@ std::chrono::microseconds NewReno::getPacingInterval() const noexcept {
void NewReno::setMinimalPacingInterval(std::chrono::microseconds) noexcept {} void NewReno::setMinimalPacingInterval(std::chrono::microseconds) noexcept {}
void NewReno::setAppLimited(bool, TimePoint) noexcept { /* unsupported */ void NewReno::setAppIdle(bool, TimePoint) noexcept { /* unsupported */
}
void NewReno::setAppLimited() { /* unsupported */
} }
bool NewReno::isAppLimited() const noexcept { bool NewReno::isAppLimited() const noexcept {

View File

@@ -27,7 +27,9 @@ class NewReno : public CongestionController {
uint64_t getCongestionWindow() const noexcept override; uint64_t getCongestionWindow() const noexcept override;
void setConnectionEmulation(uint8_t) noexcept override; void setConnectionEmulation(uint8_t) noexcept override;
bool canBePaced() const noexcept override; bool canBePaced() const noexcept override;
void setAppLimited(bool, TimePoint) noexcept override; void setAppIdle(bool, TimePoint) noexcept override;
void setAppLimited() override;
CongestionControlType type() const noexcept override; CongestionControlType type() const noexcept override;
bool inSlowStart() const noexcept; bool inSlowStart() const noexcept;

View File

@@ -154,11 +154,11 @@ bool Cubic::canBePaced() const noexcept {
return pacingEnabled; return pacingEnabled;
} }
void Cubic::setAppLimited(bool limited, TimePoint eventTime) noexcept { void Cubic::setAppIdle(bool idle, TimePoint eventTime) noexcept {
QUIC_TRACE( QUIC_TRACE(
cubic_applimited, cubic_appidle,
conn_, conn_,
limited, idle,
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(
eventTime.time_since_epoch()) eventTime.time_since_epoch())
.count(), .count(),
@@ -167,22 +167,32 @@ void Cubic::setAppLimited(bool limited, TimePoint eventTime) noexcept {
steadyState_.lastReductionTime->time_since_epoch()) steadyState_.lastReductionTime->time_since_epoch())
.count() .count()
: -1); : -1);
bool currentAppLimited = isAppLimited(); bool currentAppIdle = isAppIdle();
if (!currentAppLimited && limited) { if (!currentAppIdle && idle) {
quiescenceStart_ = eventTime; quiescenceStart_ = eventTime;
} }
if (!limited && currentAppLimited && *quiescenceStart_ <= eventTime && if (!idle && currentAppIdle && *quiescenceStart_ <= eventTime &&
steadyState_.lastReductionTime) { steadyState_.lastReductionTime) {
*steadyState_.lastReductionTime += *steadyState_.lastReductionTime +=
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(
eventTime - *quiescenceStart_); eventTime - *quiescenceStart_);
} }
if (!limited) { if (!idle) {
quiescenceStart_ = folly::none; quiescenceStart_ = folly::none;
} }
} }
void Cubic::setAppLimited() {
// we use app-idle for Cubic
}
bool Cubic::isAppLimited() const noexcept { bool Cubic::isAppLimited() const noexcept {
// Or maybe always false. This doesn't really matter for Cubic. Channeling
// isAppIdle() makes testing easier.
return isAppIdle();
}
bool Cubic::isAppIdle() const noexcept {
return quiescenceStart_.hasValue(); return quiescenceStart_.hasValue();
} }

View File

@@ -97,7 +97,8 @@ class Cubic : public CongestionController {
uint64_t getCongestionWindow() const noexcept override; uint64_t getCongestionWindow() const noexcept override;
bool canBePaced() const noexcept override; bool canBePaced() const noexcept override;
void setConnectionEmulation(uint8_t) noexcept override; void setConnectionEmulation(uint8_t) noexcept override;
void setAppLimited(bool limited, TimePoint eventTime) noexcept override; void setAppIdle(bool idle, TimePoint eventTime) noexcept override;
void setAppLimited() override;
bool isAppLimited() const noexcept override; bool isAppLimited() const noexcept override;
@@ -116,6 +117,7 @@ class Cubic : public CongestionController {
CubicStates state_{CubicStates::Hystart}; CubicStates state_{CubicStates::Hystart};
private: private:
bool isAppIdle() const noexcept;
void onPacketAcked(const AckEvent& ack); void onPacketAcked(const AckEvent& ack);
void onPacketAckedInHystart(const AckEvent& ack); void onPacketAckedInHystart(const AckEvent& ack);
void onPacketAckedInSteady(const AckEvent& ack); void onPacketAckedInSteady(const AckEvent& ack);

View File

@@ -124,7 +124,7 @@ TEST_F(CubicTest, CwndIncreaseAfterReduction) {
EXPECT_EQ(CubicStates::Steady, cubic.state()); EXPECT_EQ(CubicStates::Steady, cubic.state());
} }
TEST_F(CubicTest, AppLimited) { TEST_F(CubicTest, AppIdle) {
QuicConnectionStateBase conn(QuicNodeType::Client); QuicConnectionStateBase conn(QuicNodeType::Client);
conn.udpSendPacketLen = 1500; conn.udpSendPacketLen = 1500;
TestingCubic cubic(conn); TestingCubic cubic(conn);
@@ -150,7 +150,7 @@ TEST_F(CubicTest, AppLimited) {
EXPECT_GT(cubic.getCongestionWindow(), cwnd); EXPECT_GT(cubic.getCongestionWindow(), cwnd);
cwnd = cubic.getCongestionWindow(); cwnd = cubic.getCongestionWindow();
cubic.setAppLimited(true, reductionTime + 1100ms); cubic.setAppIdle(true, reductionTime + 1100ms);
EXPECT_TRUE(cubic.isAppLimited()); EXPECT_TRUE(cubic.isAppLimited());
auto packet2 = makeTestingWritePacket(2, 1000, 3000); auto packet2 = makeTestingWritePacket(2, 1000, 3000);
cubic.onPacketSent(packet2); cubic.onPacketSent(packet2);
@@ -159,7 +159,7 @@ TEST_F(CubicTest, AppLimited) {
EXPECT_EQ(cubic.getCongestionWindow(), cwnd); EXPECT_EQ(cubic.getCongestionWindow(), cwnd);
// 1 seconds of quiescence // 1 seconds of quiescence
cubic.setAppLimited(false, reductionTime + 2100ms); cubic.setAppIdle(false, reductionTime + 2100ms);
EXPECT_FALSE(cubic.isAppLimited()); EXPECT_FALSE(cubic.isAppLimited());
auto packet3 = makeTestingWritePacket(3, 1000, 4000); auto packet3 = makeTestingWritePacket(3, 1000, 4000);
cubic.onPacketSent(packet3); cubic.onPacketSent(packet3);

View File

@@ -153,7 +153,7 @@ QuicStreamManager::getOrCreateOpenedLocalStream(StreamId streamId) {
QuicStreamState* QuicStreamManager::getStream(StreamId streamId) { QuicStreamState* QuicStreamManager::getStream(StreamId streamId) {
if (isRemoteStream(nodeType_, streamId)) { if (isRemoteStream(nodeType_, streamId)) {
auto stream = getOrCreatePeerStream(streamId); auto stream = getOrCreatePeerStream(streamId);
updateAppLimitedState(); updateAppIdleState();
return stream; return stream;
} }
auto it = streams_.find(streamId); auto it = streams_.find(streamId);
@@ -169,7 +169,7 @@ QuicStreamState* QuicStreamManager::getStream(StreamId streamId) {
"Trying to get unopened local stream", "Trying to get unopened local stream",
TransportErrorCode::STREAM_STATE_ERROR); TransportErrorCode::STREAM_STATE_ERROR);
} }
updateAppLimitedState(); updateAppIdleState();
return stream; return stream;
} }
@@ -298,7 +298,7 @@ QuicStreamManager::createStream(StreamId streamId) {
std::forward_as_tuple(streamId), std::forward_as_tuple(streamId),
std::forward_as_tuple(streamId, conn_)); std::forward_as_tuple(streamId, conn_));
QUIC_STATS(conn_.infoCallback, onNewQuicStream); QUIC_STATS(conn_.infoCallback, onNewQuicStream);
updateAppLimitedState(); updateAppIdleState();
return &it.first->second; return &it.first->second;
} }
@@ -342,7 +342,7 @@ void QuicStreamManager::removeClosedStream(StreamId streamId) {
openLocalStreams_.erase(streamItr); openLocalStreams_.erase(streamItr);
} }
} }
updateAppLimitedState(); updateAppIdleState();
} }
void QuicStreamManager::updateLossStreams(QuicStreamState& stream) { void QuicStreamManager::updateLossStreams(QuicStreamState& stream) {
@@ -403,18 +403,18 @@ void QuicStreamManager::updatePeekableStreams(QuicStreamState& stream) {
} }
} }
void QuicStreamManager::updateAppLimitedState() { void QuicStreamManager::updateAppIdleState() {
bool currentNonCtrlStreams = hasNonCtrlStreams(); bool currentNonCtrlStreams = hasNonCtrlStreams();
if (isAppLimited_ && !currentNonCtrlStreams) { if (isAppIdle_ && !currentNonCtrlStreams) {
// We were app limited, and we continue to be app limited. // We were app limited, and we continue to be app limited.
return; return;
} else if (!isAppLimited_ && currentNonCtrlStreams) { } else if (!isAppIdle_ && currentNonCtrlStreams) {
// We were not app limited, and we continue to be not app limited. // We were not app limited, and we continue to be not app limited.
return; return;
} }
isAppLimited_ = !currentNonCtrlStreams; isAppIdle_ = !currentNonCtrlStreams;
if (conn_.congestionController) { if (conn_.congestionController) {
conn_.congestionController->setAppLimited(isAppLimited_, Clock::now()); conn_.congestionController->setAppIdle(isAppIdle_, Clock::now());
} }
} }
@@ -423,10 +423,10 @@ void QuicStreamManager::setStreamAsControl(QuicStreamState& stream) {
stream.isControl = true; stream.isControl = true;
numControlStreams_++; numControlStreams_++;
} }
updateAppLimitedState(); updateAppIdleState();
} }
bool QuicStreamManager::isAppLimited() const { bool QuicStreamManager::isAppIdle() const {
return isAppLimited_; return isAppIdle_;
} }
} // namespace quic } // namespace quic

View File

@@ -563,16 +563,16 @@ class QuicStreamManager {
dataRejectedStreams_.clear(); dataRejectedStreams_.clear();
} }
bool isAppLimited() const; bool isAppIdle() const;
private: private:
// Updates the congestion controller app limited state, after a change in the // Updates the congestion controller app-idle state, after a change in the
// number of streams. // number of streams.
// App limited state is set to true if there was at least one non-control // App-idle state is set to true if there was at least one non-control
// before the update and there are none after. It is set to false if instead // before the update and there are none after. It is set to false if instead
// there were no non-control streams before and there is at least one at the // there were no non-control streams before and there is at least one at the
// time of calling // time of calling
void updateAppLimitedState(); void updateAppIdleState();
QuicStreamState* FOLLY_NULLABLE QuicStreamState* FOLLY_NULLABLE
getOrCreateOpenedLocalStream(StreamId streamId); getOrCreateOpenedLocalStream(StreamId streamId);
@@ -666,8 +666,8 @@ class QuicStreamManager {
// Data structure to keep track of stream that have detected lost data // Data structure to keep track of stream that have detected lost data
std::vector<StreamId> lossStreams_; std::vector<StreamId> lossStreams_;
// Record whether or not we are app limited. // Record whether or not we are app-idle.
bool isAppLimited_{false}; bool isAppIdle_{false};
}; };
} // namespace quic } // namespace quic

View File

@@ -218,15 +218,21 @@ struct CongestionController {
* example, we probably don't want to pace a connection in Recovery. * example, we probably don't want to pace a connection in Recovery.
*/ */
virtual bool canBePaced() const = 0; virtual bool canBePaced() const = 0;
/**
* Notify congestion controller that the connection has become idle or active
* in the sense that there are active non-control streams.
* idle: true if the connection has become app-idle, false if the
* connection has become not app-idle.
* eventTime: the time point when the app-idle state changed.
*/
virtual void setAppIdle(bool idle, TimePoint eventTime) = 0;
/** /**
* Notify congestion controller that the connection has become app-limited or * Notify congestion controller that the connection has become app-limited or
* not app-limited. * not app-limited.
* *
* limited: true if the connection has become app-limited, false if the
* connection has become not app-limited.
* eventTime: the time point when the app-limited state changed.
*/ */
virtual void setAppLimited(bool limited, TimePoint eventTime) = 0; virtual void setAppLimited() = 0;
virtual CongestionControlType type() const = 0; virtual CongestionControlType type() const = 0;
/** /**
@@ -246,8 +252,7 @@ struct CongestionController {
/** /**
* Whether the congestion controller thinks it's currently in app-limited * Whether the congestion controller thinks it's currently in app-limited
* state. For some congestion controllers, e.g. BBR, the app-limited state may * state.
* not be the same as the app.
*/ */
virtual bool isAppLimited() const = 0; virtual bool isAppLimited() const = 0;
}; };

View File

@@ -37,7 +37,8 @@ class MockCongestionController : public CongestionController {
, ,
setMinimalPacingInterval, setMinimalPacingInterval,
void(std::chrono::microseconds)); void(std::chrono::microseconds));
GMOCK_METHOD2_(, , , setAppLimited, void(bool, TimePoint)); GMOCK_METHOD2_(, , , setAppIdle, void(bool, TimePoint));
MOCK_METHOD0(setAppLimited, void());
MOCK_CONST_METHOD0(isAppLimited, bool()); MOCK_CONST_METHOD0(isAppLimited, bool());
}; };
} // namespace test } // namespace test

View File

@@ -44,140 +44,140 @@ class QuicStreamManagerTest : public Test {
MockCongestionController* mockController; MockCongestionController* mockController;
}; };
TEST_F(QuicStreamManagerTest, TestAppLimitedCreateBidiStream) { TEST_F(QuicStreamManagerTest, TestAppIdleCreateBidiStream) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
// The app limiited state did not change. // The app limiited state did not change.
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0); EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
auto stream = manager.createNextBidirectionalStream(); auto stream = manager.createNextBidirectionalStream();
StreamId id = stream.value()->id; StreamId id = stream.value()->id;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
// Force transition to closed state // Force transition to closed state
stream.value()->send.state = StreamSendStates::Closed(); stream.value()->send.state = StreamSendStates::Closed();
stream.value()->recv.state = StreamReceiveStates::Closed(); stream.value()->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream.value()->id); manager.removeClosedStream(stream.value()->id);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
EXPECT_EQ(manager.getStream(id), nullptr); EXPECT_EQ(manager.getStream(id), nullptr);
} }
TEST_F(QuicStreamManagerTest, TestAppLimitedCreateUnidiStream) { TEST_F(QuicStreamManagerTest, TestAppIdleCreateUnidiStream) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0); EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
auto stream = manager.createNextUnidirectionalStream(); auto stream = manager.createNextUnidirectionalStream();
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
// Force transition to closed state // Force transition to closed state
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
stream.value()->send.state = StreamSendStates::Closed(); stream.value()->send.state = StreamSendStates::Closed();
stream.value()->recv.state = StreamReceiveStates::Closed(); stream.value()->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream.value()->id); manager.removeClosedStream(stream.value()->id);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
} }
TEST_F(QuicStreamManagerTest, TestAppLimitedExistingLocalStream) { TEST_F(QuicStreamManagerTest, TestAppIdleExistingLocalStream) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0); EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
auto stream = manager.createNextUnidirectionalStream(); auto stream = manager.createNextUnidirectionalStream();
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
manager.setStreamAsControl(*stream.value()); manager.setStreamAsControl(*stream.value());
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
manager.getStream(stream.value()->id); manager.getStream(stream.value()->id);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
} }
TEST_F(QuicStreamManagerTest, TestAppLimitedStreamAsControl) { TEST_F(QuicStreamManagerTest, TestAppIdleStreamAsControl) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
auto stream = manager.createNextUnidirectionalStream(); auto stream = manager.createNextUnidirectionalStream();
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
manager.setStreamAsControl(*stream.value()); manager.setStreamAsControl(*stream.value());
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(false, _)); EXPECT_CALL(*mockController, setAppIdle(false, _));
manager.createNextUnidirectionalStream(); manager.createNextUnidirectionalStream();
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
} }
TEST_F(QuicStreamManagerTest, TestAppLimitedCreatePeerStream) { TEST_F(QuicStreamManagerTest, TestAppIdleCreatePeerStream) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
StreamId id = 0; StreamId id = 0;
auto stream = manager.getStream(id); auto stream = manager.getStream(id);
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
manager.setStreamAsControl(*stream); manager.setStreamAsControl(*stream);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(false, _)); EXPECT_CALL(*mockController, setAppIdle(false, _));
StreamId id2 = 4; StreamId id2 = 4;
manager.getStream(id2); manager.getStream(id2);
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
} }
TEST_F(QuicStreamManagerTest, TestAppLimitedExistingPeerStream) { TEST_F(QuicStreamManagerTest, TestAppIdleExistingPeerStream) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0); EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
StreamId id = 0; StreamId id = 0;
auto stream = manager.getStream(id); auto stream = manager.getStream(id);
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
manager.setStreamAsControl(*stream); manager.setStreamAsControl(*stream);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
manager.getStream(id); manager.getStream(id);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
} }
TEST_F(QuicStreamManagerTest, TestAppLimitedClosePeerStream) { TEST_F(QuicStreamManagerTest, TestAppIdleClosePeerStream) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
StreamId id = 0; StreamId id = 0;
auto stream = manager.getStream(id); auto stream = manager.getStream(id);
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
// Force transition to closed state // Force transition to closed state
stream->send.state = StreamSendStates::Closed(); stream->send.state = StreamSendStates::Closed();
stream->recv.state = StreamReceiveStates::Closed(); stream->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream->id); manager.removeClosedStream(stream->id);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
EXPECT_EQ(manager.getStream(id), nullptr); EXPECT_EQ(manager.getStream(id), nullptr);
} }
TEST_F(QuicStreamManagerTest, TestAppLimitedCloseControlStream) { TEST_F(QuicStreamManagerTest, TestAppIdleCloseControlStream) {
auto& manager = *conn.streamManager; auto& manager = *conn.streamManager;
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0); EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
StreamId id = 0; StreamId id = 0;
auto stream = manager.getStream(id); auto stream = manager.getStream(id);
EXPECT_FALSE(manager.isAppLimited()); EXPECT_FALSE(manager.isAppIdle());
EXPECT_CALL(*mockController, setAppLimited(true, _)); EXPECT_CALL(*mockController, setAppIdle(true, _));
manager.setStreamAsControl(*stream); manager.setStreamAsControl(*stream);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
// Force transition to closed state // Force transition to closed state
stream->send.state = StreamSendStates::Closed(); stream->send.state = StreamSendStates::Closed();
stream->recv.state = StreamReceiveStates::Closed(); stream->recv.state = StreamReceiveStates::Closed();
manager.removeClosedStream(stream->id); manager.removeClosedStream(stream->id);
EXPECT_TRUE(manager.isAppLimited()); EXPECT_TRUE(manager.isAppIdle());
} }
} // namespace test } // namespace test
} // namespace quic } // namespace quic