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

send == 0: how to wait for socket to drain? #320

Closed
bscottm opened this issue May 2, 2018 · 9 comments
Closed

send == 0: how to wait for socket to drain? #320

bscottm opened this issue May 2, 2018 · 9 comments
Labels

Comments

@bscottm
Copy link

bscottm commented May 2, 2018

I ran into this particular problem in @mvoidex's hsdev server while tracking down why hsdev would send incomplete responses. I was able to track the problem down to a reimplementation of sendAll:

            sendAll :: Socket -> BS.ByteString -> IO ()
            sendAll sock bs
                    | BS.null bs = return ()
                    | otherwise = do
                            sent <- Net.send sock bs
                            when (sent > 0) $ sendAll sock (BS.drop sent bs)

The subtle bug in this code is the send > 0: send can return 0 because the OS is waiting for a buffer to drain and the underlying descriptors are in non-blocking mode (GHC runtime "feature".)

One way around the send == 0 issue is to call Control.Concurrent.threadDelay. Optimally, you'd really want to call Control.Concurrent.threadWaitWrite, but you can't because converting the socket to a file descriptor is a one way process.

Is there a way to call threadWaitWrite without converting the socket to a descriptor?

@eborden
Copy link
Collaborator

eborden commented May 2, 2018

Hmm, this would possibly require dropping down to GHC.Prim and using waitWrite# on the Socket file descriptor.

@eborden eborden added the bug fix label May 2, 2018
@eborden
Copy link
Collaborator

eborden commented May 2, 2018

@bscottm Are you able to produce a minimal test case for this failure?

@bscottm
Copy link
Author

bscottm commented May 2, 2018

@eborden: I'll see what I can craft... Probably the best test case would be to take the echo server and send 66Kb buffers (at least for MacOS -- localhost/::1 buffering is 64Kb.)

Receiver side is a bit trickier because the receiver's call to recv needs to consume less than 64kb at a time. In the SublimeHaskell plugin, the receiver consumes 10Kb at a time, looking for a newline (it's a JSON stream, newline is the separator between responses...)

/cc @mvoidex

@kazu-yamamoto
Copy link
Collaborator

What about this?

sendAll :: Socket      -- ^ Connected socket
        -> ByteString  -- ^ Data to send
        -> IO ()
sendAll sock bs
  | B.null bs = return ()
  | otherwise = do
      sent <- send sock bs
      if sent == 0 then do
          threadWaitWrite $ fromIntegral $ sockFd sock
          sendAll sock bs
        else if sent > 0 then
          sendAll sock (B.drop sent bs)
        else
          return ()

@bscottm
Copy link
Author

bscottm commented May 19, 2018

I have not been able to successfully reproduce the send == 0 issue.

@kazu-yamamoto
Copy link
Collaborator

@bscottm Thanks!
I will merge this into each versions.

@kazu-yamamoto
Copy link
Collaborator

kazu-yamamoto commented May 25, 2018

  • sendAll
  • sendAllTo
  • sendMany
  • sendManyTo
  • sendAll for Lazy

kazu-yamamoto added a commit to kazu-yamamoto/network that referenced this issue May 25, 2018
kazu-yamamoto added a commit to kazu-yamamoto/network that referenced this issue May 25, 2018
kazu-yamamoto added a commit to kazu-yamamoto/network that referenced this issue May 25, 2018
kazu-yamamoto added a commit to kazu-yamamoto/network that referenced this issue May 25, 2018
@kazu-yamamoto
Copy link
Collaborator

I did many git push -f. Sorry for your confusion.

kazu-yamamoto added a commit to kazu-yamamoto/network that referenced this issue May 25, 2018
@kazu-yamamoto kazu-yamamoto mentioned this issue May 25, 2018
5 tasks
@kazu-yamamoto
Copy link
Collaborator

fixed via #321

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

No branches or pull requests

3 participants