Ticket #5843 (closed bug: fixed)

Opened 16 months ago

Last modified 14 months ago

hGetBufSome blocks when all available input is buffered (on Windows only)

Reported by: joeyadams Owned by: pcapriotti
Priority: high Milestone: 7.4.2
Component: libraries/base Version: 7.2.2
Keywords: Cc:
Operating System: Windows Architecture: x86
Type of failure: Incorrect result at runtime Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

See the attached program. On Windows, it blocks on hGetSome.

If I take out the hGetLine and hWaitForInput, hGetSome does not block. If I add <code>hSetBuffering h NoBuffering?</code>, hGetBufSome still blocks after hWaitForInput because it does some buffering when it calls hLookAhead_.

Attachments

hGetSome.hs Download (0.9 KB) - added by joeyadams 16 months ago.
getsome.patch Download (0.6 KB) - added by pcapriotti 14 months ago.

Change History

Changed 16 months ago by joeyadams

Changed 16 months ago by joeyadams

Sorry, I pressed Create Ticket when I meant to press Preview.

I did some debug traces, and found that the flow of calls is as follows:

hGetSome hGetBufSome bufReadNBNonEmpty (second occurrence) bufReadNBEmpty Buffered.fillReadBuffer0

The Buffered.fillReadBuffer0 call blocks on Windows, even though it shouldn't:

  -- | reads bytes into the buffer without blocking.  Returns the
  -- number of bytes read (Nothing indicates end-of-file), and the new
  -- buffer.
  fillReadBuffer0   :: dev -> Buffer Word8 -> IO (Maybe Int, Buffer Word8)

Digging deeper, I think fillReadBuffer0 ultimately boils down to a call to fdReady. But I'm not 100% sure.

hWaitForInput, which also calls fdReady, does in fact work on Windows. Perhaps readRawBufferPtrNoBlock isn't calling fdReady correctly. Here is how GHC.IO.FD.ready calls it (for FD):

#if defined(mingw32_HOST_OS)
                          (fromIntegral $ fromEnum $ fdIsSocket fd)
#else
                          0
#endif

And here is how readRawBufferPtrNoBlock calls it:

  | otherwise    = do r <- unsafe_fdReady (fdFD fd) 0 0 0

Changed 16 months ago by simonmar

  • owner set to simonmar
  • difficulty set to Unknown
  • priority changed from normal to high
  • milestone set to 7.4.2

Thanks for the report.

Changed 14 months ago by pcapriotti

  • owner changed from simonmar to pcapriotti

Changed 14 months ago by duncan

See GHC/IO/FD.hs, in the "FD operations" section, in the ! mingw32_HOST_OS case:

readRawBufferPtrNoBlock :: String -> FD -> Ptr Word8 -> Int -> CSize -> IO CInt
readRawBufferPtrNoBlock = readRawBufferPtr

writeRawBufferPtrNoBlock :: String -> FD -> Ptr Word8 -> Int -> CSize -> IO CInt
writeRawBufferPtrNoBlock = writeRawBufferPtr

There does not seem to be any non-blocking impl here for windows (where there is one for unix just above).

Changed 14 months ago by simonmar

Duncan: we don't have a way to do non-blocking I/O on Windows, but hGetSome is supposed to work around that.

There is probably some fault in the logic in bufReadNBNonEmpty, because it is not supposed to try to read any more data when called by hGetBufSome, see the comment:

                                       bufReadNBNonEmpty h_ buf' (castPtr ptr) 0 (min r count)
                                        -- new count is  (min r count), so
                                        -- that bufReadNBNonEmpty will not
                                        -- issue another read.

Changed 14 months ago by simonmar

I see it: the second call to bufReadNBNonEmpty in hGetBufSome is passing count along unchanged, it should use the minimum of the available buffer elements and count. I don't have a handy Windows build to test this; Paulo, could you try?

Changed 14 months ago by pcapriotti

Changed 14 months ago by pcapriotti

  • status changed from new to patch

Thanks Simon, that does indeed fix the problem, at least in a simple test-case with stdin.

I'll test it on the original socket example before pushing.

Changed 14 months ago by pcapriotti

  • status changed from patch to merge

Pushed as 370fc0b455f6a03283fbd5c0baa5d08d9115379d.

Changed 14 months ago by pcapriotti

  • status changed from merge to closed
  • resolution set to fixed

Merged as bd399cef9d91060a3d733be4c18cb901a2efbbc8.

Note: See TracTickets for help on using tickets.