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

Add StopListeningAsync() to ConnectionListener #41118

Closed
halter73 opened this issue Aug 20, 2020 · 7 comments
Closed

Add StopListeningAsync() to ConnectionListener #41118

halter73 opened this issue Aug 20, 2020 · 7 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net
Milestone

Comments

@halter73
Copy link
Member

halter73 commented Aug 20, 2020

Background and Motivation

When migrating Kestrel to the new System.Net.Connections APIs, I noticed that there's no built-in ability to tell a listener to stop listening prior to disposing the transport.

We want this capability in Kestrel so new connections aren't accepted into the listen backlog as it is draining connections during shutdown. AFAIK, simply not calling AcceptAsync is not enough to prevent connections from being accepted into the listen backlog. Kestrel doesn't want to dispose the transport entirely before connections are drained because the transport owns the memory pool being used by the draining connections, and this memory pool is disposed with the transport.

Proposed APIs

namespace System.Net.Connections
{
    public class ConnectionListener
    {
+      public abstract ValueTask StopListeningAsync(CancellationToken cancellationToken = default);
    }

We can consider making this async, but I don't see why that would be necessary. It doesn't need to be async the way it's used today in Kestrel's Socket-based transport.

cc @geoffkizer @scalablecory @davidfowl

@halter73 halter73 added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net labels Aug 20, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Aug 20, 2020
@ghost
Copy link

ghost commented Aug 20, 2020

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@davidfowl davidfowl changed the title Add Unbind() to ConnectionListener Add StopListeningAsync() to ConnectionListener Aug 20, 2020
@scalablecory
Copy link
Contributor

Seems reasonable. @geoffkizer anything to add before moving to API review?

@geoffkizer
Copy link
Contributor

I'm trying to understand what user functionality this adds beyond just disposing the object. Is there anything?

If you call StopListeningAsync and then call AcceptAsync again, what exception do you get? ObjectDisposedException? Something else? If something else, then what? And if something else, that seems to imply that every Connection needs to track "stopped" differently than "disposed", which doesn't seem ideal.

If you call StopListeningAsync, can you then call ListenAsync again and start listening again? Presumably on a potentially new EndPoint? We actually had a long-standing bug in TcpListener where calling Stop and then Start didn't work in some cases. This is the kind of thing that no one ever does because it's not really useful, until someone decides to do it, and then you find out it doesn't really work properly.

If we are adding StopListeningAsync, then should we rename ListenAsync to StartListeningAsync?

@geoffkizer
Copy link
Contributor

Kestrel doesn't want to dispose the transport entirely before connections are drained because the transport owns the memory pool being used by the draining connections, and this memory pool is disposed with the transport.

I'm certainly sympathetic to making the Kestrel porting effort easier, but it seems like this is something you could handle pretty easily by just adding something like KestrelConnection that holds both a Connection and the memory pool stuff, with a Stop method that disposes the underlying Connection but not the memory pool. Am I missing something?

@davidfowl
Copy link
Member

The problem is that the listener factory currently owns both the listen socket and the memory pool and there's no way to say dispose/stop the listen socket without disposing the memory pool as well. We need a way to decouple the thing that is listening from any other resources that may be affecting existing connections.

@davidfowl
Copy link
Member

If we are adding StopListeningAsync, then should we rename ListenAsync to StartListeningAsync?

I like ListenAsync but it doesn't have a good inverse (UnListen 😄 )

@davidfowl davidfowl mentioned this issue Aug 23, 2020
2 tasks
@halter73
Copy link
Member Author

but it seems like this is something you could handle pretty easily by just adding something like KestrelConnection that holds both a Connection and the memory pool stuff,

Instead of sharing the memory pool ownership between the connections with some sort of reference counting, I've moved the memory pool ownership to the ConnectionListenerFactory instead of the ConnectionListener itself (I think @geoffkizer might have also suggested exactly this in our Teams chat). This allows Kestrel to dispose the listener before draining all the connections. It looks like this works well, so I'm closing this issue.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 7, 2020
@karelz karelz added this to the 6.0.0 milestone Jan 26, 2021
@karelz karelz removed the untriaged New issue has not been triaged by the area owner label Oct 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net
Projects
None yet
Development

No branches or pull requests

6 participants