-
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
UdpClient with span support #53429
UdpClient with span support #53429
Conversation
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
Tagging subscribers to this area: @dotnet/ncl Issue Details
|
@lateapexearlyspeed thanks, let us know if you have any questions in the area. |
@@ -757,6 +757,7 @@ public partial class UdpClient : System.IDisposable | |||
public byte[] Receive([System.Diagnostics.CodeAnalysis.NotNullAttribute] ref System.Net.IPEndPoint? remoteEP) { throw null; } | |||
public System.Threading.Tasks.Task<System.Net.Sockets.UdpReceiveResult> ReceiveAsync() { throw null; } | |||
public int Send(byte[] dgram, int bytes) { throw null; } | |||
public int Send(ReadOnlySpan<byte> dgram) {throw null; } |
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.
The param name as approved by API review is "datagram".
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.
Fixed, thanks.
…ostname, port) overloads.
See my comment on the API as approved here: #864 (comment) |
Hi @geoffkizer Just confirm, based on comment, you have decided to "match existing API nullability and semantics when null is used" now. So Happily current code in PR just meets this :) |
Yes :) |
…ostname, int port ...); Add test.
Hi @geoffkizer , this PR is ready for review now, thanks. |
@antonfirsov Can you review? |
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.
Thanks for the addition @lateapexearlyspeed! The change is conceptually fine, but we need to fix a couple of small things before merging.
@@ -600,9 +612,15 @@ public void DropMulticastGroup(IPAddress multicastAddr, int ifindex) | |||
public Task<int> SendAsync(byte[] datagram, int bytes) => | |||
SendAsync(datagram, bytes, null); | |||
|
|||
public ValueTask<int> SendAsync(ReadOnlyMemory<byte> datagram, CancellationToken cancellationToken = default) => |
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.
According to a new rule, we need to add triple-slash docs for all new public methods right in product code PR-s. They will be fed to a tool to generate API docs.
You can find examples in Socket.cs
. My recommendation is to copy and alter documentation text from existing overloads
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.
Added xml doc for new APIs.
Assert.Throws<ObjectDisposedException>(() => udpClient.Send(new ReadOnlySpan<byte>())); | ||
Assert.Throws<ObjectDisposedException>(() => udpClient.Send(new ReadOnlySpan<byte>(), "localhost", 0)); | ||
|
||
await Assert.ThrowsAsync<ObjectDisposedException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>(), remoteEP).AsTask()); |
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.
await Assert.ThrowsAsync<ObjectDisposedException>(() => udpClient.SendAsync(new ReadOnlyMemory<byte>(), remoteEP).AsTask()); | |
Assert.Throws<ObjectDisposedException>(() => _ = udpClient.SendAsync(new ReadOnlyMemory<byte>(), remoteEP)); |
The ObjectDisposedException
-s should be thrown synchronously (same for the rest).
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.
Let's also add coverage for the existing SendAsync overloads that are not covered here.
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.
Fixed and added.
[OuterLoop] | ||
[Theory] | ||
[MemberData(nameof(Loopbacks))] | ||
public async Task SendToRecvFromAsyncWithReadOnlyMemory_Datagram_UDP_UdpClient(IPAddress loopbackAddress) |
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.
Instead of duplicating the whole test case, can we add an additional bool useMemoryOverload
parameter to the previous one?
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.
Fixed.
public Task<int> SendAsync(byte[] datagram, int bytes, string? hostname, int port) => | ||
SendAsync(datagram, bytes, GetEndpoint(hostname, port)); | ||
|
||
public ValueTask<int> SendAsync(ReadOnlyMemory<byte> datagram, string? hostname, int port, CancellationToken cancellationToken = default) => | ||
SendAsync(datagram, GetEndpoint(hostname, port), cancellationToken); |
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.
Since this is an async overload, we should not use the blocking Dns.GetHostAddresses
when resolving hostname
. We need an async variant of GetEndpoint
.
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.
I agree we should have an async version of GetEndpoint. That said, the existing SendAsync above just uses sync GetEndpoint. So since we don't have this today, I think we could live without it for this PR and file a separate issue.
@@ -334,6 +335,17 @@ private void ValidateDatagram(byte[] datagram, int bytes, IPEndPoint? endPoint) | |||
} | |||
} | |||
|
|||
private void ValidateDatagram(ReadOnlyMemory<byte> datagram, IPEndPoint? endPoint) |
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.
The datagram
argument is unused, note that this also makes the naming unfortunate. Since this particular overload has only one usage, it's cleaner to inline probably.
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.
Refactored code to validate inside calling code.
@@ -606,6 +621,40 @@ public async Task SendAsync_ReceiveAsync_Success(bool ipv4) | |||
} | |||
} | |||
|
|||
[OuterLoop] |
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.
Let's remove [OuterLoop]
from all send/receive tests in UdpClientTest
. It's a leftover from dotnet/corefx@defbb8a which was a carpet bombing.
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.
Removed.
using (var receiver = new UdpClient(new IPEndPoint(address, 0))) | ||
using (var sender = new UdpClient(new IPEndPoint(address, 0))) | ||
{ | ||
await sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1]), new IPEndPoint(address, ((IPEndPoint)receiver.Client.LocalEndPoint).Port)); |
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.
This only tests one overload, we need to also test the remaining ones (especially the ones using GetEndpoint
).
I know this is a debt that also applies to other overloads, but we need to deal with it before proceeding, and make sure every new API we add is tested.
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.
Added.
} | ||
|
||
[Fact] | ||
[PlatformSpecific(TestPlatforms.Windows)] // "localhost" resolves to IPv4 & IPV6 on Windows, but may resolve to only one of those on Unix |
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.
Rather than making this test platform specific, can we test cancellation with an unconnected UdpClient
?
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.
Fixed.
using (var receiver = new UdpClient("localhost", 0)) | ||
using (var sender = new UdpClient("localhost", ((IPEndPoint)receiver.Client.LocalEndPoint).Port)) | ||
{ | ||
await sender.SendAsync(new ReadOnlyMemory<byte>(new byte[1])); |
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.
Same as in SendAsyncWithReadOnlyMemory_ReceiveAsync_Success
: we need to test the other overloads too. We also need to verify if InvalidOperationException
is being thrown on hostName != null
or port != 0
for a connected UdpClient
.
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.
Added.
There is one thing not sure: |
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.
@lateapexearlyspeed sorry for the delay, and thanks a lot for your patience and accurate work! Just a few more requests and we are done.
/// <exception cref="SocketException">An error occurred when accessing the socket.</exception> | ||
public ValueTask<int> SendAsync(ReadOnlyMemory<byte> datagram, CancellationToken cancellationToken = default) => |
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.
This is makes the bar really unfair for community contributors IMO, but I think we should also copy the Remarks sections when present:
(This applies to all new overloads.)
@carlossanlop any plans to make this process simpler and the code less bloated with long copy-pasted blocks? (Not sure if you are the right person to tag about this.)
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.
The long-term plan is to merge the existing external docs back in to the code, so that the code is the one source of truth.
I don't know when/how this will happen; @carlossanlop is the right person to talk to.
The only remaining issue in the PR is highlighted in #53429 (comment), apart from that, this is ready to merge in my opinion. @geoffkizer any thoughts on the |
I wouldn't worry about But @carlossanlop would know for sure. |
/azp run runtime-libraries-coreclr outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
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, let's not bother with <remarks>
. Test failures are unrelated. @lateapexearlyspeed thanks again!
Thanks @antonfirsov , I am happy to work and finish it :) |
…bugger2 * origin/main: (107 commits) Disable MacCatalyst arm64 PR test runs on staging pipeline (dotnet#54678) [WASM] Fix async/await in config loading (dotnet#54652) Fix for heap_use_after_free flagged by sanitizer (dotnet#54679) [wasm] Bump emscripten to 2.0.23 (dotnet#53603) Fix compiler references when building inside VS (dotnet#54614) process more TLS frames at one when available (dotnet#50815) Add PeriodicTimer (dotnet#53899) UdpClient with span support (dotnet#53429) exclude fragile tests (dotnet#54671) get last error before calling a method that might fail as well (dotnet#54667) [FileStream] add tests for device and UNC paths (dotnet#54545) Fix sporadic double fd close (dotnet#54660) Remove Version.Clone from AssemblyName.Clone (dotnet#54621) [wasm] Enable fixed libraries tests (dotnet#54641) [wasm] Fix blazor/aot builds (dotnet#54651) [mono][wasm] Fix compilation error on wasm (dotnet#54659) Fix telemetry for Socket connects to Dns endpoints (dotnet#54071) [wasm] Build static components; include hot_reload in runtime (dotnet#54568) [wasm][debugger] Reuse debugger-agent on wasm debugger (dotnet#52300) Put Crossgen2 in sync with dotnet#54235 (dotnet#54438) ...
Fix #864 , I am starting working on it.