@@ -28,7 +28,8 @@ internal sealed class SocketConnection : TransportConnection
28
28
private readonly object _shutdownLock = new object ( ) ;
29
29
private volatile bool _socketDisposed ;
30
30
private volatile Exception ? _shutdownReason ;
31
- private Task ? _processingTask ;
31
+ private Task ? _sendingTask ;
32
+ private Task ? _receivingTask ;
32
33
private readonly TaskCompletionSource _waitForConnectionClosedTcs = new TaskCompletionSource ( ) ;
33
34
private bool _connectionClosed ;
34
35
private readonly bool _waitForData ;
@@ -78,28 +79,16 @@ internal SocketConnection(Socket socket,
78
79
public override MemoryPool < byte > MemoryPool { get ; }
79
80
80
81
public void Start ( )
81
- {
82
- _processingTask = StartAsync ( ) ;
83
- }
84
-
85
- private async Task StartAsync ( )
86
82
{
87
83
try
88
84
{
89
85
// Spawn send and receive logic
90
- var receiveTask = DoReceive ( ) ;
91
- var sendTask = DoSend ( ) ;
92
-
93
- // Now wait for both to complete
94
- await receiveTask ;
95
- await sendTask ;
96
-
97
- _receiver . Dispose ( ) ;
98
- _sender ? . Dispose ( ) ;
86
+ _receivingTask = DoReceive ( ) ;
87
+ _sendingTask = DoSend ( ) ;
99
88
}
100
89
catch ( Exception ex )
101
90
{
102
- _trace . LogError ( 0 , ex , $ "Unexpected exception in { nameof ( SocketConnection ) } .{ nameof ( StartAsync ) } .") ;
91
+ _trace . LogError ( 0 , ex , $ "Unexpected exception in { nameof ( SocketConnection ) } .{ nameof ( Start ) } .") ;
103
92
}
104
93
}
105
94
@@ -118,9 +107,28 @@ public override async ValueTask DisposeAsync()
118
107
_originalTransport . Input . Complete ( ) ;
119
108
_originalTransport . Output . Complete ( ) ;
120
109
121
- if ( _processingTask != null )
110
+ try
111
+ {
112
+ // Now wait for both to complete
113
+ if ( _receivingTask != null )
114
+ {
115
+ await _receivingTask ;
116
+ }
117
+
118
+ if ( _sendingTask != null )
119
+ {
120
+ await _sendingTask ;
121
+ }
122
+
123
+ }
124
+ catch ( Exception ex )
125
+ {
126
+ _trace . LogError ( 0 , ex , $ "Unexpected exception in { nameof ( SocketConnection ) } .{ nameof ( Start ) } .") ;
127
+ }
128
+ finally
122
129
{
123
- await _processingTask ;
130
+ _receiver . Dispose ( ) ;
131
+ _sender ? . Dispose ( ) ;
124
132
}
125
133
126
134
_connectionClosedTokenSource . Dispose ( ) ;
@@ -132,7 +140,50 @@ private async Task DoReceive()
132
140
133
141
try
134
142
{
135
- await ProcessReceives ( ) ;
143
+ while ( true )
144
+ {
145
+ if ( _waitForData )
146
+ {
147
+ // Wait for data before allocating a buffer.
148
+ await _receiver . WaitForDataAsync ( _socket ) ;
149
+ }
150
+
151
+ // Ensure we have some reasonable amount of buffer space
152
+ var buffer = Input . GetMemory ( MinAllocBufferSize ) ;
153
+
154
+ var bytesReceived = await _receiver . ReceiveAsync ( _socket , buffer ) ;
155
+
156
+ if ( bytesReceived == 0 )
157
+ {
158
+ // FIN
159
+ _trace . ConnectionReadFin ( ConnectionId ) ;
160
+ break ;
161
+ }
162
+
163
+ Input . Advance ( bytesReceived ) ;
164
+
165
+ var flushTask = Input . FlushAsync ( ) ;
166
+
167
+ var paused = ! flushTask . IsCompleted ;
168
+
169
+ if ( paused )
170
+ {
171
+ _trace . ConnectionPause ( ConnectionId ) ;
172
+ }
173
+
174
+ var result = await flushTask ;
175
+
176
+ if ( paused )
177
+ {
178
+ _trace . ConnectionResume ( ConnectionId ) ;
179
+ }
180
+
181
+ if ( result . IsCompleted || result . IsCanceled )
182
+ {
183
+ // Pipe consumer is shut down, do we stop writing
184
+ break ;
185
+ }
186
+ }
136
187
}
137
188
catch ( SocketException ex ) when ( IsConnectionResetError ( ex . SocketErrorCode ) )
138
189
{
@@ -176,64 +227,40 @@ private async Task DoReceive()
176
227
}
177
228
}
178
229
179
- private async Task ProcessReceives ( )
180
- {
181
- // Resolve `input` PipeWriter via the IDuplexPipe interface prior to loop start for performance.
182
- var input = Input ;
183
- while ( true )
184
- {
185
- if ( _waitForData )
186
- {
187
- // Wait for data before allocating a buffer.
188
- await _receiver . WaitForDataAsync ( _socket ) ;
189
- }
190
-
191
- // Ensure we have some reasonable amount of buffer space
192
- var buffer = input . GetMemory ( MinAllocBufferSize ) ;
193
-
194
- var bytesReceived = await _receiver . ReceiveAsync ( _socket , buffer ) ;
195
-
196
- if ( bytesReceived == 0 )
197
- {
198
- // FIN
199
- _trace . ConnectionReadFin ( ConnectionId ) ;
200
- break ;
201
- }
202
-
203
- input . Advance ( bytesReceived ) ;
204
-
205
- var flushTask = input . FlushAsync ( ) ;
206
-
207
- var paused = ! flushTask . IsCompleted ;
208
-
209
- if ( paused )
210
- {
211
- _trace . ConnectionPause ( ConnectionId ) ;
212
- }
213
-
214
- var result = await flushTask ;
215
-
216
- if ( paused )
217
- {
218
- _trace . ConnectionResume ( ConnectionId ) ;
219
- }
220
-
221
- if ( result . IsCompleted || result . IsCanceled )
222
- {
223
- // Pipe consumer is shut down, do we stop writing
224
- break ;
225
- }
226
- }
227
- }
228
-
229
230
private async Task DoSend ( )
230
231
{
231
232
Exception ? shutdownReason = null ;
232
233
Exception ? unexpectedError = null ;
233
234
234
235
try
235
236
{
236
- await ProcessSends ( ) ;
237
+ while ( true )
238
+ {
239
+ var result = await Output . ReadAsync ( ) ;
240
+
241
+ if ( result . IsCanceled )
242
+ {
243
+ break ;
244
+ }
245
+ var buffer = result . Buffer ;
246
+
247
+ if ( ! buffer . IsEmpty )
248
+ {
249
+ _sender = _socketSenderPool . Rent ( ) ;
250
+ await _sender . SendAsync ( _socket , buffer ) ;
251
+ // We don't return to the pool if there was an exception, and
252
+ // we keep the _sender assigned so that we can dispose it in StartAsync.
253
+ _socketSenderPool . Return ( _sender ) ;
254
+ _sender = null ;
255
+ }
256
+
257
+ Output . AdvanceTo ( buffer . End ) ;
258
+
259
+ if ( result . IsCompleted )
260
+ {
261
+ break ;
262
+ }
263
+ }
237
264
}
238
265
catch ( SocketException ex ) when ( IsConnectionResetError ( ex . SocketErrorCode ) )
239
266
{
@@ -265,42 +292,6 @@ private async Task DoSend()
265
292
}
266
293
}
267
294
268
- private async Task ProcessSends ( )
269
- {
270
- // Resolve `output` PipeReader via the IDuplexPipe interface prior to loop start for performance.
271
- var output = Output ;
272
- while ( true )
273
- {
274
- var result = await output . ReadAsync ( ) ;
275
-
276
- if ( result . IsCanceled )
277
- {
278
- break ;
279
- }
280
-
281
- var buffer = result . Buffer ;
282
-
283
- var end = buffer . End ;
284
- var isCompleted = result . IsCompleted ;
285
- if ( ! buffer . IsEmpty )
286
- {
287
- _sender = _socketSenderPool . Rent ( ) ;
288
- await _sender . SendAsync ( _socket , buffer ) ;
289
- // We don't return to the pool if there was an exception, and
290
- // we keep the _sender assigned so that we can dispose it in StartAsync.
291
- _socketSenderPool . Return ( _sender ) ;
292
- _sender = null ;
293
- }
294
-
295
- output . AdvanceTo ( end ) ;
296
-
297
- if ( isCompleted )
298
- {
299
- break ;
300
- }
301
- }
302
- }
303
-
304
295
private void FireConnectionClosed ( )
305
296
{
306
297
// Guard against scheduling this multiple times
0 commit comments