mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-09 20:42:44 +03:00
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
199 lines
6.8 KiB
C++
199 lines
6.8 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
|
|
#include "SimpleFrameFunctions.h"
|
|
|
|
#include <boost/variant/get.hpp>
|
|
#include <quic/state/QuicStateFunctions.h>
|
|
#include <quic/state/QuicStreamFunctions.h>
|
|
|
|
namespace quic {
|
|
void sendSimpleFrame(QuicConnectionStateBase& conn, QuicSimpleFrame frame) {
|
|
conn.pendingEvents.frames.emplace_back(std::move(frame));
|
|
}
|
|
|
|
void updateSimpleFrameOnAck(
|
|
QuicConnectionStateBase& /*conn*/,
|
|
const QuicSimpleFrame& /*frame*/) {
|
|
// TODO implement.
|
|
}
|
|
|
|
folly::Optional<QuicSimpleFrame> updateSimpleFrameOnPacketClone(
|
|
QuicConnectionStateBase& conn,
|
|
const QuicSimpleFrame& frame) {
|
|
return folly::variant_match(
|
|
frame,
|
|
[&](const StopSendingFrame& frame) -> folly::Optional<QuicSimpleFrame> {
|
|
if (!conn.streamManager->streamExists(frame.streamId)) {
|
|
return folly::none;
|
|
}
|
|
return QuicSimpleFrame(frame);
|
|
},
|
|
[&](const MinStreamDataFrame& frame) -> folly::Optional<QuicSimpleFrame> {
|
|
if (!conn.streamManager->streamExists(frame.streamId)) {
|
|
return folly::none;
|
|
}
|
|
return QuicSimpleFrame(frame);
|
|
},
|
|
[&](const ExpiredStreamDataFrame& frame)
|
|
-> folly::Optional<QuicSimpleFrame> {
|
|
if (!conn.streamManager->streamExists(frame.streamId)) {
|
|
return folly::none;
|
|
}
|
|
return QuicSimpleFrame(frame);
|
|
},
|
|
[&](const PathChallengeFrame& frame) -> folly::Optional<QuicSimpleFrame> {
|
|
// Path validation timer expired, path validation failed;
|
|
// or a different path validation was scheduled
|
|
if (!conn.outstandingPathValidation ||
|
|
frame != *conn.outstandingPathValidation) {
|
|
return folly::none;
|
|
}
|
|
return QuicSimpleFrame(frame);
|
|
},
|
|
[&](const PathResponseFrame&) -> folly::Optional<QuicSimpleFrame> {
|
|
// Do not clone PATH_RESPONSE to avoid buffering
|
|
return folly::none;
|
|
},
|
|
[&](const NewConnectionIdFrame& frame)
|
|
-> folly::Optional<QuicSimpleFrame> {
|
|
return QuicSimpleFrame(frame);
|
|
},
|
|
[&](const MaxStreamsFrame& frame) -> folly::Optional<QuicSimpleFrame> {
|
|
return QuicSimpleFrame(frame);
|
|
});
|
|
}
|
|
|
|
void updateSimpleFrameOnPacketSent(
|
|
QuicConnectionStateBase& conn,
|
|
const QuicSimpleFrame& simpleFrame) {
|
|
folly::variant_match(
|
|
simpleFrame,
|
|
[&](const PathChallengeFrame&) {
|
|
conn.outstandingPathValidation =
|
|
std::move(conn.pendingEvents.pathChallenge);
|
|
conn.pendingEvents.schedulePathValidationTimeout = true;
|
|
},
|
|
[&](const QuicSimpleFrame& frame) {
|
|
auto& frames = conn.pendingEvents.frames;
|
|
auto itr =
|
|
find_if(frames.begin(), frames.end(), [&](QuicSimpleFrame& f) {
|
|
return folly::variant_match(frame, [&](auto& vFrame) {
|
|
auto fptr = boost::get<decltype(vFrame)>(&f);
|
|
return fptr != nullptr && *fptr == vFrame;
|
|
});
|
|
});
|
|
CHECK(itr != frames.end());
|
|
frames.erase(itr);
|
|
});
|
|
}
|
|
|
|
void updateSimpleFrameOnPacketLoss(
|
|
QuicConnectionStateBase& conn,
|
|
const QuicSimpleFrame& frameIn) {
|
|
folly::variant_match(
|
|
frameIn,
|
|
[&](const StopSendingFrame& frame) {
|
|
if (conn.streamManager->streamExists(frame.streamId)) {
|
|
conn.pendingEvents.frames.push_back(frame);
|
|
}
|
|
},
|
|
[&](const MinStreamDataFrame& frame) {
|
|
auto stream = conn.streamManager->getStream(frame.streamId);
|
|
if (stream && stream->conn.partialReliabilityEnabled) {
|
|
advanceCurrentReceiveOffset(stream, frame.minimumStreamOffset);
|
|
}
|
|
},
|
|
[&](const ExpiredStreamDataFrame& frame) {
|
|
auto stream = conn.streamManager->getStream(frame.streamId);
|
|
if (stream && stream->conn.partialReliabilityEnabled) {
|
|
advanceMinimumRetransmittableOffset(
|
|
stream, frame.minimumStreamOffset);
|
|
}
|
|
},
|
|
[&](const PathChallengeFrame& frame) {
|
|
if (conn.outstandingPathValidation &&
|
|
frame == *conn.outstandingPathValidation) {
|
|
conn.pendingEvents.pathChallenge = frame;
|
|
}
|
|
},
|
|
[&](const PathResponseFrame&) {
|
|
// Do not retransmit PATH_RESPONSE to avoid buffering
|
|
},
|
|
[&](const NewConnectionIdFrame& frame) {
|
|
conn.pendingEvents.frames.push_back(frame);
|
|
},
|
|
[&](const MaxStreamsFrame& frame) {
|
|
conn.pendingEvents.frames.push_back(frame);
|
|
});
|
|
}
|
|
|
|
bool updateSimpleFrameOnPacketReceived(
|
|
QuicConnectionStateBase& conn,
|
|
const QuicSimpleFrame& frameIn,
|
|
PacketNum packetNum,
|
|
bool fromChangedPeerAddress) {
|
|
return folly::variant_match(
|
|
frameIn,
|
|
[&](const StopSendingFrame& frame) {
|
|
auto stream = conn.streamManager->getStream(frame.streamId);
|
|
if (stream) {
|
|
invokeStreamSendStateMachine(conn, *stream, frame);
|
|
}
|
|
return true;
|
|
},
|
|
[&](const MinStreamDataFrame& frame) {
|
|
auto stream = conn.streamManager->getStream(frame.streamId);
|
|
if (stream && stream->conn.partialReliabilityEnabled) {
|
|
onRecvMinStreamDataFrame(stream, frame, packetNum);
|
|
}
|
|
return true;
|
|
},
|
|
[&](const ExpiredStreamDataFrame& frame) {
|
|
auto stream = conn.streamManager->getStream(frame.streamId);
|
|
if (stream && stream->conn.partialReliabilityEnabled) {
|
|
onRecvExpiredStreamDataFrame(stream, frame);
|
|
}
|
|
return true;
|
|
},
|
|
[&](const PathChallengeFrame& frame) {
|
|
conn.pendingEvents.frames.emplace_back(
|
|
PathResponseFrame(frame.pathData));
|
|
return false;
|
|
},
|
|
[&](const PathResponseFrame& frame) {
|
|
// Ignore the response if outstandingPathValidation is none or
|
|
// the path data doesn't match what's in outstandingPathValidation
|
|
if (fromChangedPeerAddress || !conn.outstandingPathValidation ||
|
|
frame.pathData != conn.outstandingPathValidation->pathData) {
|
|
return false;
|
|
}
|
|
// TODO update source token,
|
|
conn.outstandingPathValidation = folly::none;
|
|
conn.pendingEvents.schedulePathValidationTimeout = false;
|
|
conn.writableBytesLimit = folly::none;
|
|
return false;
|
|
},
|
|
[&](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;
|
|
});
|
|
}
|
|
|
|
} // namespace quic
|