Loading src/quic.cpp +16 −4 Original line number Diff line number Diff line Loading @@ -7046,10 +7046,12 @@ void quic::closeStream(uint64_t stream_id) { // such no-op stream is needless wire traffic (observed to visibly // degrade unrelated traffic on the same connection when done in bulk). if (!stream.recv_fin && stream.send_offset > 0) { stopSending(stream_id); // Already confirmed to exist (it, above) — skip stopSending()'s own // existence check and go straight to the wire-building helper. sendStopSendingFrame(stream_id, 0x0102); } retireStreamIfDone(stream_id); retireStreamIfDone(stream_id, it); } // If a stream has terminated in both directions, erase it and — for a Loading @@ -7059,6 +7061,10 @@ void quic::closeStream(uint64_t stream_id) { void quic::retireStreamIfDone(uint64_t stream_id) { auto it = _streams.find(stream_id); if (it == _streams.end()) return; retireStreamIfDone(stream_id, it); } void quic::retireStreamIfDone(uint64_t stream_id, std::unordered_map<uint64_t, Stream>::iterator it) { Stream& stream = it->second; // If both sides have sent FIN, remove stream and replenish peer's budget Loading Loading @@ -7123,10 +7129,13 @@ void quic::resetStream(uint64_t stream_id, uint64_t error_code) { // No further STREAM frames should follow an abort. if (it != _streams.end()) { it->second.send_fin = true; } // Already have a valid iterator from the lookup above — pass it // directly instead of making retireStreamIfDone() re-find stream_id. retireStreamIfDone(stream_id, it); } else { retireStreamIfDone(stream_id); } } // ============================================================================ // STOP_SENDING: ask the peer to abort its send side (RFC 9000 §19.5) Loading @@ -7136,7 +7145,10 @@ void quic::stopSending(uint64_t stream_id, uint64_t error_code) { std::lock_guard<std::recursive_mutex> lock(quic_mtx()); if (_streams.find(stream_id) == _streams.end()) return; sendStopSendingFrame(stream_id, error_code); } void quic::sendStopSendingFrame(uint64_t stream_id, uint64_t error_code) { std::vector<uint8_t> frame; frame.push_back(0x05); // STOP_SENDING frame type uint8_t buf[8]; Loading src/socket.h +21 −2 Original line number Diff line number Diff line Loading @@ -888,6 +888,18 @@ namespace netplus { // to read buffered data or check isStreamReset()/hasStreamData() // after a STREAM/RESET_STREAM frame updates send_fin/recv_fin. void retireStreamIfDone(uint64_t stream_id); // Iterator-based variant: used by closeStream()/resetStream(), which // already hold a valid iterator from their own _streams lookup, so // one logical "close a stream" call doesn't re-find the same // stream_id 2-3 times over (F48). `it` must be a valid, dereferenceable // iterator (checked by the caller) — this overload skips the // existence check the stream_id-only version does. void retireStreamIfDone(uint64_t stream_id, std::unordered_map<uint64_t, Stream>::iterator it); // Wire-only STOP_SENDING send, no existence check — shared by the // public stopSending() (which checks first) and closeStream() (which // already knows the stream exists from its own lookup). void sendStopSendingFrame(uint64_t stream_id, uint64_t error_code); // Loss detection and retransmission void checkLossAndRetransmit(); Loading Loading @@ -1210,8 +1222,15 @@ namespace netplus { // itself is declared near the top of the private section, above). EncryptionLevel _current_enc_level = EncryptionLevel::Initial; // Streams std::map<uint64_t, Stream> _streams; // Streams. unordered_map, not map: stream_id has no ordering // requirement anywhere this is used (both iteration sites in // quic.cpp visit "every stream"/"any stream with data" without // relying on sort order — grep confirms it), so there is no reason // to pay std::map's O(log n) tree lookups on the hot per-STREAM- // frame lookup path when std::hash<uint64_t> gives O(1) average // lookup for free — mirrors the same std::map -> unordered_map // reasoning already applied to _child_connections. std::unordered_map<uint64_t, Stream> _streams; // Bounded tracking for "has this stream_id been fully closed" // (prevents resurrection via a stale/retransmitted frame after Loading Loading
src/quic.cpp +16 −4 Original line number Diff line number Diff line Loading @@ -7046,10 +7046,12 @@ void quic::closeStream(uint64_t stream_id) { // such no-op stream is needless wire traffic (observed to visibly // degrade unrelated traffic on the same connection when done in bulk). if (!stream.recv_fin && stream.send_offset > 0) { stopSending(stream_id); // Already confirmed to exist (it, above) — skip stopSending()'s own // existence check and go straight to the wire-building helper. sendStopSendingFrame(stream_id, 0x0102); } retireStreamIfDone(stream_id); retireStreamIfDone(stream_id, it); } // If a stream has terminated in both directions, erase it and — for a Loading @@ -7059,6 +7061,10 @@ void quic::closeStream(uint64_t stream_id) { void quic::retireStreamIfDone(uint64_t stream_id) { auto it = _streams.find(stream_id); if (it == _streams.end()) return; retireStreamIfDone(stream_id, it); } void quic::retireStreamIfDone(uint64_t stream_id, std::unordered_map<uint64_t, Stream>::iterator it) { Stream& stream = it->second; // If both sides have sent FIN, remove stream and replenish peer's budget Loading Loading @@ -7123,10 +7129,13 @@ void quic::resetStream(uint64_t stream_id, uint64_t error_code) { // No further STREAM frames should follow an abort. if (it != _streams.end()) { it->second.send_fin = true; } // Already have a valid iterator from the lookup above — pass it // directly instead of making retireStreamIfDone() re-find stream_id. retireStreamIfDone(stream_id, it); } else { retireStreamIfDone(stream_id); } } // ============================================================================ // STOP_SENDING: ask the peer to abort its send side (RFC 9000 §19.5) Loading @@ -7136,7 +7145,10 @@ void quic::stopSending(uint64_t stream_id, uint64_t error_code) { std::lock_guard<std::recursive_mutex> lock(quic_mtx()); if (_streams.find(stream_id) == _streams.end()) return; sendStopSendingFrame(stream_id, error_code); } void quic::sendStopSendingFrame(uint64_t stream_id, uint64_t error_code) { std::vector<uint8_t> frame; frame.push_back(0x05); // STOP_SENDING frame type uint8_t buf[8]; Loading
src/socket.h +21 −2 Original line number Diff line number Diff line Loading @@ -888,6 +888,18 @@ namespace netplus { // to read buffered data or check isStreamReset()/hasStreamData() // after a STREAM/RESET_STREAM frame updates send_fin/recv_fin. void retireStreamIfDone(uint64_t stream_id); // Iterator-based variant: used by closeStream()/resetStream(), which // already hold a valid iterator from their own _streams lookup, so // one logical "close a stream" call doesn't re-find the same // stream_id 2-3 times over (F48). `it` must be a valid, dereferenceable // iterator (checked by the caller) — this overload skips the // existence check the stream_id-only version does. void retireStreamIfDone(uint64_t stream_id, std::unordered_map<uint64_t, Stream>::iterator it); // Wire-only STOP_SENDING send, no existence check — shared by the // public stopSending() (which checks first) and closeStream() (which // already knows the stream exists from its own lookup). void sendStopSendingFrame(uint64_t stream_id, uint64_t error_code); // Loss detection and retransmission void checkLossAndRetransmit(); Loading Loading @@ -1210,8 +1222,15 @@ namespace netplus { // itself is declared near the top of the private section, above). EncryptionLevel _current_enc_level = EncryptionLevel::Initial; // Streams std::map<uint64_t, Stream> _streams; // Streams. unordered_map, not map: stream_id has no ordering // requirement anywhere this is used (both iteration sites in // quic.cpp visit "every stream"/"any stream with data" without // relying on sort order — grep confirms it), so there is no reason // to pay std::map's O(log n) tree lookups on the hot per-STREAM- // frame lookup path when std::hash<uint64_t> gives O(1) average // lookup for free — mirrors the same std::map -> unordered_map // reasoning already applied to _child_connections. std::unordered_map<uint64_t, Stream> _streams; // Bounded tracking for "has this stream_id been fully closed" // (prevents resurrection via a stale/retransmitted frame after Loading