From 48a6f94913b7182689956c9bd0fb0f55367b3efb Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 19 May 2020 17:14:14 +0200 Subject: [PATCH 1/7] SocketAsyncContext.Unix: remove Lock from IsReady --- .../src/System.Net.Sockets.csproj | 1 + .../Net/Sockets/SocketAsyncContext.Unix.cs | 61 ++++++++++--------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index 3661f2bfafeda9..e47be94e52afac 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -314,5 +314,6 @@ + diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index a0479bfc336298..edcb132a277f61 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -742,22 +742,24 @@ public void Init() _sequenceNumber = 0; } - // IsReady returns the current _sequenceNumber, which must be passed to StartAsyncOperation below. - public bool IsReady(SocketAsyncContext context, out int observedSequenceNumber) + // IsReady returns whether an operation can be executed immediately without queueing it. + // When IsReady returns true, the observedSequenceNumber is set. + // This ensures the sequence number is read, before executing the operation. + // When the operation can't complete, StartAsyncOperation compares against the current + // sequence number to see if the operation should be tried again. + public bool IsReady(SocketAsyncContext context, out int? observedSequenceNumber) { - using (Lock()) - { - observedSequenceNumber = _sequenceNumber; - bool isReady = (_state == QueueState.Ready) || (_state == QueueState.Stopped); + QueueState state = (QueueState)Volatile.Read(ref Unsafe.As(ref _state)); + bool isReady = state == QueueState.Ready || state == QueueState.Stopped; + observedSequenceNumber = isReady ? Volatile.Read(ref _sequenceNumber) : default; - Trace(context, $"{isReady}"); + Trace(context, $"{isReady}"); - return isReady; - } + return isReady; } // Return true for pending, false for completed synchronously (including failure and abort) - public bool StartAsyncOperation(SocketAsyncContext context, TOperation operation, int observedSequenceNumber, CancellationToken cancellationToken = default) + public bool StartAsyncOperation(SocketAsyncContext context, TOperation operation, int? observedSequenceNumber, CancellationToken cancellationToken = default) { Trace(context, $"Enter"); @@ -1100,6 +1102,7 @@ public void CancelAndContinueProcessing(TOperation op) if (_tail == null) { _state = QueueState.Ready; + _sequenceNumber++; } } } @@ -1261,7 +1264,7 @@ public void SetNonBlocking() } } - private void PerformSyncOperation(ref OperationQueue queue, TOperation operation, int timeout, int observedSequenceNumber) + private void PerformSyncOperation(ref OperationQueue queue, TOperation operation, int timeout, int? observedSequenceNumber) where TOperation : AsyncOperation { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); @@ -1343,7 +1346,7 @@ public SocketError Accept(byte[] socketAddress, ref int socketAddressLen, out In Debug.Assert(socketAddressLen > 0, $"Unexpected socketAddressLen: {socketAddressLen}"); SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) { @@ -1373,7 +1376,7 @@ public SocketError AcceptAsync(byte[] socketAddress, ref int socketAddressLen, o SetNonBlocking(); SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) { @@ -1411,7 +1414,7 @@ public SocketError Connect(byte[] socketAddress, int socketAddressLen) // before we try to complete it via epoll notification. // Thus, always call TryStartConnect regardless of readiness. SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; _sendQueue.IsReady(this, out observedSequenceNumber); if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode)) { @@ -1442,7 +1445,7 @@ public SocketError ConnectAsync(byte[] socketAddress, int socketAddressLen, Acti // We need to initiate the connect before we try to complete it. // Thus, always call TryStartConnect regardless of readiness. SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; _sendQueue.IsReady(this, out observedSequenceNumber); if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode)) { @@ -1489,7 +1492,7 @@ public SocketError ReceiveFrom(Memory buffer, ref SocketFlags flags, byte[ SocketFlags receivedFlags; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveFrom(_socket, buffer.Span, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1517,7 +1520,7 @@ public unsafe SocketError ReceiveFrom(Span buffer, ref SocketFlags flags, { SocketFlags receivedFlags; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveFrom(_socket, buffer, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1550,7 +1553,7 @@ public SocketError ReceiveFromAsync(Memory buffer, SocketFlags flags, byt SetNonBlocking(); SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteReceiveFrom(_socket, buffer.Span, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { @@ -1596,7 +1599,7 @@ public SocketError ReceiveFrom(IList> buffers, ref SocketFlag SocketFlags receivedFlags; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1626,7 +1629,7 @@ public SocketError ReceiveFromAsync(IList> buffers, SocketFla SetNonBlocking(); SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { @@ -1664,7 +1667,7 @@ public SocketError ReceiveMessageFrom( SocketFlags receivedFlags; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveMessageFrom(_socket, buffer.Span, buffers, flags, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1698,7 +1701,7 @@ public SocketError ReceiveMessageFromAsync(Memory buffer, IList buffer, SocketFlags flags, b bytesSent = 0; SocketError errorCode; int bufferIndexIgnored = 0, offset = 0, count = buffer.Length; - int observedSequenceNumber; + int? observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteSendTo(_socket, buffer, null, ref bufferIndexIgnored, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1818,7 +1821,7 @@ public SocketError SendToAsync(Memory buffer, int offset, int count, Socke bytesSent = 0; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteSendTo(_socket, buffer.Span, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { @@ -1866,7 +1869,7 @@ public SocketError SendTo(IList> buffers, SocketFlags flags, int bufferIndex = 0; int offset = 0; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1899,7 +1902,7 @@ public SocketError SendToAsync(IList> buffers, SocketFlags fl int bufferIndex = 0; int offset = 0; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { @@ -1934,7 +1937,7 @@ public SocketError SendFile(SafeFileHandle fileHandle, long offset, long count, bytesSent = 0; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1962,7 +1965,7 @@ public SocketError SendFileAsync(SafeFileHandle fileHandle, long offset, long co bytesSent = 0; SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode)) { From 4604d723e49657c533f8d0206e8c51581fd73153 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 19 May 2020 20:16:07 +0200 Subject: [PATCH 2/7] Make QueueState an int --- .../src/System/Net/Sockets/SocketAsyncContext.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index edcb132a277f61..16564994db8807 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -699,7 +699,7 @@ private struct OperationQueue // If we successfully process all enqueued operations, then the state becomes Ready; // otherwise, the state becomes Waiting and we wait for another epoll notification. - private enum QueueState : byte + private enum QueueState : int { Ready = 0, // Indicates that data MAY be available on the socket. // Queue must be empty. From b8bcfd08aa8ad8be7a844db3a805f151b56db825 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Wed, 20 May 2020 21:12:22 +0200 Subject: [PATCH 3/7] Update comment --- .../src/System/Net/Sockets/SocketAsyncContext.Unix.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index 16564994db8807..524b5987ecde43 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -743,12 +743,15 @@ public void Init() } // IsReady returns whether an operation can be executed immediately without queueing it. - // When IsReady returns true, the observedSequenceNumber is set. - // This ensures the sequence number is read, before executing the operation. - // When the operation can't complete, StartAsyncOperation compares against the current - // sequence number to see if the operation should be tried again. + // observedSequenceNumber must be passed to StartAsyncOperation. public bool IsReady(SocketAsyncContext context, out int? observedSequenceNumber) { + // It is safe to read _state and _sequence without using Lock. + // - The return value is soly based on Volatile.Read of _state. + // - The Volatile.Read of _sequenceNumber ensures we read a value before executing the operation. + // This is needed to retry the operation in StartAsyncOperation in case the _sequenceNumber incremented. + // - Because no Lock is taken, it is possible we observe a sequence number increment before the state + // becomes Ready. When that happens, observedSequenceNumber is null, and StartAsyncOperation will execute the operation. QueueState state = (QueueState)Volatile.Read(ref Unsafe.As(ref _state)); bool isReady = state == QueueState.Ready || state == QueueState.Stopped; observedSequenceNumber = isReady ? Volatile.Read(ref _sequenceNumber) : default; From bdb78f773ad29749d666b6197d1b46310f02a93f Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Wed, 20 May 2020 21:16:03 +0200 Subject: [PATCH 4/7] cleanup comment --- .../src/System/Net/Sockets/SocketAsyncContext.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index 524b5987ecde43..efc1d9f686529e 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -742,7 +742,7 @@ public void Init() _sequenceNumber = 0; } - // IsReady returns whether an operation can be executed immediately without queueing it. + // IsReady returns whether an operation can be executed immediately. // observedSequenceNumber must be passed to StartAsyncOperation. public bool IsReady(SocketAsyncContext context, out int? observedSequenceNumber) { From b436b1b953bb31a878797f68b850065c10094cc6 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Thu, 21 May 2020 20:45:58 +0200 Subject: [PATCH 5/7] Update for rebase --- .../src/System/Net/Sockets/SocketAsyncContext.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index e9bcb27848b29f..b3eca64c7c7e46 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -1567,7 +1567,7 @@ public SocketError ReceiveAsync(Memory buffer, SocketFlags flags, out int SetNonBlocking(); SocketError errorCode; - int observedSequenceNumber; + int? observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteReceive(_socket, buffer.Span, flags, out bytesReceived, out errorCode)) { From a06db65e3cfcf0c126a6e2ea362a38af435fc40c Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Fri, 5 Jun 2020 14:19:40 +0200 Subject: [PATCH 6/7] Decrement sequenceNumber instead of using nullable int --- .../Net/Sockets/SocketAsyncContext.Unix.cs | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index b3eca64c7c7e46..86662a725426ae 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -755,17 +755,23 @@ public void Init() // IsReady returns whether an operation can be executed immediately. // observedSequenceNumber must be passed to StartAsyncOperation. - public bool IsReady(SocketAsyncContext context, out int? observedSequenceNumber) + public bool IsReady(SocketAsyncContext context, out int observedSequenceNumber) { // It is safe to read _state and _sequence without using Lock. // - The return value is soly based on Volatile.Read of _state. // - The Volatile.Read of _sequenceNumber ensures we read a value before executing the operation. // This is needed to retry the operation in StartAsyncOperation in case the _sequenceNumber incremented. // - Because no Lock is taken, it is possible we observe a sequence number increment before the state - // becomes Ready. When that happens, observedSequenceNumber is null, and StartAsyncOperation will execute the operation. + // becomes Ready. When that happens, observedSequenceNumber is decremented, and StartAsyncOperation will + // execute the operation because the sequence number won't match. QueueState state = (QueueState)Volatile.Read(ref Unsafe.As(ref _state)); + observedSequenceNumber = Volatile.Read(ref _sequenceNumber); + bool isReady = state == QueueState.Ready || state == QueueState.Stopped; - observedSequenceNumber = isReady ? Volatile.Read(ref _sequenceNumber) : default; + if (!isReady) + { + observedSequenceNumber--; + } Trace(context, $"{isReady}"); @@ -773,7 +779,7 @@ public bool IsReady(SocketAsyncContext context, out int? observedSequenceNumber) } // Return true for pending, false for completed synchronously (including failure and abort) - public bool StartAsyncOperation(SocketAsyncContext context, TOperation operation, int? observedSequenceNumber, CancellationToken cancellationToken = default) + public bool StartAsyncOperation(SocketAsyncContext context, TOperation operation, int observedSequenceNumber, CancellationToken cancellationToken = default) { Trace(context, $"Enter"); @@ -1278,7 +1284,7 @@ public void SetNonBlocking() } } - private void PerformSyncOperation(ref OperationQueue queue, TOperation operation, int timeout, int? observedSequenceNumber) + private void PerformSyncOperation(ref OperationQueue queue, TOperation operation, int timeout, int observedSequenceNumber) where TOperation : AsyncOperation { Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); @@ -1360,7 +1366,7 @@ public SocketError Accept(byte[] socketAddress, ref int socketAddressLen, out In Debug.Assert(socketAddressLen > 0, $"Unexpected socketAddressLen: {socketAddressLen}"); SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) { @@ -1390,7 +1396,7 @@ public SocketError AcceptAsync(byte[] socketAddress, ref int socketAddressLen, o SetNonBlocking(); SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode)) { @@ -1428,7 +1434,7 @@ public SocketError Connect(byte[] socketAddress, int socketAddressLen) // before we try to complete it via epoll notification. // Thus, always call TryStartConnect regardless of readiness. SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; _sendQueue.IsReady(this, out observedSequenceNumber); if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode)) { @@ -1459,7 +1465,7 @@ public SocketError ConnectAsync(byte[] socketAddress, int socketAddressLen, Acti // We need to initiate the connect before we try to complete it. // Thus, always call TryStartConnect regardless of readiness. SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; _sendQueue.IsReady(this, out observedSequenceNumber); if (SocketPal.TryStartConnect(_socket, socketAddress, socketAddressLen, out errorCode)) { @@ -1506,7 +1512,7 @@ public SocketError ReceiveFrom(Memory buffer, ref SocketFlags flags, byte[ SocketFlags receivedFlags; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveFrom(_socket, buffer.Span, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1534,7 +1540,7 @@ public unsafe SocketError ReceiveFrom(Span buffer, ref SocketFlags flags, { SocketFlags receivedFlags; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveFrom(_socket, buffer, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1567,7 +1573,7 @@ public SocketError ReceiveAsync(Memory buffer, SocketFlags flags, out int SetNonBlocking(); SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteReceive(_socket, buffer.Span, flags, out bytesReceived, out errorCode)) { @@ -1600,7 +1606,7 @@ public SocketError ReceiveFromAsync(Memory buffer, SocketFlags flags, byt SetNonBlocking(); SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteReceiveFrom(_socket, buffer.Span, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { @@ -1647,7 +1653,7 @@ public SocketError ReceiveFrom(IList> buffers, ref SocketFlag SocketFlags receivedFlags; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1677,7 +1683,7 @@ public SocketError ReceiveFromAsync(IList> buffers, SocketFla SetNonBlocking(); SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode)) { @@ -1715,7 +1721,7 @@ public SocketError ReceiveMessageFrom( SocketFlags receivedFlags; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_receiveQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteReceiveMessageFrom(_socket, buffer.Span, buffers, flags, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1749,7 +1755,7 @@ public SocketError ReceiveMessageFromAsync(Memory buffer, IList buffer, SocketFlags flags, b bytesSent = 0; SocketError errorCode; int bufferIndexIgnored = 0, offset = 0, count = buffer.Length; - int? observedSequenceNumber; + int observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteSendTo(_socket, buffer, null, ref bufferIndexIgnored, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1869,7 +1875,7 @@ public SocketError SendToAsync(Memory buffer, int offset, int count, Socke bytesSent = 0; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteSendTo(_socket, buffer.Span, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { @@ -1917,7 +1923,7 @@ public SocketError SendTo(IList> buffers, SocketFlags flags, int bufferIndex = 0; int offset = 0; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -1950,7 +1956,7 @@ public SocketError SendToAsync(IList> buffers, SocketFlags fl int bufferIndex = 0; int offset = 0; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode)) { @@ -1985,7 +1991,7 @@ public SocketError SendFile(SafeFileHandle fileHandle, long offset, long count, bytesSent = 0; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && (SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode) || !ShouldRetrySyncOperation(out errorCode))) @@ -2013,7 +2019,7 @@ public SocketError SendFileAsync(SafeFileHandle fileHandle, long offset, long co bytesSent = 0; SocketError errorCode; - int? observedSequenceNumber; + int observedSequenceNumber; if (_sendQueue.IsReady(this, out observedSequenceNumber) && SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode)) { From 44009202f38d0c5f0ebcdc6681fa3be53337457c Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 9 Jun 2020 09:51:20 +0200 Subject: [PATCH 7/7] PR feedback --- .../src/System/Net/Sockets/SocketAsyncContext.Unix.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index 86662a725426ae..0df52bb456a2de 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -758,12 +758,14 @@ public void Init() public bool IsReady(SocketAsyncContext context, out int observedSequenceNumber) { // It is safe to read _state and _sequence without using Lock. - // - The return value is soly based on Volatile.Read of _state. + // - The return value is soley based on Volatile.Read of _state. // - The Volatile.Read of _sequenceNumber ensures we read a value before executing the operation. // This is needed to retry the operation in StartAsyncOperation in case the _sequenceNumber incremented. // - Because no Lock is taken, it is possible we observe a sequence number increment before the state // becomes Ready. When that happens, observedSequenceNumber is decremented, and StartAsyncOperation will // execute the operation because the sequence number won't match. + + Debug.Assert(sizeof(QueueState) == sizeof(int)); QueueState state = (QueueState)Volatile.Read(ref Unsafe.As(ref _state)); observedSequenceNumber = Volatile.Read(ref _sequenceNumber);