Skip to content

Commit eb8d2e1

Browse files
authored
CP: Allow the app to react when app-provided buffer is insufficient (#5347)
* Allow the app to react when app-provided buffer is insufficient (#5313) * Notify the app when not enough receive buffer is present * Remove constrains on flow control * Add test * Update documentation
1 parent c359363 commit eb8d2e1

22 files changed

+523
-1504
lines changed

docs/PreviewFeatures.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ TODO
3535

3636
TODO
3737

38-
### StreamProvideReceiveBuffers
38+
### App-provided receive buffers
3939

40-
[StreamProvideReceiveBuffers](StreamProvideReceiveBuffers.md)
41-
42-
TODO
40+
- [StreamProvideReceiveBuffers](api/StreamProvideReceiveBuffers.md)
41+
- [QUIC_API_ENABLE_PREVIEW_FEATURES](api/QUIC_STREAM_EVENT.md#quic_stream_event_receive_buffer_needed)

docs/Streams.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,16 @@ The application regains full ownership of a buffer after it get a receive notifi
170170
If the application accepts all the buffer's bytes **inline** from the receive notification, by returning `QUIC_STATUS_SUCCESS` and setting `TotalBufferLength` appropriately,
171171
it can free or reuse the buffer while in the notification handler.
172172

173+
If more data is received on the stream than buffer space was provided by the application, MsQuic will emit a `QUIC_STREAM_EVENT_RECEIVE_BUFFER_NEEDED` notification.
174+
When receiving this notification, the app can:
175+
- provide a sufficient amount of buffer space **inline** from the callback using [`StreamProvideReceiveBuffers`](./api/StreamProvideReceiveBuffers.md)
176+
- shutdown the stream receive direction of the stream **inline** by calling [`StreamShutdown`](api/StreamShutdown.md) with the `QUIC_STREAM_SHUTDOWN_FLAG_INLINE` flag
177+
178+
When the callback returns, if the stream has not been shutdown and a sufficient amount of memory is not available, the connection is closed abortively.
179+
Providing memory in reaction to `QUIC_STREAM_EVENT_RECEIVE_BUFFER_NEEDED` can impact performances negatively.
180+
173181
For an application, providing receive buffers can improve performances by saving a copy: MsQuic places data directly in its final destination.
174-
However, it comes with a large complexity overhead for the application, both in term of memory management and in term of flow control:
175-
an application providing too much or too little buffer space could negatively impact performances.
182+
However, it comes with a large complexity overhead for the application, both in term of memory management and in term of flow control: an application providing too much or too little buffer space could negatively impact performances.
176183
Because of this, app-owned mode should be considered an advanced feature and used with caution.
177184

178185
> **Note**: As of now, app-owned buffer mode is not compatible with multi-receive mode. If multi-receive mode is enabled for the connection and app-owned mode is enabled on a stream, that specific stream will behave as if multi-receive mode was disabled. This may change in the future.

docs/api/QUIC_STREAM_EVENT.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ typedef enum QUIC_STREAM_EVENT_TYPE {
1717
QUIC_STREAM_EVENT_IDEAL_SEND_BUFFER_SIZE = 8,
1818
QUIC_STREAM_EVENT_PEER_ACCEPTED = 9,
1919
QUIC_STREAM_EVENT_CANCEL_ON_LOSS = 10,
20+
#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
21+
QUIC_STREAM_EVENT_RECEIVE_BUFFER_NEEDED = 11,
22+
#endif
2023
} QUIC_STREAM_EVENT_TYPE;
2124
```
2225

@@ -69,6 +72,11 @@ typedef struct QUIC_STREAM_EVENT {
6972
struct {
7073
/* out */ QUIC_UINT62 ErrorCode;
7174
} CANCEL_ON_LOSS;
75+
#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
76+
struct {
77+
/* in */ uint64_t BufferLengthNeeded;
78+
} RECEIVE_BUFFER_NEEDED;
79+
#endif
7280
};
7381
} QUIC_STREAM_EVENT;
7482
```
@@ -261,6 +269,23 @@ The application can supply an error code in this struct to be sent to the peer.
261269

262270
The application can set this 62 bit error code to communicate to the peer about the stream shutdown, which is received by the peer as a `QUIC_STREAM_EVENT_PEER_SEND_ABORTED` event on its stream object.
263271

272+
## QUIC_STREAM_EVENT_RECEIVE_BUFFER_NEEDED
273+
274+
This event is raised when a stream using app-provided receive buffers runs out of receive buffer space.
275+
276+
`BufferLengthNeeded`
277+
278+
The number of bytes MsQuic needs to be able to store the received data.
279+
280+
When receiving this notification, the app can:
281+
- provide a sufficient amount of buffer space **inline** from the callback using [`StreamProvideReceiveBuffers`](./StreamProvideReceiveBuffers.md)
282+
- shutdown the stream receive direction of the stream **inline** by calling [`StreamShutdown`](./StreamShutdown.md)
283+
with the `QUIC_STREAM_SHUTDOWN_FLAG_INLINE` flag
284+
285+
Otherwise, the connection will be closed abortively.
286+
287+
See [App-Owned Buffer Mode](../Streams.md#App-Owned_Buffer_Mode) for further details.
288+
264289
# See Also
265290

266291
[Streams](../Streams.md)<br>

docs/api/StreamProvideReceiveBuffers.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ QUIC_STATUS
2828
# Remarks
2929
3030
This is an asynchronous API but it can run inline if called in a callback.
31-
If called inline when handling a `QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED` event, it will convert the stream to app-owned buffer mode.
31+
32+
If called inline when handling a `QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED` event, it will convert
33+
the stream to app-owned buffer mode.
34+
35+
If called inline when handling a `QUIC_STREAM_EVENT_RECEIVE_BUFFER_NEEDED` event, the provided
36+
memory buffers will be used to store the received data.
3237
3338
# See also
3439

src/core/crypto.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ QuicCryptoProcessDataFrame(
12341234
{
12351235
QUIC_STATUS Status;
12361236
QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
1237-
uint64_t FlowControlLimit = UINT16_MAX;
1237+
uint64_t FlowControlQuota = UINT16_MAX;
12381238

12391239
*DataReady = FALSE;
12401240

@@ -1267,14 +1267,18 @@ QuicCryptoProcessDataFrame(
12671267
// Write the received data (could be duplicate) to the stream buffer. The
12681268
// stream buffer will indicate if there is data to process.
12691269
//
1270+
uint64_t BufferSizeNeeded = 0;
12701271
Status =
12711272
QuicRecvBufferWrite(
12721273
&Crypto->RecvBuffer,
12731274
Crypto->RecvEncryptLevelStartOffset + Frame->Offset,
12741275
(uint16_t)Frame->Length,
12751276
Frame->Data,
1276-
&FlowControlLimit,
1277-
DataReady);
1277+
FlowControlQuota,
1278+
&FlowControlQuota,
1279+
DataReady,
1280+
&BufferSizeNeeded);
1281+
CXPLAT_DBG_ASSERT(BufferSizeNeeded == 0);
12781282
if (QUIC_FAILED(Status)) {
12791283
if (Status == QUIC_STATUS_BUFFER_TOO_SMALL) {
12801284
QuicTraceEvent(

0 commit comments

Comments
 (0)