quic/dsr/frontend/WriteFunctions.cpp (78 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include <folly/ScopeGuard.h> #include <quic/dsr/frontend/WriteFunctions.h> namespace quic { uint64_t writePacketizationRequest( QuicServerConnectionState& connection, const ConnectionId& dstCid, size_t packetLimit, const Aead& aead, TimePoint writeLoopBeginTime) { DSRStreamFrameScheduler scheduler(connection); uint64_t packetCounter = 0; folly::F14FastSet<DSRPacketizationRequestSender*> senders; SCOPE_EXIT { for (auto sender : senders) { if (connection.qLogger) { connection.qLogger->addTransportStateUpdate("DSR flushing sender"); } sender->flush(); } }; if (!writeLoopTimeLimit(writeLoopBeginTime, connection)) { return packetCounter; } while (scheduler.hasPendingData() && packetCounter < packetLimit && (packetCounter < connection.transportSettings.maxBatchSize || writeLoopTimeLimit(writeLoopBeginTime, connection))) { auto packetNum = getNextPacketNum(connection, PacketNumberSpace::AppData); ShortHeader header(ProtectionType::KeyPhaseZero, dstCid, packetNum); auto writableBytes = std::min( connection.udpSendPacketLen, congestionControlWritableBytes(connection)); uint64_t cipherOverhead = aead.getCipherOverhead(); if (writableBytes < cipherOverhead) { writableBytes = 0; } else { writableBytes -= cipherOverhead; } DSRPacketBuilder packetBuilder( writableBytes, std::move(header), getAckState(connection, PacketNumberSpace::AppData) .largestAckedByPeer.value_or(0)); auto schedulerResult = scheduler.writeStream(packetBuilder); if (!schedulerResult.writeSuccess) { /** * Scheduling can fail when we: * (1) run out of flow control * (2) there is actually no DSR stream to write - we shouldn't come here * in the first place though. * (3) Packet is no space left - e.g., due to CC * (4) Error in write codec - Can that happen? * * At least for (1) and (3), we should flush the sender. */ if (schedulerResult.sender) { senders.insert(schedulerResult.sender); } return packetCounter; } CHECK(schedulerResult.sender); auto packet = std::move(packetBuilder).buildPacket(); // The contract is that if scheduler can schedule, builder has to be able to // build. CHECK_GT(packet.encodedSize, 0); bool instructionAddError = false; for (const auto& instruction : packet.sendInstructions) { if (!schedulerResult.sender->addSendInstruction(instruction)) { instructionAddError = true; break; } } // Similar to the regular write case, if we build, we update connection // states. The connection states are changed already no matter the result // of addSendInstruction() call. updateConnection( connection, folly::none /* Packet Event */, packet.packet, Clock::now(), packet.encodedSize, // TODO: (yangchi) Figure out how to calculate the // packet.encodedBodySize for the DSR case. For now, it's not being // used, so setting it to 0 0, true /* isDSRPacket */); connection.dsrPacketCount++; if (instructionAddError) { // TODO: Support empty write loop detection senders.insert(schedulerResult.sender); return packetCounter; } ++packetCounter; senders.insert(schedulerResult.sender); } return packetCounter; } } // namespace quic