-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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 allocations in SendTo and Quic #78591
Closed
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
src/libraries/Common/src/Interop/BSD/System.Native/Interop.SocketAddress.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
[StructLayout(LayoutKind.Sequential)] | ||
internal unsafe struct sockaddr_in | ||
{ | ||
public byte sin_len; | ||
public byte sin_family; | ||
public ushort sin_port; | ||
public fixed byte sin_addr[4]; | ||
private fixed byte sin_zero[8]; | ||
|
||
public void SetAddressFamily(int family) | ||
{ | ||
sin_family = (byte)family; | ||
} | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal unsafe struct sockaddr_in6 | ||
{ | ||
public byte sin_len; | ||
public byte sin6_family; | ||
public ushort sin6_port; | ||
public uint sin6_flowinfo; | ||
public fixed byte sin6_addr[16]; | ||
public uint sin6_scope_id; | ||
|
||
public void SetAddressFamily(int family) | ||
{ | ||
sin6_family = (byte)family; | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/libraries/Common/src/Interop/FreeBSD/System.Native/Interop.SocketAddress.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
internal const int AF_INET = 2; | ||
internal const int AF_INET6 = 28; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/libraries/Common/src/Interop/Linux/System.Native/Interop.SocketAddress.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
internal const int AF_INET = 2; | ||
internal const int AF_INET6 = 10; | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal unsafe struct sockaddr_in | ||
{ | ||
public ushort sin_family; | ||
public ushort sin_port; | ||
public fixed byte sin_addr[4]; | ||
private fixed byte sin_zero[8]; | ||
|
||
public void SetAddressFamily(int family) | ||
{ | ||
sin_family = (ushort)family; | ||
} | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal unsafe struct sockaddr_in6 | ||
{ | ||
public ushort sin6_family; | ||
public ushort sin6_port; | ||
public uint sin6_flowinfo; | ||
public fixed byte sin6_addr[16]; | ||
public uint sin6_scope_id; | ||
|
||
public void SetAddressFamily(int family) | ||
{ | ||
sin6_family = (ushort)family; | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/libraries/Common/src/Interop/OSX/System.Native/Interop.SocketAddress.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
internal const int AF_INET = 2; | ||
internal const int AF_INET6 = 30; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/libraries/Common/src/Interop/Windows/WinSock/Interop.SocketAddress.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
internal const int AF_INET = 2; | ||
internal const int AF_INET6 = 23; | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal unsafe struct sockaddr_in | ||
{ | ||
public ushort sin_family; | ||
public ushort sin_port; | ||
public fixed byte sin_addr[4]; | ||
private fixed byte sin_zero[8]; | ||
|
||
public void SetAddressFamily(int family) | ||
{ | ||
sin_family = (ushort)family; | ||
} | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
internal unsafe struct sockaddr_in6 | ||
{ | ||
public ushort sin6_family; | ||
public ushort sin6_port; | ||
public uint sin6_flowinfo; | ||
public fixed byte sin6_addr[16]; | ||
public uint sin6_scope_id; | ||
|
||
public void SetAddressFamily(int family) | ||
{ | ||
sin6_family = (ushort)family; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Net.Sockets; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace System.Net.Sockets | ||
{ | ||
[StructLayout(LayoutKind.Explicit)] | ||
internal partial struct SockAddr | ||
{ | ||
internal const int IPv4AddressSize = 4; | ||
internal const int IPv6AddressSize = 16; | ||
|
||
private static ReadOnlySpan<byte> V4MappedPrefix=> new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; | ||
|
||
[FieldOffset(0)] | ||
public Interop.Sys.sockaddr_in Ipv4; | ||
[FieldOffset(0)] | ||
public Interop.Sys.sockaddr_in6 Ipv6; | ||
|
||
public int Family => Ipv4.sin_family; | ||
|
||
public unsafe SockAddr(IPEndPoint endPoint) : this(endPoint, endPoint.Address.AddressFamily) | ||
{ | ||
} | ||
|
||
public unsafe SockAddr(IPEndPoint endPoint, AddressFamily addressFamily) | ||
{ | ||
if (addressFamily == endPoint.Address.AddressFamily) | ||
{ | ||
if (endPoint.AddressFamily == AddressFamily.InterNetwork) | ||
{ | ||
Ipv4.sin_family = Interop.Sys.AF_INET; | ||
Ipv4.sin_port = (ushort)IPAddress.HostToNetworkOrder((short)endPoint.Port); | ||
Span<byte> address = MemoryMarshal.CreateSpan(ref Ipv4.sin_addr[0], IPv4AddressSize); | ||
if (!endPoint.Address.TryWriteBytes(address, out int bytesWritten) || bytesWritten != IPv4AddressSize) | ||
{ | ||
throw new SocketException((int)SocketError.InvalidArgument); | ||
} | ||
} | ||
else if (endPoint.AddressFamily == AddressFamily.InterNetworkV6) | ||
{ | ||
Ipv6.sin6_family = Interop.Sys.AF_INET6; | ||
Ipv6.sin6_port = (ushort)IPAddress.HostToNetworkOrder((short)endPoint.Port); | ||
Span<byte> address = MemoryMarshal.CreateSpan(ref Ipv6.sin6_addr[0], IPv6AddressSize); | ||
if (!endPoint.Address.TryWriteBytes(address, out int bytesWritten) || bytesWritten != IPv6AddressSize) | ||
{ | ||
throw new SocketException((int)SocketError.InvalidArgument); | ||
} | ||
} | ||
else | ||
{ | ||
// Next IP version??? | ||
throw new SocketException((int)SocketError.AddressFamilyNotSupported); | ||
} | ||
} | ||
else if (endPoint.Address.AddressFamily == AddressFamily.InterNetwork && addressFamily == AddressFamily.InterNetworkV6) | ||
{ | ||
// Map IPv4 to IPv4. This is currently only supported cases when AddressFamily does not match. | ||
Ipv6.sin6_family = Interop.Sys.AF_INET6; | ||
Ipv6.sin6_port = (ushort)IPAddress.HostToNetworkOrder((short)endPoint.Port); | ||
Span<byte> address = MemoryMarshal.CreateSpan(ref Ipv6.sin6_addr[0], IPv6AddressSize); | ||
|
||
V4MappedPrefix.CopyTo(address); | ||
if (!endPoint.Address.TryWriteBytes(address.Slice(V4MappedPrefix.Length), out int bytesWritten) || bytesWritten != IPv4AddressSize) | ||
{ | ||
throw new SocketException((int)SocketError.InvalidArgument); | ||
} | ||
} | ||
else | ||
{ | ||
throw new SocketException((int)SocketError.AddressFamilyNotSupported); | ||
} | ||
} | ||
|
||
public AddressFamily AddressFamily | ||
{ | ||
get | ||
{ | ||
switch (Family) | ||
{ | ||
case Interop.Sys.AF_INET: return AddressFamily.InterNetwork; | ||
case Interop.Sys.AF_INET6: return AddressFamily.InterNetworkV6; | ||
default: | ||
throw new SocketException((int)SocketError.AddressFamilyNotSupported); | ||
} | ||
} | ||
} | ||
|
||
public unsafe int Length | ||
{ | ||
get | ||
{ | ||
switch (Family) | ||
{ | ||
case Interop.Sys.AF_INET: return sizeof(Interop.Sys.sockaddr_in); | ||
case Interop.Sys.AF_INET6: return sizeof(Interop.Sys.sockaddr_in6); | ||
default: | ||
throw new SocketException((int)SocketError.AddressFamilyNotSupported); | ||
} | ||
} | ||
} | ||
|
||
public unsafe ReadOnlySpan<byte> AsBytes() | ||
{ | ||
return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref this, 1)).Slice(0, Length); | ||
} | ||
|
||
public ushort GetPort() | ||
{ | ||
if (AddressFamily == AddressFamily.InterNetworkV6) | ||
{ | ||
return (ushort)IPAddress.NetworkToHostOrder((short)Ipv6.sin6_port); | ||
} | ||
else if (AddressFamily == AddressFamily.InterNetwork) | ||
{ | ||
return (ushort)IPAddress.NetworkToHostOrder((short)Ipv4.sin_port); | ||
} | ||
else | ||
{ | ||
throw new SocketException((int)SocketError.AddressFamilyNotSupported); | ||
} | ||
} | ||
|
||
public unsafe IPAddress GetIPAddress() | ||
{ | ||
if (AddressFamily == AddressFamily.InterNetworkV6) | ||
{ | ||
return new IPAddress(MemoryMarshal.CreateSpan(ref Ipv6.sin6_addr[0], IPv6AddressSize), Ipv6.sin6_scope_id); | ||
} | ||
else if (AddressFamily == AddressFamily.InterNetwork) | ||
{ | ||
return new IPAddress(MemoryMarshal.CreateSpan(ref Ipv4.sin_addr[0], IPv4AddressSize)); | ||
} | ||
else | ||
{ | ||
throw new SocketException((int)SocketError.AddressFamilyNotSupported); | ||
} | ||
} | ||
|
||
public IPEndPoint GetIPEndPoint() | ||
{ | ||
return new IPEndPoint(GetIPAddress(), GetPort()); | ||
} | ||
|
||
public void SetAddressFamily(AddressFamily family) => Ipv4.SetAddressFamily((int)family); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We historically deal in our native PAL with these kind of concerns. I don't know enough context to decide if it's safe and good for us to reimplement the mapping in managed code for all the platforms.
Instead of defining structures, we could just hold into a
fixed byte data[IPv6AddressSize]
and letSocketAddressPal
to do the dirty work. I think the cost of the extra shared library function call would be negligible.@stephentoub @tmds thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not just the call, it is also pinning
runtime/src/libraries/Common/src/System/Net/SocketAddressPal.Unix.cs
Lines 54 to 61 in 57bfe47
for something that would be easy to do in managed code. I feel the
sockaddr
is unlikely to change for platforms and families we care about. If anything, I would keep code to get size ofsockaddr_storage
in native so we do not need to track addition of new protocols.And we do it for
Quic
already anyway. I feel it would be nice if we can share more code.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can avoid it by introducing non-pinning overloads, since
SockAddr
is created on the stack, or is expected to be pre-pinned in async cases.System.Net.Quic
can also adaptSocketAddressPal
. We would end up having fewer LOC.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the pinning cost worth the duplication?
If you're adding the mapping in managed code, then some existing users of
{Get/Set}IPv{4,6}Address
can probably also use it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pinning with fixed is close to free unless there's a GC that occurs concurrently, and that's not particularly likely with a really fast call like this one. Plus if the thing being pinned isn't actually on the heap, the cost during a GC that does occur is also minimal. I wouldn't duplicate a lot of code to avoid it.