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

avoid allocating SocketAddress GCHandle for every ReceiveFromAsync #89808

Merged
merged 4 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 0 additions & 24 deletions src/libraries/Common/src/System/Net/SocketAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,6 @@ public SocketAddress(AddressFamily family, int size)
ArgumentOutOfRangeException.ThrowIfLessThan(size, MinSize);

InternalSize = size;
#if !SYSTEM_NET_PRIMITIVES_DLL && WINDOWS
// WSARecvFrom needs a pinned pointer to the 32bit socket address size: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsarecvfrom
// Allocate IntPtr.Size extra bytes at the end of Buffer ensuring IntPtr.Size alignment, so we don't need to pin anything else.
// The following formula will extend 'size' to the alignment boundary then add IntPtr.Size more bytes.
size = (size + IntPtr.Size - 1) / IntPtr.Size * IntPtr.Size + IntPtr.Size;
#endif
InternalBuffer = new byte[size];
InternalBuffer[0] = (byte)InternalSize;

Expand Down Expand Up @@ -197,24 +191,6 @@ internal IPEndPoint GetIPEndPoint()
return new IPEndPoint(GetIPAddress(), GetPort());
}

#if !SYSTEM_NET_PRIMITIVES_DLL && WINDOWS
// For ReceiveFrom we need to pin address size, using reserved Buffer space.
internal void CopyAddressSizeIntoBuffer()
{
int addressSizeOffset = GetAddressSizeOffset();
InternalBuffer[addressSizeOffset] = unchecked((byte)(InternalSize));
InternalBuffer[addressSizeOffset + 1] = unchecked((byte)(InternalSize >> 8));
InternalBuffer[addressSizeOffset + 2] = unchecked((byte)(InternalSize >> 16));
InternalBuffer[addressSizeOffset + 3] = unchecked((byte)(InternalSize >> 24));
}

// Can be called after the above method did work.
internal int GetAddressSizeOffset()
{
return InternalBuffer.Length - IntPtr.Size;
}
#endif

public override bool Equals(object? comparand) =>
comparand is SocketAddress other && Equals(other);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,8 @@ public partial class SocketAsyncEventArgs : EventArgs, IDisposable
private byte[]? _controlBufferPinned;
private WSABuffer[]? _wsaRecvMsgWSABufferArrayPinned;

// Internal SocketAddress buffer
private GCHandle _socketAddressGCHandle;
private Internals.SocketAddress? _pinnedSocketAddress;
// SocketAddress buffer
private IntPtr SocketAddressNativeMemory;
wfurt marked this conversation as resolved.
Show resolved Hide resolved

// SendPacketsElements property variables.
private SafeFileHandle[]? _sendPacketsFileHandles;
Expand Down Expand Up @@ -434,8 +433,8 @@ internal unsafe SocketError DoOperationReceiveFromSingleBuffer(SafeSocketHandle
1,
out int bytesTransferred,
ref flags,
PtrSocketAddressBuffer,
PtrSocketAddressBufferSize,
PtrSocketAddressBuffer(),
PtrSocketAddressSize(),
overlapped,
IntPtr.Zero);

Expand Down Expand Up @@ -463,8 +462,8 @@ internal unsafe SocketError DoOperationReceiveFromMultiBuffer(SafeSocketHandle h
_bufferListInternal!.Count,
out int bytesTransferred,
ref flags,
PtrSocketAddressBuffer,
PtrSocketAddressBufferSize,
PtrSocketAddressBuffer(),
PtrSocketAddressSize(),
overlapped,
IntPtr.Zero);

