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 docs for shutting down sockets in a threaded scenario #548

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

voidus
Copy link

@voidus voidus commented Apr 5, 2023

Closes #545, see discussion there.

Basically, this is the documentation that I think would have helped me a few days ago.

I'm very much open to changing all it. The code example is a bit more complex than what I would like, but I thought it's better to show the situation where I needed it than to make it very simple.

@kazu-yamamoto
Copy link
Collaborator

shutdown is typically used to stop sending but continue receiving.
If it is OK to stop the both sides, just use close.

@voidus
Copy link
Author

voidus commented Apr 23, 2023

I think the problem with close is that there are two places where you would want to do that.

Let's say you have a background thread that's listening on the socket. Given that the other side might also close it, there's probably a bracket _ close in there somewhere. But if we now close the socket from another thread, the file descriptor will be freed and possibly reused. If that happens before the bracket close runs, whatever uses the file descriptor now would be closed, right?

This makes a few assumptions about the way the operating system works, but it doesn't seem unrealistic to me. Using shutdown from the outside and waiting for the bracketed close to do it's thing seems safer.

@kazu-yamamoto
Copy link
Collaborator

I'm not sure I understand your programming model correctly but the top of the manual says:

The proper programming model is that one Socket is handled by a single thread. If multiple threads use one Socket concurrently, unexpected things would happen. There is one exception for multiple threads vs a single Socket: one thread reads data from a Socket only and the other thread writes data to the Socket only.

And when Socket is closed, the internal FD is set to (-1). No reuse problem will happen. See the document of unsafeFdSocket for more information.

@voidus
Copy link
Author

voidus commented Apr 24, 2023

I'm basically building a proxy with two sockets and one thread for each. One threads reads from socket A and writes to B, the other one does it the other way around. As far as I understand it, this pattern is explicitly covered by the part you quoted.
Let's say the remote of socket A closes the connection. In this case, I want to shut down the connection to socket B, and I was unclear about how to do that. Hope that clears things up.

I missed that multiple calls to close are safe, thank you for pointing it out. I'll rewrite this to suggest closeing the socket rather than using shutdown.

I get that this might feel a bit unnecessary, but given the amount of pitfalls low-level network programming has, I think it would be very useful to users to be explicitly shown a way to do this that is safe.

@@ -100,6 +100,44 @@
-- unexpected things would happen. There is one exception for multiple
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This existing sentence is not good.

unexpected things, what's that referring to?

This needs explanation.

@@ -100,6 +100,44 @@
-- unexpected things would happen. There is one exception for multiple
-- threads vs a single 'Socket': one thread reads data from a 'Socket'
-- only and the other thread writes data to the 'Socket' only.
--
-- The preferred way to terminate a thread that is blocked on a call to
-- 'Network.Socket.ByteString.recv' is to use 'shutdown'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The immediate question this poses:

Why this, instead of just throwing an exception to the thread that's blocked on recv?

That would be the most natural to me, as that's how e.g. async does it.

recv is implemented in terms of threadWaitRead, so throwTo to it should work fine and interrupt it, shouldn't it?

Then, if you want your app to do a graceful shutdown, it can do

bracket createConnection gracefulClose $ -- ... code using recv ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Document how to close sockets in a threaded scenario
3 participants