mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-08 09:42:06 +03:00
Move core functionality to QuicTransportBaseLite [8/n]
Summary: See title. Reviewed By: mjoras Differential Revision: D64065153 fbshipit-source-id: 5c9515dcaba1ef1f30d49f701e366f715854527a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c62dac180e
commit
b7169e3cf7
@@ -53,17 +53,8 @@ QuicTransportBase::QuicTransportBase(
|
|||||||
useConnectionEndWithErrorCallback),
|
useConnectionEndWithErrorCallback),
|
||||||
ackTimeout_(this),
|
ackTimeout_(this),
|
||||||
pathValidationTimeout_(this),
|
pathValidationTimeout_(this),
|
||||||
keepaliveTimeout_(this),
|
|
||||||
drainTimeout_(this),
|
drainTimeout_(this),
|
||||||
pingTimeout_(this),
|
pingTimeout_(this) {
|
||||||
readLooper_(new FunctionLooper(
|
|
||||||
evb_,
|
|
||||||
[this]() { invokeReadDataAndCallbacks(); },
|
|
||||||
LooperType::ReadLooper)),
|
|
||||||
peekLooper_(new FunctionLooper(
|
|
||||||
evb_,
|
|
||||||
[this]() { invokePeekDataAndCallbacks(); },
|
|
||||||
LooperType::PeekLooper)) {
|
|
||||||
writeLooper_->setPacingFunction([this]() -> auto {
|
writeLooper_->setPacingFunction([this]() -> auto {
|
||||||
if (isConnectionPaced(*conn_)) {
|
if (isConnectionPaced(*conn_)) {
|
||||||
return conn_->pacer->getTimeUntilNextWrite();
|
return conn_->pacer->getTimeUntilNextWrite();
|
||||||
@@ -734,34 +725,6 @@ QuicTransportBase::pauseOrResumeRead(StreamId id, bool resume) {
|
|||||||
return folly::unit;
|
return folly::unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuicTransportBase::updateReadLooper() {
|
|
||||||
if (closeState_ != CloseState::OPEN) {
|
|
||||||
VLOG(10) << "Stopping read looper " << *this;
|
|
||||||
readLooper_->stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto iter = std::find_if(
|
|
||||||
conn_->streamManager->readableStreams().begin(),
|
|
||||||
conn_->streamManager->readableStreams().end(),
|
|
||||||
[&readCallbacks = readCallbacks_](StreamId s) {
|
|
||||||
auto readCb = readCallbacks.find(s);
|
|
||||||
if (readCb == readCallbacks.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// TODO: if the stream has an error and it is also paused we should
|
|
||||||
// still return an error
|
|
||||||
return readCb->second.readCb && readCb->second.resumed;
|
|
||||||
});
|
|
||||||
if (iter != conn_->streamManager->readableStreams().end() ||
|
|
||||||
!conn_->datagramState.readBuffer.empty()) {
|
|
||||||
VLOG(10) << "Scheduling read looper " << *this;
|
|
||||||
readLooper_->run();
|
|
||||||
} else {
|
|
||||||
VLOG(10) << "Stopping read looper " << *this;
|
|
||||||
readLooper_->stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
folly::Expected<folly::Unit, LocalErrorCode> QuicTransportBase::setPeekCallback(
|
folly::Expected<folly::Unit, LocalErrorCode> QuicTransportBase::setPeekCallback(
|
||||||
StreamId id,
|
StreamId id,
|
||||||
PeekCallback* cb) {
|
PeekCallback* cb) {
|
||||||
@@ -848,89 +811,6 @@ void QuicTransportBase::invokeStreamsAvailableCallbacks() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuicTransportBase::updatePeekLooper() {
|
|
||||||
if (peekCallbacks_.empty() || closeState_ != CloseState::OPEN) {
|
|
||||||
VLOG(10) << "Stopping peek looper " << *this;
|
|
||||||
peekLooper_->stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VLOG(10) << "Updating peek looper, has "
|
|
||||||
<< conn_->streamManager->peekableStreams().size()
|
|
||||||
<< " peekable streams";
|
|
||||||
auto iter = std::find_if(
|
|
||||||
conn_->streamManager->peekableStreams().begin(),
|
|
||||||
conn_->streamManager->peekableStreams().end(),
|
|
||||||
[&peekCallbacks = peekCallbacks_](StreamId s) {
|
|
||||||
VLOG(10) << "Checking stream=" << s;
|
|
||||||
auto peekCb = peekCallbacks.find(s);
|
|
||||||
if (peekCb == peekCallbacks.end()) {
|
|
||||||
VLOG(10) << "No peek callbacks for stream=" << s;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!peekCb->second.resumed) {
|
|
||||||
VLOG(10) << "peek callback for stream=" << s << " not resumed";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!peekCb->second.peekCb) {
|
|
||||||
VLOG(10) << "no peekCb in peekCb stream=" << s;
|
|
||||||
}
|
|
||||||
return peekCb->second.peekCb && peekCb->second.resumed;
|
|
||||||
});
|
|
||||||
if (iter != conn_->streamManager->peekableStreams().end()) {
|
|
||||||
VLOG(10) << "Scheduling peek looper " << *this;
|
|
||||||
peekLooper_->run();
|
|
||||||
} else {
|
|
||||||
VLOG(10) << "Stopping peek looper " << *this;
|
|
||||||
peekLooper_->stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuicTransportBase::updateWriteLooper(bool thisIteration, bool runInline) {
|
|
||||||
if (closeState_ == CloseState::CLOSED) {
|
|
||||||
VLOG(10) << nodeToString(conn_->nodeType)
|
|
||||||
<< " stopping write looper because conn closed " << *this;
|
|
||||||
writeLooper_->stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn_->transportSettings.checkIdleTimerOnWrite) {
|
|
||||||
checkIdleTimer(Clock::now());
|
|
||||||
if (closeState_ == CloseState::CLOSED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If socket writable events are in use, do nothing if we are already waiting
|
|
||||||
// for the write event.
|
|
||||||
if (conn_->transportSettings.useSockWritableEvents &&
|
|
||||||
socket_->isWritableCallbackSet()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto writeDataReason = shouldWriteData(*conn_);
|
|
||||||
if (writeDataReason != WriteDataReason::NO_WRITE) {
|
|
||||||
VLOG(10) << nodeToString(conn_->nodeType)
|
|
||||||
<< " running write looper thisIteration=" << thisIteration << " "
|
|
||||||
<< *this;
|
|
||||||
writeLooper_->run(thisIteration, runInline);
|
|
||||||
if (conn_->loopDetectorCallback) {
|
|
||||||
conn_->writeDebugState.needsWriteLoopDetect =
|
|
||||||
(conn_->loopDetectorCallback != nullptr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
VLOG(10) << nodeToString(conn_->nodeType) << " stopping write looper "
|
|
||||||
<< *this;
|
|
||||||
writeLooper_->stop();
|
|
||||||
if (conn_->loopDetectorCallback) {
|
|
||||||
conn_->writeDebugState.needsWriteLoopDetect = false;
|
|
||||||
conn_->writeDebugState.currentEmptyLoopCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (conn_->loopDetectorCallback) {
|
|
||||||
conn_->writeDebugState.writeDataReason = writeDataReason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuicTransportBase::cancelDeliveryCallbacksForStream(StreamId id) {
|
void QuicTransportBase::cancelDeliveryCallbacksForStream(StreamId id) {
|
||||||
cancelByteEventCallbacksForStream(ByteEvent::Type::ACK, id);
|
cancelByteEventCallbacksForStream(ByteEvent::Type::ACK, id);
|
||||||
}
|
}
|
||||||
@@ -1235,62 +1115,6 @@ void QuicTransportBase::handlePingCallbacks() {
|
|||||||
conn_->pendingEvents.cancelPingTimeout = false;
|
conn_->pendingEvents.cancelPingTimeout = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuicTransportBase::processCallbacksAfterWriteData() {
|
|
||||||
if (closeState_ != CloseState::OPEN) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto txStreamId = conn_->streamManager->popTx();
|
|
||||||
while (txStreamId.has_value()) {
|
|
||||||
auto streamId = *txStreamId;
|
|
||||||
auto stream = CHECK_NOTNULL(conn_->streamManager->getStream(streamId));
|
|
||||||
auto largestOffsetTxed = getLargestWriteOffsetTxed(*stream);
|
|
||||||
// if it's in the set of streams with TX, we should have a valid offset
|
|
||||||
CHECK(largestOffsetTxed.has_value());
|
|
||||||
|
|
||||||
// lambda to help get the next callback to call for this stream
|
|
||||||
auto getNextTxCallbackForStreamAndCleanup =
|
|
||||||
[this, &largestOffsetTxed](
|
|
||||||
const auto& streamId) -> Optional<ByteEventDetail> {
|
|
||||||
auto txCallbacksForStreamIt = txCallbacks_.find(streamId);
|
|
||||||
if (txCallbacksForStreamIt == txCallbacks_.end() ||
|
|
||||||
txCallbacksForStreamIt->second.empty()) {
|
|
||||||
return none;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& txCallbacksForStream = txCallbacksForStreamIt->second;
|
|
||||||
if (txCallbacksForStream.front().offset > *largestOffsetTxed) {
|
|
||||||
return none;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract the callback, pop from the queue, then check for cleanup
|
|
||||||
auto result = txCallbacksForStream.front();
|
|
||||||
txCallbacksForStream.pop_front();
|
|
||||||
if (txCallbacksForStream.empty()) {
|
|
||||||
txCallbacks_.erase(txCallbacksForStreamIt);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
Optional<ByteEventDetail> nextOffsetAndCallback;
|
|
||||||
while (
|
|
||||||
(nextOffsetAndCallback =
|
|
||||||
getNextTxCallbackForStreamAndCleanup(streamId))) {
|
|
||||||
ByteEvent byteEvent{
|
|
||||||
streamId, nextOffsetAndCallback->offset, ByteEvent::Type::TX};
|
|
||||||
nextOffsetAndCallback->callback->onByteEvent(byteEvent);
|
|
||||||
|
|
||||||
// connection may be closed by callback
|
|
||||||
if (closeState_ != CloseState::OPEN) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pop the next stream
|
|
||||||
txStreamId = conn_->streamManager->popTx();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuicTransportBase::handleKnobCallbacks() {
|
void QuicTransportBase::handleKnobCallbacks() {
|
||||||
if (!conn_->transportSettings.advertisedKnobFrameSupport) {
|
if (!conn_->transportSettings.advertisedKnobFrameSupport) {
|
||||||
VLOG(4) << "Received knob frames without advertising support";
|
VLOG(4) << "Received knob frames without advertising support";
|
||||||
@@ -1770,33 +1594,6 @@ void QuicTransportBase::onNetworkData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuicTransportBase::setIdleTimer() {
|
|
||||||
if (closeState_ == CloseState::CLOSED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cancelTimeout(&idleTimeout_);
|
|
||||||
cancelTimeout(&keepaliveTimeout_);
|
|
||||||
auto localIdleTimeout = conn_->transportSettings.idleTimeout;
|
|
||||||
// The local idle timeout being zero means it is disabled.
|
|
||||||
if (localIdleTimeout == 0ms) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto peerIdleTimeout =
|
|
||||||
conn_->peerIdleTimeout > 0ms ? conn_->peerIdleTimeout : localIdleTimeout;
|
|
||||||
auto idleTimeout = timeMin(localIdleTimeout, peerIdleTimeout);
|
|
||||||
|
|
||||||
idleTimeoutCheck_.idleTimeoutMs = idleTimeout;
|
|
||||||
idleTimeoutCheck_.lastTimeIdleTimeoutScheduled_ = Clock::now();
|
|
||||||
|
|
||||||
scheduleTimeout(&idleTimeout_, idleTimeout);
|
|
||||||
auto idleTimeoutCount = idleTimeout.count();
|
|
||||||
if (conn_->transportSettings.enableKeepalive) {
|
|
||||||
std::chrono::milliseconds keepaliveTimeout = std::chrono::milliseconds(
|
|
||||||
idleTimeoutCount - static_cast<int64_t>(idleTimeoutCount * .15));
|
|
||||||
scheduleTimeout(&keepaliveTimeout_, keepaliveTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t QuicTransportBase::getNumOpenableBidirectionalStreams() const {
|
uint64_t QuicTransportBase::getNumOpenableBidirectionalStreams() const {
|
||||||
return conn_->streamManager->openableLocalBidirectionalStreams();
|
return conn_->streamManager->openableLocalBidirectionalStreams();
|
||||||
}
|
}
|
||||||
@@ -2198,12 +1995,6 @@ void QuicTransportBase::pathValidationTimeoutExpired() noexcept {
|
|||||||
std::string("Path validation timed out")));
|
std::string("Path validation timed out")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuicTransportBase::keepaliveTimeoutExpired() noexcept {
|
|
||||||
[[maybe_unused]] auto self = sharedGuard();
|
|
||||||
conn_->pendingEvents.sendPing = true;
|
|
||||||
updateWriteLooper(true, conn_->transportSettings.inlineWriteAfterRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuicTransportBase::scheduleAckTimeout() {
|
void QuicTransportBase::scheduleAckTimeout() {
|
||||||
if (closeState_ == CloseState::CLOSED) {
|
if (closeState_ == CloseState::CLOSED) {
|
||||||
return;
|
return;
|
||||||
@@ -2272,10 +2063,6 @@ void QuicTransportBase::schedulePathValidationTimeout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuicTransportBase::isLossTimeoutScheduled() {
|
|
||||||
return isTimeoutScheduled(&lossTimeout_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuicTransportBase::setSupportedVersions(
|
void QuicTransportBase::setSupportedVersions(
|
||||||
const std::vector<QuicVersion>& versions) {
|
const std::vector<QuicVersion>& versions) {
|
||||||
conn_->originalVersion = versions.at(0);
|
conn_->originalVersion = versions.at(0);
|
||||||
|
@@ -433,24 +433,6 @@ class QuicTransportBase : public QuicSocket,
|
|||||||
QuicTransportBase* transport_;
|
QuicTransportBase* transport_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeepaliveTimeout : public QuicTimerCallback {
|
|
||||||
public:
|
|
||||||
~KeepaliveTimeout() override = default;
|
|
||||||
|
|
||||||
explicit KeepaliveTimeout(QuicTransportBase* transport)
|
|
||||||
: transport_(transport) {}
|
|
||||||
|
|
||||||
void timeoutExpired() noexcept override {
|
|
||||||
transport_->keepaliveTimeoutExpired();
|
|
||||||
}
|
|
||||||
void callbackCanceled() noexcept override {
|
|
||||||
// Specifically do nothing since if we got canceled we shouldn't write.
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QuicTransportBase* transport_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// DrainTimeout is a bit different from other timeouts. It needs to hold a
|
// DrainTimeout is a bit different from other timeouts. It needs to hold a
|
||||||
// shared_ptr to the transport, since if a DrainTimeout is scheduled,
|
// shared_ptr to the transport, since if a DrainTimeout is scheduled,
|
||||||
// transport cannot die.
|
// transport cannot die.
|
||||||
@@ -469,8 +451,6 @@ class QuicTransportBase : public QuicSocket,
|
|||||||
QuicTransportBase* transport_;
|
QuicTransportBase* transport_;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isLossTimeoutScheduled() override; // TODO: make this const again
|
|
||||||
|
|
||||||
// If you don't set it, the default is Cubic
|
// If you don't set it, the default is Cubic
|
||||||
void setCongestionControl(CongestionControlType type) override;
|
void setCongestionControl(CongestionControlType type) override;
|
||||||
|
|
||||||
@@ -552,12 +532,8 @@ class QuicTransportBase : public QuicSocket,
|
|||||||
void updateCongestionControlSettings(
|
void updateCongestionControlSettings(
|
||||||
const TransportSettings& transportSettings);
|
const TransportSettings& transportSettings);
|
||||||
void updateSocketTosSettings(uint8_t dscpValue);
|
void updateSocketTosSettings(uint8_t dscpValue);
|
||||||
void processCallbacksAfterWriteData() override;
|
|
||||||
void processCallbacksAfterNetworkData();
|
void processCallbacksAfterNetworkData();
|
||||||
void invokeStreamsAvailableCallbacks();
|
void invokeStreamsAvailableCallbacks();
|
||||||
void updateReadLooper() override;
|
|
||||||
void updatePeekLooper() override;
|
|
||||||
void updateWriteLooper(bool thisIteration, bool runInline = false) override;
|
|
||||||
void handlePingCallbacks();
|
void handlePingCallbacks();
|
||||||
void handleKnobCallbacks();
|
void handleKnobCallbacks();
|
||||||
void handleAckEventCallbacks();
|
void handleAckEventCallbacks();
|
||||||
@@ -604,11 +580,9 @@ class QuicTransportBase : public QuicSocket,
|
|||||||
|
|
||||||
void ackTimeoutExpired() noexcept;
|
void ackTimeoutExpired() noexcept;
|
||||||
void pathValidationTimeoutExpired() noexcept;
|
void pathValidationTimeoutExpired() noexcept;
|
||||||
void keepaliveTimeoutExpired() noexcept;
|
|
||||||
void drainTimeoutExpired() noexcept;
|
void drainTimeoutExpired() noexcept;
|
||||||
void pingTimeoutExpired() noexcept;
|
void pingTimeoutExpired() noexcept;
|
||||||
|
|
||||||
void setIdleTimer() override;
|
|
||||||
void scheduleAckTimeout() override;
|
void scheduleAckTimeout() override;
|
||||||
void schedulePathValidationTimeout() override;
|
void schedulePathValidationTimeout() override;
|
||||||
void schedulePingTimeout(
|
void schedulePingTimeout(
|
||||||
@@ -662,11 +636,8 @@ class QuicTransportBase : public QuicSocket,
|
|||||||
|
|
||||||
AckTimeout ackTimeout_;
|
AckTimeout ackTimeout_;
|
||||||
PathValidationTimeout pathValidationTimeout_;
|
PathValidationTimeout pathValidationTimeout_;
|
||||||
KeepaliveTimeout keepaliveTimeout_;
|
|
||||||
DrainTimeout drainTimeout_;
|
DrainTimeout drainTimeout_;
|
||||||
PingTimeout pingTimeout_;
|
PingTimeout pingTimeout_;
|
||||||
FunctionLooper::Ptr readLooper_;
|
|
||||||
FunctionLooper::Ptr peekLooper_;
|
|
||||||
|
|
||||||
// TODO: This is silly. We need a better solution.
|
// TODO: This is silly. We need a better solution.
|
||||||
// Uninitialied local address as a fallback answer when socket isn't bound.
|
// Uninitialied local address as a fallback answer when socket isn't bound.
|
||||||
|
@@ -417,6 +417,119 @@ void QuicTransportBaseLite::runOnEvbAsync(
|
|||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuicTransportBaseLite::updateWriteLooper(
|
||||||
|
bool thisIteration,
|
||||||
|
bool runInline) {
|
||||||
|
if (closeState_ == CloseState::CLOSED) {
|
||||||
|
VLOG(10) << nodeToString(conn_->nodeType)
|
||||||
|
<< " stopping write looper because conn closed " << *this;
|
||||||
|
writeLooper_->stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn_->transportSettings.checkIdleTimerOnWrite) {
|
||||||
|
checkIdleTimer(Clock::now());
|
||||||
|
if (closeState_ == CloseState::CLOSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If socket writable events are in use, do nothing if we are already waiting
|
||||||
|
// for the write event.
|
||||||
|
if (conn_->transportSettings.useSockWritableEvents &&
|
||||||
|
socket_->isWritableCallbackSet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto writeDataReason = shouldWriteData(*conn_);
|
||||||
|
if (writeDataReason != WriteDataReason::NO_WRITE) {
|
||||||
|
VLOG(10) << nodeToString(conn_->nodeType)
|
||||||
|
<< " running write looper thisIteration=" << thisIteration << " "
|
||||||
|
<< *this;
|
||||||
|
writeLooper_->run(thisIteration, runInline);
|
||||||
|
if (conn_->loopDetectorCallback) {
|
||||||
|
conn_->writeDebugState.needsWriteLoopDetect =
|
||||||
|
(conn_->loopDetectorCallback != nullptr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VLOG(10) << nodeToString(conn_->nodeType) << " stopping write looper "
|
||||||
|
<< *this;
|
||||||
|
writeLooper_->stop();
|
||||||
|
if (conn_->loopDetectorCallback) {
|
||||||
|
conn_->writeDebugState.needsWriteLoopDetect = false;
|
||||||
|
conn_->writeDebugState.currentEmptyLoopCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conn_->loopDetectorCallback) {
|
||||||
|
conn_->writeDebugState.writeDataReason = writeDataReason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuicTransportBaseLite::updateReadLooper() {
|
||||||
|
if (closeState_ != CloseState::OPEN) {
|
||||||
|
VLOG(10) << "Stopping read looper " << *this;
|
||||||
|
readLooper_->stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto iter = std::find_if(
|
||||||
|
conn_->streamManager->readableStreams().begin(),
|
||||||
|
conn_->streamManager->readableStreams().end(),
|
||||||
|
[&readCallbacks = readCallbacks_](StreamId s) {
|
||||||
|
auto readCb = readCallbacks.find(s);
|
||||||
|
if (readCb == readCallbacks.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: if the stream has an error and it is also paused we should
|
||||||
|
// still return an error
|
||||||
|
return readCb->second.readCb && readCb->second.resumed;
|
||||||
|
});
|
||||||
|
if (iter != conn_->streamManager->readableStreams().end() ||
|
||||||
|
!conn_->datagramState.readBuffer.empty()) {
|
||||||
|
VLOG(10) << "Scheduling read looper " << *this;
|
||||||
|
readLooper_->run();
|
||||||
|
} else {
|
||||||
|
VLOG(10) << "Stopping read looper " << *this;
|
||||||
|
readLooper_->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuicTransportBaseLite::updatePeekLooper() {
|
||||||
|
if (peekCallbacks_.empty() || closeState_ != CloseState::OPEN) {
|
||||||
|
VLOG(10) << "Stopping peek looper " << *this;
|
||||||
|
peekLooper_->stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VLOG(10) << "Updating peek looper, has "
|
||||||
|
<< conn_->streamManager->peekableStreams().size()
|
||||||
|
<< " peekable streams";
|
||||||
|
auto iter = std::find_if(
|
||||||
|
conn_->streamManager->peekableStreams().begin(),
|
||||||
|
conn_->streamManager->peekableStreams().end(),
|
||||||
|
[&peekCallbacks = peekCallbacks_](StreamId s) {
|
||||||
|
VLOG(10) << "Checking stream=" << s;
|
||||||
|
auto peekCb = peekCallbacks.find(s);
|
||||||
|
if (peekCb == peekCallbacks.end()) {
|
||||||
|
VLOG(10) << "No peek callbacks for stream=" << s;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!peekCb->second.resumed) {
|
||||||
|
VLOG(10) << "peek callback for stream=" << s << " not resumed";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!peekCb->second.peekCb) {
|
||||||
|
VLOG(10) << "no peekCb in peekCb stream=" << s;
|
||||||
|
}
|
||||||
|
return peekCb->second.peekCb && peekCb->second.resumed;
|
||||||
|
});
|
||||||
|
if (iter != conn_->streamManager->peekableStreams().end()) {
|
||||||
|
VLOG(10) << "Scheduling peek looper " << *this;
|
||||||
|
peekLooper_->run();
|
||||||
|
} else {
|
||||||
|
VLOG(10) << "Stopping peek looper " << *this;
|
||||||
|
peekLooper_->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QuicTransportBaseLite::maybeStopWriteLooperAndArmSocketWritableEvent() {
|
void QuicTransportBaseLite::maybeStopWriteLooperAndArmSocketWritableEvent() {
|
||||||
if (!socket_ || (closeState_ == CloseState::CLOSED)) {
|
if (!socket_ || (closeState_ == CloseState::CLOSED)) {
|
||||||
return;
|
return;
|
||||||
@@ -682,6 +795,12 @@ void QuicTransportBaseLite::idleTimeoutExpired(bool drain) noexcept {
|
|||||||
!drain /* sendCloseImmediately */);
|
!drain /* sendCloseImmediately */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuicTransportBaseLite::keepaliveTimeoutExpired() noexcept {
|
||||||
|
[[maybe_unused]] auto self = sharedGuard();
|
||||||
|
conn_->pendingEvents.sendPing = true;
|
||||||
|
updateWriteLooper(true, conn_->transportSettings.inlineWriteAfterRead);
|
||||||
|
}
|
||||||
|
|
||||||
bool QuicTransportBaseLite::processCancelCode(const QuicError& cancelCode) {
|
bool QuicTransportBaseLite::processCancelCode(const QuicError& cancelCode) {
|
||||||
bool noError = false;
|
bool noError = false;
|
||||||
switch (cancelCode.code.type()) {
|
switch (cancelCode.code.type()) {
|
||||||
@@ -740,6 +859,10 @@ void QuicTransportBaseLite::cancelLossTimeout() {
|
|||||||
cancelTimeout(&lossTimeout_);
|
cancelTimeout(&lossTimeout_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QuicTransportBaseLite::isLossTimeoutScheduled() {
|
||||||
|
return isTimeoutScheduled(&lossTimeout_);
|
||||||
|
}
|
||||||
|
|
||||||
bool QuicTransportBaseLite::isTimeoutScheduled(
|
bool QuicTransportBaseLite::isTimeoutScheduled(
|
||||||
QuicTimerCallback* callback) const {
|
QuicTimerCallback* callback) const {
|
||||||
return callback->isTimerCallbackScheduled();
|
return callback->isTimerCallbackScheduled();
|
||||||
@@ -857,6 +980,89 @@ void QuicTransportBaseLite::invokePeekDataAndCallbacks() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuicTransportBaseLite::processCallbacksAfterWriteData() {
|
||||||
|
if (closeState_ != CloseState::OPEN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto txStreamId = conn_->streamManager->popTx();
|
||||||
|
while (txStreamId.has_value()) {
|
||||||
|
auto streamId = *txStreamId;
|
||||||
|
auto stream = CHECK_NOTNULL(conn_->streamManager->getStream(streamId));
|
||||||
|
auto largestOffsetTxed = getLargestWriteOffsetTxed(*stream);
|
||||||
|
// if it's in the set of streams with TX, we should have a valid offset
|
||||||
|
CHECK(largestOffsetTxed.has_value());
|
||||||
|
|
||||||
|
// lambda to help get the next callback to call for this stream
|
||||||
|
auto getNextTxCallbackForStreamAndCleanup =
|
||||||
|
[this, &largestOffsetTxed](
|
||||||
|
const auto& streamId) -> Optional<ByteEventDetail> {
|
||||||
|
auto txCallbacksForStreamIt = txCallbacks_.find(streamId);
|
||||||
|
if (txCallbacksForStreamIt == txCallbacks_.end() ||
|
||||||
|
txCallbacksForStreamIt->second.empty()) {
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& txCallbacksForStream = txCallbacksForStreamIt->second;
|
||||||
|
if (txCallbacksForStream.front().offset > *largestOffsetTxed) {
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract the callback, pop from the queue, then check for cleanup
|
||||||
|
auto result = txCallbacksForStream.front();
|
||||||
|
txCallbacksForStream.pop_front();
|
||||||
|
if (txCallbacksForStream.empty()) {
|
||||||
|
txCallbacks_.erase(txCallbacksForStreamIt);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
Optional<ByteEventDetail> nextOffsetAndCallback;
|
||||||
|
while (
|
||||||
|
(nextOffsetAndCallback =
|
||||||
|
getNextTxCallbackForStreamAndCleanup(streamId))) {
|
||||||
|
ByteEvent byteEvent{
|
||||||
|
streamId, nextOffsetAndCallback->offset, ByteEvent::Type::TX};
|
||||||
|
nextOffsetAndCallback->callback->onByteEvent(byteEvent);
|
||||||
|
|
||||||
|
// connection may be closed by callback
|
||||||
|
if (closeState_ != CloseState::OPEN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop the next stream
|
||||||
|
txStreamId = conn_->streamManager->popTx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuicTransportBaseLite::setIdleTimer() {
|
||||||
|
if (closeState_ == CloseState::CLOSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cancelTimeout(&idleTimeout_);
|
||||||
|
cancelTimeout(&keepaliveTimeout_);
|
||||||
|
auto localIdleTimeout = conn_->transportSettings.idleTimeout;
|
||||||
|
// The local idle timeout being zero means it is disabled.
|
||||||
|
if (localIdleTimeout == 0ms) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto peerIdleTimeout =
|
||||||
|
conn_->peerIdleTimeout > 0ms ? conn_->peerIdleTimeout : localIdleTimeout;
|
||||||
|
auto idleTimeout = timeMin(localIdleTimeout, peerIdleTimeout);
|
||||||
|
|
||||||
|
idleTimeoutCheck_.idleTimeoutMs = idleTimeout;
|
||||||
|
idleTimeoutCheck_.lastTimeIdleTimeoutScheduled_ = Clock::now();
|
||||||
|
|
||||||
|
scheduleTimeout(&idleTimeout_, idleTimeout);
|
||||||
|
auto idleTimeoutCount = idleTimeout.count();
|
||||||
|
if (conn_->transportSettings.enableKeepalive) {
|
||||||
|
std::chrono::milliseconds keepaliveTimeout = std::chrono::milliseconds(
|
||||||
|
idleTimeoutCount - static_cast<int64_t>(idleTimeoutCount * .15));
|
||||||
|
scheduleTimeout(&keepaliveTimeout_, keepaliveTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QuicTransportBaseLite::updatePacketProcessorsPrewriteRequests() {
|
void QuicTransportBaseLite::updatePacketProcessorsPrewriteRequests() {
|
||||||
folly::SocketCmsgMap cmsgs;
|
folly::SocketCmsgMap cmsgs;
|
||||||
for (const auto& pp : conn_->packetProcessors) {
|
for (const auto& pp : conn_->packetProcessors) {
|
||||||
|
@@ -27,10 +27,19 @@ class QuicTransportBaseLite : virtual public QuicSocketLite,
|
|||||||
lossTimeout_(this),
|
lossTimeout_(this),
|
||||||
excessWriteTimeout_(this),
|
excessWriteTimeout_(this),
|
||||||
idleTimeout_(this),
|
idleTimeout_(this),
|
||||||
|
keepaliveTimeout_(this),
|
||||||
writeLooper_(new FunctionLooper(
|
writeLooper_(new FunctionLooper(
|
||||||
evb_,
|
evb_,
|
||||||
[this]() { pacedWriteDataToSocket(); },
|
[this]() { pacedWriteDataToSocket(); },
|
||||||
LooperType::WriteLooper)) {}
|
LooperType::WriteLooper)),
|
||||||
|
readLooper_(new FunctionLooper(
|
||||||
|
evb_,
|
||||||
|
[this]() { invokeReadDataAndCallbacks(); },
|
||||||
|
LooperType::ReadLooper)),
|
||||||
|
peekLooper_(new FunctionLooper(
|
||||||
|
evb_,
|
||||||
|
[this]() { invokePeekDataAndCallbacks(); },
|
||||||
|
LooperType::PeekLooper)) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when we have to write some data to the wire.
|
* Invoked when we have to write some data to the wire.
|
||||||
@@ -172,15 +181,29 @@ class QuicTransportBaseLite : virtual public QuicSocketLite,
|
|||||||
QuicTransportBaseLite* transport_;
|
QuicTransportBaseLite* transport_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class KeepaliveTimeout : public QuicTimerCallback {
|
||||||
|
public:
|
||||||
|
~KeepaliveTimeout() override = default;
|
||||||
|
|
||||||
|
explicit KeepaliveTimeout(QuicTransportBaseLite* transport)
|
||||||
|
: transport_(transport) {}
|
||||||
|
|
||||||
|
void timeoutExpired() noexcept override {
|
||||||
|
transport_->keepaliveTimeoutExpired();
|
||||||
|
}
|
||||||
|
void callbackCanceled() noexcept override {
|
||||||
|
// Specifically do nothing since if we got canceled we shouldn't write.
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QuicTransportBaseLite* transport_;
|
||||||
|
};
|
||||||
|
|
||||||
void scheduleLossTimeout(std::chrono::milliseconds timeout);
|
void scheduleLossTimeout(std::chrono::milliseconds timeout);
|
||||||
|
|
||||||
void cancelLossTimeout();
|
void cancelLossTimeout();
|
||||||
|
|
||||||
virtual bool isLossTimeoutScheduled() {
|
bool isLossTimeoutScheduled();
|
||||||
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
|
||||||
// qualifier
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a shared_ptr which can be used as a guard to keep this
|
* Returns a shared_ptr which can be used as a guard to keep this
|
||||||
@@ -231,22 +254,9 @@ class QuicTransportBaseLite : virtual public QuicSocketLite,
|
|||||||
void runOnEvbAsync(
|
void runOnEvbAsync(
|
||||||
folly::Function<void(std::shared_ptr<QuicTransportBaseLite>)> func);
|
folly::Function<void(std::shared_ptr<QuicTransportBaseLite>)> func);
|
||||||
|
|
||||||
virtual void updateWriteLooper(
|
void updateWriteLooper(bool thisIteration, bool runInline = false);
|
||||||
bool /* thisIteration */,
|
void updateReadLooper();
|
||||||
bool /* runInline */ = false) {
|
void updatePeekLooper();
|
||||||
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
|
||||||
// qualifier
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void updateReadLooper() {
|
|
||||||
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
|
||||||
// qualifier
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void updatePeekLooper() {
|
|
||||||
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
|
||||||
// qualifier
|
|
||||||
}
|
|
||||||
|
|
||||||
void maybeStopWriteLooperAndArmSocketWritableEvent();
|
void maybeStopWriteLooperAndArmSocketWritableEvent();
|
||||||
|
|
||||||
@@ -260,6 +270,7 @@ class QuicTransportBaseLite : virtual public QuicSocketLite,
|
|||||||
void excessWriteTimeoutExpired() noexcept;
|
void excessWriteTimeoutExpired() noexcept;
|
||||||
void lossTimeoutExpired() noexcept;
|
void lossTimeoutExpired() noexcept;
|
||||||
void idleTimeoutExpired(bool drain) noexcept;
|
void idleTimeoutExpired(bool drain) noexcept;
|
||||||
|
void keepaliveTimeoutExpired() noexcept;
|
||||||
|
|
||||||
bool isTimeoutScheduled(QuicTimerCallback* callback) const;
|
bool isTimeoutScheduled(QuicTimerCallback* callback) const;
|
||||||
|
|
||||||
@@ -284,15 +295,9 @@ class QuicTransportBaseLite : virtual public QuicSocketLite,
|
|||||||
// qualifier
|
// qualifier
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void processCallbacksAfterWriteData() {
|
void processCallbacksAfterWriteData();
|
||||||
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
|
||||||
// qualifier
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void setIdleTimer() {
|
void setIdleTimer();
|
||||||
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
|
||||||
// qualifier
|
|
||||||
}
|
|
||||||
virtual void scheduleAckTimeout() {
|
virtual void scheduleAckTimeout() {
|
||||||
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
// TODO: Fill this in from QuicTransportBase and remove the "virtual"
|
||||||
// qualifier
|
// qualifier
|
||||||
@@ -377,8 +382,11 @@ class QuicTransportBaseLite : virtual public QuicSocketLite,
|
|||||||
LossTimeout lossTimeout_;
|
LossTimeout lossTimeout_;
|
||||||
ExcessWriteTimeout excessWriteTimeout_;
|
ExcessWriteTimeout excessWriteTimeout_;
|
||||||
IdleTimeout idleTimeout_;
|
IdleTimeout idleTimeout_;
|
||||||
|
KeepaliveTimeout keepaliveTimeout_;
|
||||||
|
|
||||||
FunctionLooper::Ptr writeLooper_;
|
FunctionLooper::Ptr writeLooper_;
|
||||||
|
FunctionLooper::Ptr readLooper_;
|
||||||
|
FunctionLooper::Ptr peekLooper_;
|
||||||
|
|
||||||
Optional<std::string> exceptionCloseWhat_;
|
Optional<std::string> exceptionCloseWhat_;
|
||||||
|
|
||||||
|
@@ -588,8 +588,7 @@ class TestQuicTransport
|
|||||||
return none;
|
return none;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateWriteLooper(bool thisIteration, bool /* runInline */ = false)
|
void updateWriteLooper(bool thisIteration, bool /* runInline */ = false) {
|
||||||
override {
|
|
||||||
QuicTransportBase::updateWriteLooper(thisIteration);
|
QuicTransportBase::updateWriteLooper(thisIteration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -74,8 +74,7 @@ class TestQuicTransport
|
|||||||
return conn.version.value_or(*conn.originalVersion);
|
return conn.version.value_or(*conn.originalVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateWriteLooper(bool thisIteration, bool /* runInline */ = false)
|
void updateWriteLooper(bool thisIteration, bool /* runInline */ = false) {
|
||||||
override {
|
|
||||||
QuicTransportBase::updateWriteLooper(thisIteration);
|
QuicTransportBase::updateWriteLooper(thisIteration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user