Loading src/quic.cpp +22 −3 Original line number Diff line number Diff line Loading @@ -1985,7 +1985,17 @@ void quic::accept(std::unique_ptr<socket>& csock, bool nonblock) { std::map<std::shared_ptr<quic>, std::vector<std::pair<const uint8_t*, size_t>>> pending_app_pkts; auto flush_pending_app = [&]() { for (auto& [existing, pkts] : pending_app_pkts) { std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx()); // try_lock, not block: this runs on the shared accept/drain // loop (see the concurrency note above — normally one thread, // but never more than one at a time for a given listener), so // blocking here to wait out a connection that's busy elsewhere // (e.g. mid-transfer) stalls accepting brand-new connections // and processing every OTHER existing connection's traffic // too, not just this one. QUIC's own loss detection retransmits // whatever we skip, so dropping it now and picking it up on a // later drain cycle is safe and far cheaper than blocking. std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx(), std::try_to_lock); if (!child_lock.owns_lock()) continue; existing->processApplicationPacketsBatch(pkts); if (existing->_handshake_complete && Loading Loading @@ -2164,8 +2174,17 @@ void quic::accept(std::unique_ptr<socket>& csock, bool nonblock) { // Fall through to create a new connection } else { // Active connection std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx()); // Active connection. try_lock, not block — this long-header // packet (a mid-handshake retransmission, typically) is handled // inline rather than deferred via pending_app_pkts, so blocking // here stalls this accept() call at the current datagram, // before it can ever reach later ones in the same batch — // including a brand-new connection's Initial packet. A busy // connection (e.g. mid-transfer elsewhere) must never be able // to stall new connections from being accepted at all. QUIC's // own loss detection retransmits whatever we skip here. std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx(), std::try_to_lock); if (!child_lock.owns_lock()) continue; existing->processIncomingPacket(dgram.data(), dgram.size()); // Flush child's pending flow control frames Loading Loading
src/quic.cpp +22 −3 Original line number Diff line number Diff line Loading @@ -1985,7 +1985,17 @@ void quic::accept(std::unique_ptr<socket>& csock, bool nonblock) { std::map<std::shared_ptr<quic>, std::vector<std::pair<const uint8_t*, size_t>>> pending_app_pkts; auto flush_pending_app = [&]() { for (auto& [existing, pkts] : pending_app_pkts) { std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx()); // try_lock, not block: this runs on the shared accept/drain // loop (see the concurrency note above — normally one thread, // but never more than one at a time for a given listener), so // blocking here to wait out a connection that's busy elsewhere // (e.g. mid-transfer) stalls accepting brand-new connections // and processing every OTHER existing connection's traffic // too, not just this one. QUIC's own loss detection retransmits // whatever we skip, so dropping it now and picking it up on a // later drain cycle is safe and far cheaper than blocking. std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx(), std::try_to_lock); if (!child_lock.owns_lock()) continue; existing->processApplicationPacketsBatch(pkts); if (existing->_handshake_complete && Loading Loading @@ -2164,8 +2174,17 @@ void quic::accept(std::unique_ptr<socket>& csock, bool nonblock) { // Fall through to create a new connection } else { // Active connection std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx()); // Active connection. try_lock, not block — this long-header // packet (a mid-handshake retransmission, typically) is handled // inline rather than deferred via pending_app_pkts, so blocking // here stalls this accept() call at the current datagram, // before it can ever reach later ones in the same batch — // including a brand-new connection's Initial packet. A busy // connection (e.g. mid-transfer elsewhere) must never be able // to stall new connections from being accepted at all. QUIC's // own loss detection retransmits whatever we skip here. std::unique_lock<std::recursive_mutex> child_lock(existing->quic_mtx(), std::try_to_lock); if (!child_lock.owns_lock()) continue; existing->processIncomingPacket(dgram.data(), dgram.size()); // Flush child's pending flow control frames Loading