Commit 3e41974d authored by jan.koester's avatar jan.koester
Browse files

test

parent c950a047
Loading
Loading
Loading
Loading
+31 −10
Original line number Diff line number Diff line
@@ -4502,7 +4502,7 @@ size_t quic::recvData(buffer& data, int flags) {
// For use by HTTP/3 clients that read specific streams via recvStreamData().
// ============================================================================

void quic::pumpNetwork(int flags) {
size_t quic::pumpNetwork(int flags) {
    (void)flags;
    std::vector<PendingDispatch> dispatches;

@@ -4512,6 +4512,7 @@ void quic::pumpNetwork(int flags) {
    static constexpr int MAX_PUMP = 64;
    std::vector<DatagramView> datagrams;
    recvBatchViews(datagrams, MAX_PUMP);
    const size_t datagram_count = datagrams.size();

    if (!datagrams.empty()) {
        std::lock_guard<std::recursive_mutex> lock(quic_mtx());
@@ -4598,19 +4599,39 @@ void quic::pumpNetwork(int flags) {
        std::lock_guard<std::recursive_mutex> lock(quic_mtx());
        pmtudTick();
    }

    return datagram_count;
}

void quic::pumpNetworkWait(int timeout_ms) {
    // Bounded, best-effort wait for the socket to become readable — never
    // holds quic_mtx() while waiting (socketwait operates on the raw fd),
    // so this can't stall anything else touching this connection
    // concurrently. A false/timeout return is not an error: it just means
    // pumpNetwork() below will find nothing new, same as if this wait were
    // skipped entirely.
    if (timeout_ms > 0) {
        socketwait sw;
        sw.waitRead(*this, timeout_ms);
    // Fast path: try the cheap non-blocking pump first. If it already found
    // something, we're done — this costs exactly what pumpNetwork(
    // MSG_DONTWAIT) alone would have cost (one recvmmsg()), no extra
    // syscalls, regardless of timeout_ms. This matters: an earlier version
    // of this function waited unconditionally *before* pumping, which on a
    // tight interleaved send/receive loop (e.g. a request/response client)
    // paid for epoll_ctl(ADD)+epoll_wait()+epoll_ctl(DEL) on every single
    // call even when data was already sitting in the socket buffer —
    // measured ~35% slower end-to-end than plain busy-polling on a loopback
    // echo benchmark, i.e. worse, not better.
    if (pumpNetwork(MSG_DONTWAIT) > 0 || timeout_ms <= 0) {
        return;
    }

    // Nothing immediately available — block (bounded) for it instead of
    // letting the caller spin this function itself. Reuses one socketwait
    // (and its epoll fd) across every call instead of constructing a fresh
    // one each time: socketwait's constructor/destructor each cost a
    // syscall (epoll_create1()/close()) on top of waitRead()'s own
    // epoll_ctl(ADD)+epoll_wait()+epoll_ctl(DEL) — see _cc_socketwait's
    // comment for the same fix applied to the congestion-window wait path.
    // Never holds quic_mtx() while waiting (socketwait operates on the raw
    // fd), so this can't stall anything else touching this connection
    // concurrently. A false/timeout return is not an error: the follow-up
    // pumpNetwork() below just finds nothing new, same as if the wait were
    // skipped entirely.
    if (!_pump_socketwait) _pump_socketwait = std::make_unique<socketwait>();
    _pump_socketwait->waitRead(*this, timeout_ms);
    pumpNetwork(MSG_DONTWAIT);
}

+16 −2
Original line number Diff line number Diff line
@@ -884,8 +884,12 @@ namespace netplus {

		// Read raw UDP datagrams and process QUIC packets without consuming
		// stream data.  Use this in HTTP/3 client loops to pump the network
		// while reading specific streams via recvStreamData().
		void pumpNetwork(int flags = 0);
		// while reading specific streams via recvStreamData(). Returns the
		// number of datagrams processed this call (0 if none were
		// available) — existing callers that ignore the return value are
		// unaffected; pumpNetworkWait() below uses it to decide whether a
		// wait is even necessary.
		size_t pumpNetwork(int flags = 0);

		// Like pumpNetwork(), but if nothing is immediately available,
		// blocks up to timeout_ms waiting for the socket to become readable
@@ -2185,6 +2189,16 @@ namespace netplus {
		// fd per connection that never needs one.
		std::unique_ptr<socketwait> _cc_socketwait;

		// Same reuse rationale as _cc_socketwait, but dedicated to
		// pumpNetworkWait() rather than shared with it: the congestion-wait
		// loop above unlocks quic_mtx() while _cc_socketwait->waitRead() is
		// in flight, so another thread calling pumpNetworkWait() on this
		// same connection at that moment could otherwise be touching the
		// same socketwait object (and its single epoll fd) concurrently —
		// socketwait isn't designed for that. A second, separate lazily-
		// constructed instance avoids the question entirely.
		std::unique_ptr<socketwait> _pump_socketwait;

		// ---- Path MTU Discovery (DPLPMTUD, RFC 8899 / RFC 9000 §14.3-14.4) ----
		// A probe is a PATH_CHALLENGE frame padded with PADDING to an exact
		// target wire size, sent as an ordinary 1-RTT packet. Success is