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

Document how to close sockets in a threaded scenario #545

Open
voidus opened this issue Apr 1, 2023 · 7 comments · May be fixed by #548
Open

Document how to close sockets in a threaded scenario #545

voidus opened this issue Apr 1, 2023 · 7 comments · May be fixed by #548

Comments

@voidus
Copy link

voidus commented Apr 1, 2023

Heya,

Disclaimer: I'm still figuring this stuff out, so it might be obvious for some of you. That would actually be cool, because then documenting it should be easy :)

I think we should document a recommended way to interrupt threads that are blocking on a recv call.

Motivating context

I want to build a small tool that listens to a unix socket, connects to another one and forwards data in both directions. I've decided to have one thread per direction, i.e. downstream recvs from the connection made to us and writes to the one we connect to, while upstream does the same thing but with the sockets flipped.

The situation I'm struggling with is that one of the connections is closed by the other end. (Since it's basically symmetrical at this point, I think we can consider them in one go). I obviously want the tool to be a good citizen, so the other socket should be gracefully closed immediately.

As far as I can tell, there's no way to wait for recv or something else, so the only thing I can do is use throwTo to interrupt the thread, right? (And then close the socket in an appropriate exception handler)

So I was looking for something in the documentation that tells me whether asynchronous exceptions are delivered while blocking on recv but didn't find anything. I've considered using shutdown on the socket instead, but that seems like a worse solution because it is socket-specific.

It seems to me that all applications that work with multiple sockets in a single context need something like this, so some guidance would be great. Could also be just a link to some existing explanation of course

@voidus
Copy link
Author

voidus commented Apr 1, 2023

Note: Experimentally, async exceptions seem to be delivered to blocked threads. This is on arch linux + nix with these package versions:

> cabal list --installed
Cabal 3.6.3.0
HUnit 1.6.2.0
OneTuple 0.3.1
QuickCheck 2.14.2
StateVar 1.2.2
adjunctions 4.4.2
ansi-terminal 0.11.4
ansi-wl-pprint 0.6.9
array 0.5.4.0
assoc 1.0.2
async 2.2.4
base 4.16.4.0
base-orphans 0.8.7
bifunctors 5.5.14
binary 0.8.9.0
bytestring 0.11.4.0
call-stack 0.4.0
clock 0.8.3
colour 2.3.6
comonad 5.0.8
containers 0.6.5.1
contravariant 1.5.5
data-array-byte 0.1.0.1
deepseq 1.4.6.1
directory 1.3.6.2
distributive 0.6.2.1
exceptions 0.10.4
extra 1.7.12
filepath 1.4.2.2
free 5.1.10
ghc 9.2.7
ghc-bignum 1.2
ghc-boot 9.2.7
ghc-boot-th 9.2.7
ghc-compact 0.1.0.0
ghc-heap 9.2.7
ghc-prim 0.8.0
ghci 9.2.7
hashable 1.4.2.0
haskeline 0.8.2
hpc 0.6.1.0
hspec 2.9.7
hspec-core 2.9.7
hspec-discover 2.9.7
hspec-expectations 0.8.2
indexed-traversable 0.1.2
indexed-traversable-instances 0.1.1.1
integer-gmp 1.1
invariant 0.6
kan-extensions 5.2.5
lens 5.1.1
libiserv 9.2.7
mtl 2.2.2
network 3.1.2.7
optparse-applicative 0.17.0.0
package-version 0.3
parallel 3.2.2.0
parsec 3.1.15.0
pretty 1.1.3.6
prettyprinter 1.7.1
primitive 0.7.3.0
process 1.6.16.0
profunctors 5.6.2
quickcheck-io 0.2.0
random 1.2.1.1
record-dot-preprocessor 0.2.15
record-hasfield 1.0
reflection 2.1.6
relude 1.1.0.0
rts 1.0.2
safe-exceptions 0.1.7.3
semigroupoids 5.3.7
semigroups 0.20
setenv 0.1.1.3
splitmix 0.1.0.4
stm 2.5.0.2
strict 0.4.0.1
syb 0.7.2.2
tagged 0.8.6.1
template-haskell 2.18.0.0
terminfo 0.4.1.5
text 1.2.5.0
tf-random 0.5
th-abstraction 0.4.5.0
these 1.1.1.1
time 1.11.1.1
transformers 0.5.6.2
transformers-base 0.4.6
transformers-compat 0.7.2
uniplate 1.6.13
unix 2.7.2.2
unordered-containers 0.2.19.1
validation 1.1.2
vector 0.12.3.1
void 0.7.3
xhtml 3000.2.2.1

@coot
Copy link
Contributor

coot commented Apr 3, 2023

We implemented a way to use duplex connections in IOG, although in a more complex scenario. We have a component which tracks state of the connection, e.g. in which direction it is being used. We do it at the protocol level (communication protocol which we implemented), while for taking care of socket we simply use bracket (socket ...) close ... (well almost...).

What I would do is implement a simple protocol which has a termination message, then in the bracket callback you could wait receiving termination message & terminating the local sender before exiting the callback (which will let bracket close the socket).

@kazu-yamamoto
Copy link
Collaborator

Usually, shutdown should be used.

@voidus You are using UNIX-domain sockets. I don't understand why socket-specific is not a good solution.

@voidus
Copy link
Author

voidus commented Apr 4, 2023

@coot I'd rather not depend on the connecting applications being well-behaved on both ends.

@kazu-yamamoto I'm not sure what you mean with socket-specific. I tried to search for it but couldn't find anything really.
If I understand you correctly, calling shutdown on a socket guarantees that the thread will wake up. Is that correct?

@kazu-yamamoto
Copy link
Collaborator

@voidus

You said "but that seems like a worse solution because it is socket-specific" in the first comment. I would like to know why you think so.

@kazu-yamamoto
Copy link
Collaborator

If I understand you correctly, calling shutdown on a socket guarantees that the thread will wake up. Is that correct?

When thread A calls shutdown sock ShutdownSend, recv of thread B returns with "".

@voidus
Copy link
Author

voidus commented Apr 5, 2023

Honestly, looking back, I'd say it's because I'm writing a lot of python and have no hesitation to use exceptions for control flow. The other reason why I thought it would be better is that it's applicable in other situations as well, so it felt like a more general solution.

I'll prepare a PR for brief documentation changes that would have helped me.

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 a pull request may close this issue.

3 participants