-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Do not overallocate SocketAddress.Buffer #78860
Do not overallocate SocketAddress.Buffer #78860
Conversation
… accessible as System.Net.Sockets internals on Windows
Tagging subscribers to this area: @dotnet/ncl Issue DetailsWe build SocketAddress.cs in two modes: to get the public type in System.Net.Primitives and - independently - as a System.Net.Sockets internal, so we can access internals, and allocate some extra bytes for WSARecvFrom which needs a pinned pointer to the socket address size. This was a technique to help porting .NET Framework socket code to Unix. There seem to be two obvious problems:
With this formula we allocate 32 bytes for a 16-byte IPv4 address for both
|
The |
@wfurt I did some reading on this, and my understanding is that for windows API we need to comply with the C standard which states in [3.2]:
Scrap that, I realized that the above wording is not super clear, updated the code to always align to |
/azp run runtime-libraries-coreclr outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
// 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 forumla will extend 'size' to the alignment boundary then add IntPtr.Size more bytes. | ||
size = (size + IntPtr.Size - 1) / IntPtr.Size * IntPtr.Size + IntPtr.Size; |
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.
int Extend(int size) => (size + IntPtr.Size - 1) / IntPtr.Size * IntPtr.Size + IntPtr.Size;
for (int i = 10; i < 30; i++) Console.Write($"{i,2} | ");
Console.WriteLine();
for (int i = 10; i < 30; i++) Console.Write($"{Extend(i),2} | ");
Will print:
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
24 | 24 | 24 | 24 | 24 | 24 | 24 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 40 | 40 | 40 | 40 | 40 |
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.
Right, the old code did
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
24 | 24 | 24 | 24 | 24 | 24 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 32 | 40 | 40 | 40 | 40 | 40 | 40 |
Sensible address structure would already be aligned IMHO and we really just need to put int
on boundary of its size (e.g. 4) and we need to make sure it fits to the buffer. The new code does it and now we do it only for Windows where it is used.
#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 forumla will extend 'size' to the alignment boundary then add IntPtr.Size more bytes. |
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.
nit: forumla -> formula
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.
LGTM
/azp run runtime-libraries-coreclr outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
We build SocketAddress.cs in two modes: to get the public type in System.Net.Primitives and - independently - as a System.Net.Sockets internal, so we can access internal members, and work withe the extra bytes allocated at the end of
SocketAddress.Buffer
for WSARecvFrom which needs a pinned pointer to the socket address size. This duplication was a refactoring technique when Framework code was ported to Core where System.Net.Sockets and System.Net.Primtives live in separate dll-s and socket code cannot access SocketAddress internals.There seem to be two obvious problems:
SocketAddress
everywhere, including Unix platformsruntime/src/libraries/Common/src/System/Net/SocketAddress.cs
Line 95 in 4f53c2f
This allocates 32 bytes for a 16-byte IPv4 address for both
SocketAddress
andInternals.SocketAddress
on a 64bit machine.The intention of the formula was probably to make sure thatEdit: updated the code reintroducing alignment (though with a "smarter" formula), see discussion.lpFromlen
's buffer is 4-byte aligned (assuming 32 bit), but I don't see any signs in WSARecvFrom docs that there is a need for any alignment.