Skip to content

Commit f433f4b

Browse files
authored
Scope the datagram send state indication to servers (#50)
Description Brings this branch in line with the upstream version of the #47 fix, microsoft#6179. #47 fixed the real bug — a server application is never told it may send datagrams unless path MTU discovery happens to change the max send length — but it changed client behaviour on the way. Preparing the same fix for upstream showed that the client-side part is neither needed nor desirable, so this narrows it. What #47 did to clients #47 re-evaluated the send state for every connection at QuicConnSetConfiguration, and started the indicated state at (FALSE, 0) for both roles. On a client that produces an extra DATAGRAM_STATE_CHANGED at ConnectionStart, carrying SendEnabled = TRUE before the peer's transport parameters are known. That indication is speculative: when the peer turns out not to support datagrams it is reversed a moment later. It also required changing thirteen ordered event expectations in EventTest.cpp, which is the test suite telling us the client's event sequence changed. What this changes Two changes make the fix server-only. The re-evaluation at SetConfiguration runs only for servers. A client's send state changes always have an owner to indicate to, so its indicated state never goes stale and it needs nothing there. The indicated state starts out as what the application can be assumed to know already, which differs by role. A client opens the connection itself, so it knows the documented initial state of (TRUE, UINT16_MAX). A server is handed a connection whose send state was settled before it ever saw it, so it knows nothing and assumes the conservative default of not being able to send. Result Client behaviour is bit-for-bit identical to before Track the datagram send state last indicated to the application #47. Servers gain exactly one indication: the application learns, once, that it may send datagrams. A server whose peer does not support datagrams is still told nothing, matching what it already assumes. No spurious event is introduced. EventTest.cpp needs no changes, which is the check that client behaviour is untouched. The core of #47 — tracking the state last indicated separately from the live state, so a change made while unowned is not mistaken for one already reported — is unchanged and still what makes the fix work when the recomputed values happen to match those recorded while unowned. Testing Also carried over from upstream: QuicTestDatagramNegotiation now checks that the server was told it can send, rather than only that it can. TestConnection records the count and contents of the DATAGRAM_STATE_CHANGED events indicated to it, since GetParam(QUIC_PARAM_CONN_DATAGRAM_SEND_ENABLED) reports the live state whether or not the application ever heard about it. With the server re-evaluation removed, DatagramNegotiation fails on all four parameterisations, so the assertion covers the original bug. *Datagram*:*Event*:*ValidateConn*:*Basic*:*Mtu*:*Resumption* passes in full: 529 tests, no failures. No compiler warnings. Documentation No documentation change. docs/API.md and docs/api/QUIC_CONNECTION_EVENT.md already describe the behaviour this implements.
1 parent 937c965 commit f433f4b

6 files changed

Lines changed: 75 additions & 36 deletions

File tree

src/core/connection.c

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ QuicConnAlloc(
127127
QuicOperationQueueInitialize(&Connection->OperQ);
128128
QuicSendInitialize(&Connection->Send, &Connection->Settings);
129129
QuicPathIDSetInitialize(&Connection->PathIDs);
130-
QuicDatagramInitialize(&Connection->Datagram);
130+
QuicDatagramInitialize(&Connection->Datagram, IsServer);
131131
QuicRangeInitialize(
132132
QUIC_MAX_RANGE_DECODE_ACKS,
133133
&Connection->DecodedAckRanges);
@@ -2445,21 +2445,21 @@ QuicConnSetConfiguration(
24452445
"[conn][%p] Handshake start",
24462446
Connection);
24472447

2448-
//
2449-
// Re-evaluate the datagram send state now that the connection is started
2450-
// and owned by the application.
2451-
//
2452-
// `Started` is an input to the max send length (QuicDatagramOnSendStateChanged
2453-
// derives it from QUIC_DPLPMTUD_MIN_MTU until then, and from the path's MTU
2454-
// afterwards), so it has to be recomputed here regardless. For a server this
2455-
// is also the first evaluation with an owner to indicate to: the peer's
2456-
// transport parameters are processed before the listener hands the
2457-
// connection over, so nothing before this point could reach the application.
2458-
//
2459-
// Clients reach this path before any peer transport parameters exist, so
2460-
// their behaviour is unchanged.
2461-
//
2462-
QuicDatagramOnSendStateChanged(&Connection->Datagram);
2448+
if (QuicConnIsServer(Connection)) {
2449+
//
2450+
// Evaluate the datagram send state for a server. This is the first
2451+
// point at which both of its inputs are settled and there is an
2452+
// external owner to indicate the result to: the peer's transport
2453+
// parameters are processed before the listener hands the connection to
2454+
// the application, and `Started` selects the MTU that the max send
2455+
// length is derived from.
2456+
//
2457+
// A client's send state is evaluated once its peer's transport
2458+
// parameters arrive, which is always after it has an owner, so it needs
2459+
// nothing here.
2460+
//
2461+
QuicDatagramOnSendStateChanged(&Connection->Datagram);
2462+
}
24632463

24642464
Status =
24652465
QuicCryptoInitializeTls(

src/core/datagram.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,21 @@ QuicCalculateDatagramLength(
7474
_IRQL_requires_max_(PASSIVE_LEVEL)
7575
void
7676
QuicDatagramInitialize(
77-
_In_ QUIC_DATAGRAM* Datagram
77+
_In_ QUIC_DATAGRAM* Datagram,
78+
_In_ BOOLEAN IsServer
7879
)
7980
{
8081
Datagram->SendEnabled = TRUE;
8182
Datagram->MaxSendLength = UINT16_MAX;
82-
Datagram->IndicatedSendEnabled = FALSE;
83-
Datagram->IndicatedMaxSendLength = 0;
83+
//
84+
// The indicated state starts out as what the application can be assumed to
85+
// know already. A client opens the connection itself, so it knows the initial
86+
// state set just above; a server is handed a connection whose send state is
87+
// settled before it ever sees it, so it knows nothing and assumes the
88+
// conservative default of not being able to send.
89+
//
90+
Datagram->IndicatedSendEnabled = IsServer ? FALSE : TRUE;
91+
Datagram->IndicatedMaxSendLength = IsServer ? 0 : UINT16_MAX;
8492
Datagram->PrioritySendQueueTail = &Datagram->SendQueue;
8593
Datagram->SendQueueTail = &Datagram->SendQueue;
8694
CxPlatDispatchLockInitialize(&Datagram->ApiQueueLock);
@@ -286,13 +294,11 @@ QuicDatagramOnSendStateChanged(
286294
}
287295

288296
//
289-
// Whether the live state moved, and whether the application's view of it is
290-
// stale, are separate questions. They diverge when the state changes with no
291-
// external owner to indicate to, which is the normal case for a server: the
292-
// peer's transport parameters are processed before the listener hands the
293-
// connection over. Comparing only against the live state there would leave
294-
// the change looking already reported, and the application would never learn
295-
// that datagrams are sendable.
297+
// Whether the live state moved and whether the application's view of it is
298+
// stale are separate questions, so they are asked separately. The two answers
299+
// differ whenever the state changed with no external owner to indicate to,
300+
// which is the normal case for a server: the peer's transport parameters are
301+
// processed before the listener hands the connection over.
296302
//
297303
const BOOLEAN StateChanged =
298304
SendEnabled != Datagram->SendEnabled ||

src/core/datagram.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,10 @@ typedef struct QUIC_DATAGRAM {
3434

3535
//
3636
// The send state last indicated to the application, which is not always the
37-
// live state above. The send state can change while the connection has no
38-
// external owner to indicate to — a server connection processes the peer's
39-
// transport parameters before the listener hands it to the application —
40-
// and such a change must not be mistaken for one that was already reported,
41-
// or the application is never told at all.
37+
// live state above. The two differ while a change made with no external
38+
// owner to indicate it to remains unreported, which is the normal case for a
39+
// server: the peer's transport parameters are processed before the listener
40+
// hands the connection to the application.
4241
//
4342
uint16_t IndicatedMaxSendLength;
4443

@@ -49,9 +48,7 @@ typedef struct QUIC_DATAGRAM {
4948
BOOLEAN SendEnabled : 1;
5049

5150
//
52-
// The `SendEnabled` last indicated to the application. Starts FALSE: until
53-
// an indication is made the application has been told nothing, and assumes
54-
// datagrams are not sendable.
51+
// The `SendEnabled` last indicated to the application.
5552
//
5653
BOOLEAN IndicatedSendEnabled : 1;
5754

@@ -60,7 +57,8 @@ typedef struct QUIC_DATAGRAM {
6057
_IRQL_requires_max_(PASSIVE_LEVEL)
6158
void
6259
QuicDatagramInitialize(
63-
_In_ QUIC_DATAGRAM* Datagram
60+
_In_ QUIC_DATAGRAM* Datagram,
61+
_In_ BOOLEAN IsServer
6462
);
6563

6664
_IRQL_requires_max_(PASSIVE_LEVEL)

src/test/lib/DatagramTest.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ QuicTestDatagramNegotiation(
111111

112112
TEST_TRUE(Server->GetDatagramSendEnabled()); // Client always enabled
113113

114+
//
115+
// Being able to send datagrams is not enough; the app has to be
116+
// told. For a server that indication comes once the connection
117+
// is started, the first point at which its send state has an
118+
// owner to be indicated to.
119+
//
120+
TEST_TRUE(Server->GetDatagramStateChangedCount() > 0);
121+
TEST_TRUE(Server->GetIndicatedDatagramSendEnabled());
122+
TEST_NOT_EQUAL(0, Server->GetIndicatedDatagramMaxSendLength());
123+
114124
CxPlatSleep(100); // Necessary?
115125

116126
if (DatagramReceiveEnabled) {

src/test/lib/TestConnection.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,9 @@ TestConnection::HandleConnectionEvent(
948948
break;
949949

950950
case QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED:
951-
// Use This
951+
DatagramStateChangedCount++;
952+
IndicatedDatagramSendEnabled = Event->DATAGRAM_STATE_CHANGED.SendEnabled != FALSE;
953+
IndicatedDatagramMaxSendLength = Event->DATAGRAM_STATE_CHANGED.MaxSendLength;
952954
break;
953955

954956
case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED:

src/test/lib/TestConnection.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ class TestConnection
106106
uint32_t DatagramsLost{};
107107
uint32_t DatagramsAcknowledged{};
108108

109+
uint32_t DatagramStateChangedCount{};
110+
bool IndicatedDatagramSendEnabled{false};
111+
uint16_t IndicatedDatagramMaxSendLength{};
112+
109113
const uint8_t* NegotiatedAlpn{};
110114
uint8_t NegotiatedAlpnLength{};
111115

@@ -336,6 +340,25 @@ class TestConnection
336340
LockGuard LockScope{Lock};
337341
return DatagramsCanceled;
338342
}
343+
//
344+
// The state carried by the last QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED
345+
// indicated to this connection, and how many have been indicated. Distinct
346+
// from GetDatagramSendEnabled, which queries the live state whether or not
347+
// the app was ever told about it.
348+
//
349+
uint32_t GetDatagramStateChangedCount() const {
350+
LockGuard LockScope{Lock};
351+
return DatagramStateChangedCount;
352+
}
353+
bool GetIndicatedDatagramSendEnabled() const {
354+
LockGuard LockScope{Lock};
355+
return IndicatedDatagramSendEnabled;
356+
}
357+
uint16_t GetIndicatedDatagramMaxSendLength() const {
358+
LockGuard LockScope{Lock};
359+
return IndicatedDatagramMaxSendLength;
360+
}
361+
339362
uint32_t GetDatagramsSuspectLost() const {
340363
LockGuard LockScope{Lock};
341364
return DatagramsSuspectLost;

0 commit comments

Comments
 (0)