Add DelayedDatagramSocket: UDP socket with simulated network delay [chromium/src : main]

0 views
Skip to first unread message

Patrick Meenan (Gerrit)

unread,
Jul 23, 2026, 12:28:01 PM (3 days ago) Jul 23
to Yoav Weiss (@Shopify), devtools...@chromium.org, Chromium LUCI CQ, chromium...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, devtools-re...@chromium.org, fenced-fra...@chromium.org, ipc-securi...@chromium.org, network-ser...@chromium.org, net-r...@chromium.org
Attention needed from Yoav Weiss (@Shopify)

Patrick Meenan added 11 comments

Patchset-level comments
File-level comment, Patchset 11 (Latest):
Patrick Meenan . resolved

Sorry for the delay, wanted to try to catch everything in one pass. Hopefully this is it.

File net/socket/delayed_datagram_socket.h
Line 113, Patchset 11 (Latest):class NET_EXPORT DelayedDatagramSocket : public DatagramClientSocket {
Patrick Meenan . unresolved

Consider class-level sequence checker annotation. Consider adding thread sequence assertions across public interface operations to enforce Chromium threading invariants.

**Suggested Fix:** Add private member `SEQUENCE_CHECKER(sequence_checker_);` and check `DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);` across public methods (`Read`, `Write`, `Connect`, `Close`).

Line 45, Patchset 11 (Latest):// task hop) — used as a surrogate for actual wire-arrival time. A caller Read()
Patrick Meenan . unresolved

```suggestion
// task hop) - used as a surrogate for actual wire-arrival time. A caller Read()
```

File net/socket/delayed_datagram_socket.cc
Line 7, Patchset 11 (Latest):#include <algorithm>
Patrick Meenan . unresolved

Think this is unused.

```suggestion
```

Line 162, Patchset 8: CompletionOnceCallback callback) {
Patrick Meenan . unresolved

`Read()` initiates inner read-ahead without checking `connected_`, and at line :479 the zero-RTT / unlimited-upload `Write()` path forwards directly without evaluating its connection check. Both entry points must synchronously return `ERR_SOCKET_NOT_CONNECTED` before accessing the inner socket. Otherwise, an unopened Windows UDP read path can trigger a release `CHECK` in underlying OS socket plumbing.

```suggestion
CompletionOnceCallback callback) {
if (!connected_) {
return ERR_SOCKET_NOT_CONNECTED;
}
```

Suggested Testing: In `delayed_datagram_socket_unittest.cc`, introduce a unit test fixture that instantiates a socket without calling `Connect()`, invokes `Read()`, `Write()`, and `ReadMultiple()`, and verifies `EXPECT_THAT(rv, IsError(ERR_SOCKET_NOT_CONNECTED))` without reaching underlying OS sockets.

Line 447, Patchset 8: callback) {
Patrick Meenan . unresolved
```suggestion
callback) {
if (!connected_) {
return ERR_SOCKET_NOT_CONNECTED;
}
```
Line 463, Patchset 8: const NetworkTrafficAnnotationTag& traffic_annotation) {
Patrick Meenan . unresolved
```suggestion
const NetworkTrafficAnnotationTag& traffic_annotation) {
if (!connected_) {
return ERR_SOCKET_NOT_CONNECTED;
}
```
Line 645, Patchset 8: // Inner-write errors are silently dropped: the application already saw a
// synchronous success from Write(), and UDP packet loss is a normal mode
// for the protocol. Continue draining the queue.
if (!send_queue_.empty()) {
Patrick Meenan . unresolved

shaped `Write()` calls report synchronous success before network transmission, while `OnInnerWireSendComplete()` discards subsequent asynchronous wire failures. This behavior suppresses critical QUIC error handling for events such as `ERR_MSG_TOO_BIG` (MTU discovery) and persistent route failures.

```suggestion
if (result < 0) {
if (result == ERR_MSG_TOO_BIG && !on_mtu_error_callback_.is_null()) {
on_mtu_error_callback_.Run(result);
} else if (IsFatalSocketError(result)) {
connected_ = false;
send_queue_.clear();
latched_write_error_ = result;
return;
}
}
if (!send_queue_.empty()) {
```
Line 750, Patchset 8: return wrapped_socket_->SetTos(dscp, ecn);
Patrick Meenan . unresolved

the wrapper snapshots metadata before the initial inner write, but discards the active `PendingSend` while that write is pending. A later QUIC packet can subsequently alter ECN state via `SetTos()`, causing POSIX or Windows nonblocking retries to transmit the earlier datagram using the later packet's metadata value.

**Possible Implementation:** Rather than destroying the popped packet object when `wrapped_socket_->Write()` returns `ERR_IO_PENDING`, retain the active transfer in an explicit `std::optional<PendingSend> in_flight_send_` member. When `SetTos()` is invoked, check whether `in_flight_send_` is active; if a write is pending or undergoing nonblocking retry, defer committing option mutations to `wrapped_socket_` until `OnInnerWireSendComplete()` finishes.

**Possible Testing:** Add a unit test where packet A is submitted under `SetTos(..., ECN_NO)` and returns `ERR_IO_PENDING`. While pending, configure `SetTos(..., ECN_CE)` for packet B, complete write A's callback, and verify via mock inspection that packet A hits the wire with `ECN_NO` and packet B with `ECN_CE`.

Line 743, Patchset 8: if (dscp != DSCP_NO_CHANGE) {
effective_dscp_ = dscp;
}
if (ecn != ECN_NO_CHANGE) {
effective_ecn_ = ecn;
}
tos_configured_ = true;
return wrapped_socket_->SetTos(dscp, ecn);
Patrick Meenan . unresolved
```suggestion
int rv = wrapped_socket_->SetTos(dscp, ecn);
if (rv == OK) {
if (dscp != DSCP_NO_CHANGE) {
effective_dscp_ = dscp;
}
if (ecn != ECN_NO_CHANGE) {
effective_ecn_ = ecn;
}
tos_configured_ = true;
}
return rv;
```
File net/socket/diff_serv_code_point.h
Line 71, Patchset 8: EcnCodePoint ecn) {
Patrick Meenan . unresolved
```suggestion
EcnCodePoint ecn) {
CHECK_NE(dscp, DSCP_NO_CHANGE);
CHECK_NE(ecn, ECN_NO_CHANGE);
```
Open in Gerrit

Related details

Attention is currently required from:
  • Yoav Weiss (@Shopify)
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I3d9f29c8900e7b6a6b6e99e5c039b0bdcaaa95d9
Gerrit-Change-Number: 8017223
Gerrit-PatchSet: 11
Gerrit-Owner: Yoav Weiss (@Shopify) <yoav...@chromium.org>
Gerrit-Reviewer: Patrick Meenan <pme...@chromium.org>
Gerrit-Reviewer: Yoav Weiss (@Shopify) <yoav...@chromium.org>
Gerrit-Attention: Yoav Weiss (@Shopify) <yoav...@chromium.org>
Gerrit-Comment-Date: Thu, 23 Jul 2026 16:27:41 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Yoav Weiss (@Shopify) (Gerrit)

unread,
Jul 25, 2026, 11:35:37 AM (yesterday) Jul 25
to devtools...@chromium.org, Patrick Meenan, Chromium LUCI CQ, chromium...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, devtools-re...@chromium.org, fenced-fra...@chromium.org, ipc-securi...@chromium.org, network-ser...@chromium.org, net-r...@chromium.org
Attention needed from Patrick Meenan

Yoav Weiss (@Shopify) added 7 comments

File net/socket/delayed_datagram_socket.h
Line 113, Patchset 11:class NET_EXPORT DelayedDatagramSocket : public DatagramClientSocket {
Patrick Meenan . resolved

Consider class-level sequence checker annotation. Consider adding thread sequence assertions across public interface operations to enforce Chromium threading invariants.

**Suggested Fix:** Add private member `SEQUENCE_CHECKER(sequence_checker_);` and check `DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);` across public methods (`Read`, `Write`, `Connect`, `Close`).

Yoav Weiss (@Shopify)

Done

Line 45, Patchset 11:// task hop) — used as a surrogate for actual wire-arrival time. A caller Read()
Patrick Meenan . resolved

```suggestion
// task hop) - used as a surrogate for actual wire-arrival time. A caller Read()
```

Yoav Weiss (@Shopify)

Done

File net/socket/delayed_datagram_socket.cc
Line 7, Patchset 11:#include <algorithm>
Patrick Meenan . resolved

Think this is unused.

```suggestion
```

Yoav Weiss (@Shopify)

Done

Line 162, Patchset 8: CompletionOnceCallback callback) {
Patrick Meenan . resolved

`Read()` initiates inner read-ahead without checking `connected_`, and at line :479 the zero-RTT / unlimited-upload `Write()` path forwards directly without evaluating its connection check. Both entry points must synchronously return `ERR_SOCKET_NOT_CONNECTED` before accessing the inner socket. Otherwise, an unopened Windows UDP read path can trigger a release `CHECK` in underlying OS socket plumbing.

```suggestion
CompletionOnceCallback callback) {
if (!connected_) {
return ERR_SOCKET_NOT_CONNECTED;
}
```

Suggested Testing: In `delayed_datagram_socket_unittest.cc`, introduce a unit test fixture that instantiates a socket without calling `Connect()`, invokes `Read()`, `Write()`, and `ReadMultiple()`, and verifies `EXPECT_THAT(rv, IsError(ERR_SOCKET_NOT_CONNECTED))` without reaching underlying OS sockets.

Yoav Weiss (@Shopify)

Done

Patrick Meenan . resolved
```suggestion
callback) {
if (!connected_) {
return ERR_SOCKET_NOT_CONNECTED;
}
```
Yoav Weiss (@Shopify)

Done

Line 463, Patchset 8: const NetworkTrafficAnnotationTag& traffic_annotation) {
Patrick Meenan . resolved
```suggestion
const NetworkTrafficAnnotationTag& traffic_annotation) {
if (!connected_) {
return ERR_SOCKET_NOT_CONNECTED;
}
```
Yoav Weiss (@Shopify)

Done

File net/socket/diff_serv_code_point.h
Line 71, Patchset 8: EcnCodePoint ecn) {
Patrick Meenan . resolved
```suggestion
EcnCodePoint ecn) {
CHECK_NE(dscp, DSCP_NO_CHANGE);
CHECK_NE(ecn, ECN_NO_CHANGE);
```
Yoav Weiss (@Shopify)

Done

Open in Gerrit

Related details

Attention is currently required from:
  • Patrick Meenan
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I3d9f29c8900e7b6a6b6e99e5c039b0bdcaaa95d9
Gerrit-Change-Number: 8017223
Gerrit-PatchSet: 12
Gerrit-Owner: Yoav Weiss (@Shopify) <yoav...@chromium.org>
Gerrit-Reviewer: Patrick Meenan <pme...@chromium.org>
Gerrit-Reviewer: Yoav Weiss (@Shopify) <yoav...@chromium.org>
Gerrit-Attention: Patrick Meenan <pme...@chromium.org>
Gerrit-Comment-Date: Sat, 25 Jul 2026 15:35:11 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Patrick Meenan <pme...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Yoav Weiss (@Shopify) (Gerrit)

unread,
Jul 25, 2026, 11:43:40 AM (yesterday) Jul 25
to devtools...@chromium.org, Patrick Meenan, Chromium LUCI CQ, chromium...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, devtools-re...@chromium.org, fenced-fra...@chromium.org, ipc-securi...@chromium.org, network-ser...@chromium.org, net-r...@chromium.org
Attention needed from Patrick Meenan

Yoav Weiss (@Shopify) added 3 comments

File net/socket/delayed_datagram_socket.cc
Line 645, Patchset 8: // Inner-write errors are silently dropped: the application already saw a
// synchronous success from Write(), and UDP packet loss is a normal mode
// for the protocol. Continue draining the queue.
if (!send_queue_.empty()) {
Patrick Meenan . resolved

shaped `Write()` calls report synchronous success before network transmission, while `OnInnerWireSendComplete()` discards subsequent asynchronous wire failures. This behavior suppresses critical QUIC error handling for events such as `ERR_MSG_TOO_BIG` (MTU discovery) and persistent route failures.

```suggestion
if (result < 0) {
if (result == ERR_MSG_TOO_BIG && !on_mtu_error_callback_.is_null()) {
on_mtu_error_callback_.Run(result);
} else if (IsFatalSocketError(result)) {
connected_ = false;
send_queue_.clear();
latched_write_error_ = result;
return;
}
}
if (!send_queue_.empty()) {
```
Yoav Weiss (@Shopify)

Done

Line 750, Patchset 8: return wrapped_socket_->SetTos(dscp, ecn);
Patrick Meenan . resolved

the wrapper snapshots metadata before the initial inner write, but discards the active `PendingSend` while that write is pending. A later QUIC packet can subsequently alter ECN state via `SetTos()`, causing POSIX or Windows nonblocking retries to transmit the earlier datagram using the later packet's metadata value.

**Possible Implementation:** Rather than destroying the popped packet object when `wrapped_socket_->Write()` returns `ERR_IO_PENDING`, retain the active transfer in an explicit `std::optional<PendingSend> in_flight_send_` member. When `SetTos()` is invoked, check whether `in_flight_send_` is active; if a write is pending or undergoing nonblocking retry, defer committing option mutations to `wrapped_socket_` until `OnInnerWireSendComplete()` finishes.

**Possible Testing:** Add a unit test where packet A is submitted under `SetTos(..., ECN_NO)` and returns `ERR_IO_PENDING`. While pending, configure `SetTos(..., ECN_CE)` for packet B, complete write A's callback, and verify via mock inspection that packet A hits the wire with `ECN_NO` and packet B with `ECN_CE`.

Yoav Weiss (@Shopify)

Dropped setting the TOS in the shaping path. See comment.

Line 743, Patchset 8: if (dscp != DSCP_NO_CHANGE) {

effective_dscp_ = dscp;
}
if (ecn != ECN_NO_CHANGE) {
effective_ecn_ = ecn;
}
tos_configured_ = true;
return wrapped_socket_->SetTos(dscp, ecn);
Patrick Meenan . resolved
```suggestion

int rv = wrapped_socket_->SetTos(dscp, ecn);
if (rv == OK) {
if (dscp != DSCP_NO_CHANGE) {
effective_dscp_ = dscp;
}
if (ecn != ECN_NO_CHANGE) {
effective_ecn_ = ecn;
}
tos_configured_ = true;
}
return rv;
```
Yoav Weiss (@Shopify)

Removed SetTOS entirely from the shaping path. See comment above

Open in Gerrit

Related details

Attention is currently required from:
  • Patrick Meenan
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I3d9f29c8900e7b6a6b6e99e5c039b0bdcaaa95d9
    Gerrit-Change-Number: 8017223
    Gerrit-PatchSet: 12
    Gerrit-Owner: Yoav Weiss (@Shopify) <yoav...@chromium.org>
    Gerrit-Reviewer: Patrick Meenan <pme...@chromium.org>
    Gerrit-Reviewer: Yoav Weiss (@Shopify) <yoav...@chromium.org>
    Gerrit-Attention: Patrick Meenan <pme...@chromium.org>
    Gerrit-Comment-Date: Sat, 25 Jul 2026 15:43:15 +0000
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Patrick Meenan (Gerrit)

    unread,
    Jul 25, 2026, 10:44:28 PM (21 hours ago) Jul 25
    to Yoav Weiss (@Shopify), devtools...@chromium.org, Chromium LUCI CQ, chromium...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, devtools-re...@chromium.org, fenced-fra...@chromium.org, ipc-securi...@chromium.org, network-ser...@chromium.org, net-r...@chromium.org
    Attention needed from Yoav Weiss (@Shopify)

    Patrick Meenan voted and added 1 comment

    Votes added by Patrick Meenan

    Code-Review+1

    1 comment

    Patchset-level comments
    File-level comment, Patchset 12 (Latest):
    Patrick Meenan . resolved

    LGTM, Thanks!

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Yoav Weiss (@Shopify)
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement satisfiedCode-Review
      • requirement satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I3d9f29c8900e7b6a6b6e99e5c039b0bdcaaa95d9
      Gerrit-Change-Number: 8017223
      Gerrit-PatchSet: 12
      Gerrit-Owner: Yoav Weiss (@Shopify) <yoav...@chromium.org>
      Gerrit-Reviewer: Patrick Meenan <pme...@chromium.org>
      Gerrit-Reviewer: Yoav Weiss (@Shopify) <yoav...@chromium.org>
      Gerrit-Attention: Yoav Weiss (@Shopify) <yoav...@chromium.org>
      Gerrit-Comment-Date: Sun, 26 Jul 2026 02:44:19 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy
      Reply all
      Reply to author
      Forward
      0 new messages