Opened 2 years ago

Closed 21 months ago

Last modified 20 months ago

#6057 closed bug (invalid)

hGetBufNonBlocking blocks the underlying handle on Windows

Reported by: cetinsert Owned by:
Priority: normal Milestone:
Component: libraries/base Version: 7.0.4
Keywords: Cc:
Operating System: Windows Architecture: Unknown/Multiple
Type of failure: Incorrect result at runtime Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

In a cross-platform transport-layer proxy software I am developing, I have this piece of code:

  a >- b $ j       -- forks
  b >- a $ j       -- forks

where a and b are handles; j is an exception handler and (>-) is a self-forking infinite loop IO that splices data between two handles, defined previously in terms of hGetBufSome and and hPutBuf on Windows.

I just noticed that:

  b >- a $ j       -- forks
  a >- b $ j       -- forks

behaves differently from:

  a >- b $ j       -- forks
  b >- a $ j       -- forks

When proxying connections to an SSH server the first one does not show the greeting from the SSH server before receiving user input, the second does show the greeting.

As the two lines immediately fork to their own Haskell threads the blocking must come from the underlying handles.

When I attempted to use hGetBufNonBlocking to avoid the problem I noticed that the docs read: "NOTE: on Windows, this function does not work correctly; it behaves identically to hGetBuf.".

The reason I chose to use handles in the first place was the "Improving I/O Performance over sockets" section in the network package: http://hackage.haskell.org/packages/archive/network/2.3.0.13/doc/html/Network.html.

At this point I am stuck as neither the network package exports functions that work on pointers to pre-allocated memory buffers nor the h---- famiy of functions from System.IO behave as their names suggest.

Defining (>-) in terms of recv and sendAll from Network.Socket.ByteString? does show the problem and both variants behave exactly the same at run time and this is the only correct behaviour as I have no way of knowing or special-casing which application layer protocols expect initiation from which end (client input first or server greeting first).

I am reporting this issue to both here and to the maintainers of the network package on GitHub? .

This bug is reported for 7.0.4 but I highly suspect the behavior may still be the same with newer GHC versions.

Change History (8)

comment:1 Changed 2 years ago by cetinsert

Defining (>-) in terms of recv and sendAll from Network.Socket.ByteString? does SOLVE the problem and both variants behave exactly the same at run time and this is the only correct behaviour as I have no way of knowing or special-casing which application layer protocols expect initiation from which end (client input first or server greeting first).

comment:2 Changed 2 years ago by simonmar

  • Difficulty set to Unknown

I'm not quite sure I understand what bug you're reporting here. Is it that hGetBufNonblocking blocks on Windows (a documented bug, as you noted), or is there something else? If it is something else, can you give us a program that demonstrates the problem?

comment:3 Changed 2 years ago by cetinsert

Although I do not have code, I have pinned the problem down to following steps:

  1. Create sockets
  2. Convert sockets to handles
  3. Set handles to no buffering
  4. Do handshake IO using operations on handles
  5. Set same handles to no buffering again on a different Haskell thread
  6. Observe the order-dependent behavior mentioned in this report:
        a >- b $ j    -- forks and does step 5 on a Haskell thread
        b >- a $ j    -- forks and does step 5 on a Haskell thread
    
    differs from
        b >- a $ j    -- forks and does step 5 on a Haskell thread
        a >- b $ j    -- forks and does step 5 on a Haskell thread
    

Eliminating step 5 makes the problem go away:

  a >- b $ j
  b >- a $ j

equals

  b >- a $ j
  a >- b $ j

in behaviour.

How can this be explained? It seems to happen both on Windows and Linux.

comment:4 Changed 2 years ago by simonmar

I don't understand enough about your application to be able to tell what's wrong, I'm afraid. The details are all important - I can't reproduce the bug from an informal description of what your program does. Is it possible for you to make a small program that illustrates the problem? Or failing that, give us instructions to build and run your real test case and explain exactly what is wrong?

comment:5 Changed 21 months ago by cetinsert

  • Resolution set to invalid
  • Status changed from new to closed

comment:6 Changed 20 months ago by simonmar

@cetinsert I see you closed the ticket as invalid with no explanation, so I just want to check the reason. Did you find out what the bug was?

comment:7 follow-up: Changed 20 months ago by cetinsert

@simonmar I closed it because I found out I was calling hSetBuffering NoBuffering? twice on the same handle from different Haskell threads. Now I came to learn that one should not have any assumptions on how handles should behave as they seem to very platform specific in funny ways. I updated the library I wrote to explicitly mention the handle buffering states it can work with (http://hackage.haskell.org/packages/archive/splice/0.6.1/doc/html/Network-Socket-Splice.html#v:hSplice) and the problems are gone.

comment:8 in reply to: ↑ 7 Changed 20 months ago by simonmar

Replying to cetinsert:

Now I came to learn that one should not have any assumptions on how handles should behave as they seem to very platform specific in funny ways.

Could you give more details? Handles are supposed to be a platform-independent API.

Note: See TracTickets for help on using tickets.