Expand Down Expand Up @@ -538,8 +537,8 @@ SocketError Core()
{
// Fill in WSAMessageBuffer.
Interop.Winsock.WSAMsg* pMessage = (Interop.Winsock.WSAMsg*)Marshal.UnsafeAddrOfPinnedArrayElement(_wsaMessageBufferPinned, 0);
pMessage->socketAddress = PtrSocketAddressBuffer;
pMessage->addressLength = (uint)_socketAddress.Size;
pMessage->socketAddress = PtrSocketAddressBuffer();
pMessage->addressLength = (uint)SocketAddress.GetMaximumAddressSize(_socketAddress!.Family);
fixed (void* ptrWSARecvMsgWSABufferArray = &wsaRecvMsgWSABufferArray[0])
{
pMessage->buffers = (IntPtr)ptrWSARecvMsgWSABufferArray;
Expand Down Expand Up @@ -858,43 +857,30 @@ private void SetupMultipleBuffers()
}

// Ensures appropriate SocketAddress buffer is pinned.
private void PinSocketAddressBuffer()
private unsafe void PinSocketAddressBuffer()
wfurt marked this conversation as resolved.
Show resolved Hide resolved
{
// Check if already pinned.
if (_pinnedSocketAddress == _socketAddress)
{
return;
}
//_socketAddress!.Size = SocketAddress.GetMaximumAddressSize(_socketAddress!.Family);
int size = SocketAddress.GetMaximumAddressSize(_socketAddress!.Family);

// Unpin any existing.
if (_socketAddressGCHandle.IsAllocated)
if (SocketAddressNativeMemory == IntPtr.Zero)
{
_socketAddressGCHandle.Free();
SocketAddressNativeMemory = (IntPtr)NativeMemory.Alloc((uint)(_socketAddress!.Size + sizeof(IntPtr)));
}

// Pin down the new one.
_socketAddressGCHandle = GCHandle.Alloc(_socketAddress!.InternalBuffer, GCHandleType.Pinned);
_socketAddress.CopyAddressSizeIntoBuffer();
_pinnedSocketAddress = _socketAddress;
*((int*)SocketAddressNativeMemory) = size;
}

private unsafe IntPtr PtrSocketAddressBuffer
private unsafe IntPtr PtrSocketAddressBuffer()
{
get
{
Debug.Assert(_pinnedSocketAddress != null);
Debug.Assert(_pinnedSocketAddress.InternalBuffer != null);
Debug.Assert(_pinnedSocketAddress.InternalBuffer.Length > 0);
Debug.Assert(_socketAddressGCHandle.IsAllocated);
Debug.Assert(_socketAddressGCHandle.Target == _pinnedSocketAddress.InternalBuffer);
fixed (void* ptrSocketAddressBuffer = &_pinnedSocketAddress.InternalBuffer[0])
{
return (IntPtr)ptrSocketAddressBuffer;
}
}
Debug.Assert(SocketAddressNativeMemory != IntPtr.Zero);
return SocketAddressNativeMemory + sizeof(IntPtr);
}

private IntPtr PtrSocketAddressBufferSize => PtrSocketAddressBuffer + _socketAddress!.GetAddressSizeOffset();
private IntPtr PtrSocketAddressSize()
{
Debug.Assert(SocketAddressNativeMemory != IntPtr.Zero);
return SocketAddressNativeMemory;
}

// Cleans up any existing Overlapped object and related state variables.
private void FreeOverlapped()
Expand All @@ -909,7 +895,7 @@ private void FreeOverlapped()
}
}

private void FreePinHandles()
private unsafe void FreePinHandles()
{
_pinState = PinState.None;

Expand All @@ -922,10 +908,10 @@ private void FreePinHandles()
}
}

if (_socketAddressGCHandle.IsAllocated)
if (SocketAddressNativeMemory != IntPtr.Zero)
{
_socketAddressGCHandle.Free();
_pinnedSocketAddress = null;
NativeMemory.Free((void*)SocketAddressNativeMemory);
SocketAddressNativeMemory = IntPtr.Zero;
}

Debug.Assert(_singleBufferHandle.Equals(default(MemoryHandle)));
Expand Down Expand Up @@ -1133,7 +1119,14 @@ private unsafe SocketError FinishOperationConnect()
}
}

private unsafe int GetSocketAddressSize() => *(int*)PtrSocketAddressBufferSize;
private unsafe int GetSocketAddressSize()
{
Debug.Assert(SocketAddressNativeMemory != IntPtr.Zero);
int size = *((int*)SocketAddressNativeMemory);
_socketAddress!.Size = size;
new Span<byte>((void*)PtrSocketAddressBuffer(), size).CopyTo(_socketAddress.Buffer.Span);
wfurt marked this conversation as resolved.
Show resolved Hide resolved
return size;
}

private void CompleteCore()
{
Expand Down