@@ -219,6 +219,9 @@ private unsafe SocketError ProcessIOCPResult(bool success, int bytesTransferred,
219
219
}
220
220
221
221
// We've finished setting up and launching the operation. Coordinate with the callback.
222
+ // The expectation is that in the majority of cases either the operation will have completed
223
+ // synchronously (in which case we won't be here) or the operation will complete asynchronously
224
+ // and this function will typically win the race condition with the callback.
222
225
ulong packedResult = Interlocked . Exchange ( ref _asyncCompletionOwnership , 1 ) ;
223
226
if ( packedResult == 0 )
224
227
{
@@ -1223,19 +1226,26 @@ private void FinishOperationSendPackets()
1223
1226
Debug . Assert ( saeaBox . Value != null ) ;
1224
1227
SocketAsyncEventArgs saea = saeaBox . Value ;
1225
1228
1226
- // Pack the error code and number of bytes transferred into a single ulong we can store into
1227
- // _asyncCompletionOwnership. If the field was already set by the launcher, the value won't
1228
- // be needed, but if this callback wins the race condition and transfers ownership to the
1229
- // launcher to handle completion and clean up, transfering these values over prevents needing
1230
- // to make an additional call to WSAGetOverlappedResult.
1231
- Debug . Assert ( numBytes <= int . MaxValue , "We rely on being able to set the top bit to ensure the whole packed result isn't 0." ) ;
1232
- ulong packedResult = ( 1ul << 63 ) | ( ( ulong ) numBytes << 32 ) | errorCode ;
1233
-
1234
- if ( Interlocked . Exchange ( ref saea . _asyncCompletionOwnership , packedResult ) == 0 )
1229
+ // We need to coordinate with the launching thread, just in case it hasn't yet finished setting up the operation.
1230
+ // We typically expect the launching thread to have already completed setup, in which case _asyncCompletionOwnership
1231
+ // will be 1, so we do a fast non-synchronized check to see if it's still 0, and only if it is do we proceed to
1232
+ // pack the results for use with an interlocked coordination with that thread.
1233
+ if ( saea . _asyncCompletionOwnership == 0 )
1235
1234
{
1236
- // The operation completed asynchronously so quickly that the thread launching the operation still hasn't finished setting
1237
- // up the state for the operation. Leave all cleanup and completion logic to that thread.
1238
- return ;
1235
+ // Pack the error code and number of bytes transferred into a single ulong we can store into
1236
+ // _asyncCompletionOwnership. If the field was already set by the launcher, the value won't
1237
+ // be needed, but if this callback wins the race condition and transfers ownership to the
1238
+ // launcher to handle completion and clean up, transfering these values over prevents needing
1239
+ // to make an additional call to WSAGetOverlappedResult.
1240
+ Debug . Assert ( numBytes <= int . MaxValue , "We rely on being able to set the top bit to ensure the whole packed result isn't 0." ) ;
1241
+ ulong packedResult = ( 1ul << 63 ) | ( ( ulong ) numBytes << 32 ) | errorCode ;
1242
+
1243
+ if ( Interlocked . Exchange ( ref saea . _asyncCompletionOwnership , packedResult ) == 0 )
1244
+ {
1245
+ // The operation completed asynchronously so quickly that the thread launching the operation still hasn't finished setting
1246
+ // up the state for the operation. Leave all cleanup and completion logic to that thread.
1247
+ return ;
1248
+ }
1239
1249
}
1240
1250
1241
1251
// This callback owns the completion and cleanup for the operation.
0 commit comments