Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Unix]: AcceptAsync with existing AcceptSocket support on Unix #73499

Merged
merged 7 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public SafeSocketHandle(IntPtr preexistingHandle, bool ownsHandle)

internal bool OwnsHandle { get; }

internal bool HasShutdownSend => _hasShutdownSend;

private bool TryOwnClose()
{
return OwnsHandle && Interlocked.CompareExchange(ref _ownClose, 1, 0) == 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Threading.Tasks;
using System.Runtime.Versioning;
using Microsoft.Win32.SafeHandles;
using System.Reflection;
using System.Collections;

namespace System.Net.Sockets
{
Expand Down Expand Up @@ -166,16 +168,20 @@ private static void ThrowMultiConnectNotSupported()
}

#pragma warning disable CA1822
private Socket? GetOrCreateAcceptSocket(Socket? acceptSocket, bool unused, string propertyName, out SafeSocketHandle? handle)
private Socket? GetOrCreateAcceptSocket(Socket? acceptSocket, bool checkDisconnected, string propertyName, out SafeSocketHandle? handle)
{
// AcceptSocket is not supported on Unix.
if (acceptSocket != null)
if (acceptSocket != null && acceptSocket._handle.HasShutdownSend)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_AcceptSocket);
throw new SocketException((int)SocketError.InvalidArgument);
}

if (acceptSocket != null && acceptSocket._rightEndPoint != null && (!checkDisconnected || !acceptSocket._isDisconnected))
liveans marked this conversation as resolved.
Show resolved Hide resolved
{
throw new InvalidOperationException(SR.Format(SR.net_sockets_namedmustnotbebound, propertyName));
}

handle = null;
return null;
return acceptSocket;
}
#pragma warning restore CA1822

Expand Down Expand Up @@ -228,5 +234,81 @@ private void SendFileInternal(string? fileName, ReadOnlySpan<byte> preBuffer, Re
Send(postBuffer);
}
}

internal void DisposeHandle()
{
_handle.Dispose();
}

internal void ClearHandle()
{
_handle = null!;
}

internal Socket CopyStateFromSource(Socket source)
{
_addressFamily = source._addressFamily;
_closeTimeout = source._closeTimeout;
_disposed = source._disposed;
_handle = source._handle;
_isConnected = source._isConnected;
_isDisconnected = source._isDisconnected;
_isListening = source._isListening;
_nonBlockingConnectInProgress = source._nonBlockingConnectInProgress;
_protocolType = source._protocolType;
_receivingPacketInformation = source._receivingPacketInformation;
_remoteEndPoint = source._remoteEndPoint;
_rightEndPoint = source._rightEndPoint;
_socketType = source._socketType;
_willBlock = source._willBlock;
_willBlockInternal = source._willBlockInternal;
_localEndPoint = source._localEndPoint;
_multiBufferReceiveEventArgs = source._multiBufferReceiveEventArgs;
_multiBufferSendEventArgs = source._multiBufferSendEventArgs;
_pendingConnectRightEndPoint = source._pendingConnectRightEndPoint;
_singleBufferReceiveEventArgs = source._singleBufferReceiveEventArgs;
#if DEBUG
// Try to detect if a property gets added that we're not copying correctly.
foreach (PropertyInfo pi in typeof(Socket).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
object? origValue = pi.GetValue(source);
object? cloneValue = pi.GetValue(this);

if (origValue is IEnumerable origEnumerable)
liveans marked this conversation as resolved.
Show resolved Hide resolved
{
IEnumerable? cloneEnumerable = cloneValue as IEnumerable;
Debug.Assert(cloneEnumerable != null, $"{pi.Name}. Expected enumerable cloned value.");

IEnumerator e1 = origEnumerable.GetEnumerator();
try
{
IEnumerator e2 = cloneEnumerable.GetEnumerator();
try
{
while (e1.MoveNext())
{
Debug.Assert(e2.MoveNext(), $"{pi.Name}. Cloned enumerator too short.");
Debug.Assert(Equals(e1.Current, e2.Current), $"{pi.Name}. Cloned enumerator's values don't match.");
}
Debug.Assert(!e2.MoveNext(), $"{pi.Name}. Cloned enumerator too long.");
}
finally
{
(e2 as IDisposable)?.Dispose();
}
}
finally
{
(e1 as IDisposable)?.Dispose();
}
}
else
{
Debug.Assert(Equals(origValue, cloneValue), $"{pi.Name}. Expected: {origValue}, Actual: {cloneValue}");
}
}
#endif
return this;
liveans marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,21 @@ internal void LogBuffer(int size)
private SocketError FinishOperationAccept(Internals.SocketAddress remoteSocketAddress)
{
System.Buffer.BlockCopy(_acceptBuffer!, 0, remoteSocketAddress.Buffer, 0, _acceptAddressBufferCount);
Socket? sukru = _acceptSocket;
liveans marked this conversation as resolved.
Show resolved Hide resolved
_acceptSocket = _currentSocket!.CreateAcceptSocket(
SocketPal.CreateSocket(_acceptedFileDescriptor),
_currentSocket._rightEndPoint!.Create(remoteSocketAddress));
if (sukru != null)
{
sukru.DisposeHandle();
sukru.CopyStateFromSource(_acceptSocket);
// We keep this socket to make clean-up.
Socket temp = _acceptSocket;
_acceptSocket = sukru;
temp.ClearHandle();
temp.Dispose();
liveans marked this conversation as resolved.
Show resolved Hide resolved
GC.SuppressFinalize(temp);
liveans marked this conversation as resolved.
Show resolved Hide resolved
}
return SocketError.Success;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ public async Task Accept_ConcurrentAcceptsAfterConnects_Success(int numberAccept

[OuterLoop]
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/1483", TestPlatforms.AnyUnix)]
public async Task Accept_WithTargetSocket_Success()
{
if (!SupportsAcceptIntoExistingSocket)
Expand All @@ -166,7 +165,6 @@ public async Task Accept_WithTargetSocket_Success()
}
}

[ActiveIssue("https://github.com/dotnet/runtime/issues/1483", TestPlatforms.AnyUnix)]
[OuterLoop]
[Theory]
[InlineData(false)]
Expand Down Expand Up @@ -221,7 +219,6 @@ public async Task Accept_WithTargetSocket_ReuseAfterDisconnect_Success(bool reus

[OuterLoop]
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/1483", TestPlatforms.AnyUnix)]
public async Task Accept_WithAlreadyBoundTargetSocket_Fails()
{
if (!SupportsAcceptIntoExistingSocket)
Expand All @@ -242,7 +239,6 @@ public async Task Accept_WithAlreadyBoundTargetSocket_Fails()

[OuterLoop]
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/1483", TestPlatforms.AnyUnix)]
public async Task Accept_WithInUseTargetSocket_Fails()
{
if (!SupportsAcceptIntoExistingSocket)
Expand Down