[GHC] #14503: Killing a thread will block if there is another process reading from a handle

#14503: Killing a thread will block if there is another process reading from a handle ----------------------------------------+--------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.0.2 Keywords: | Operating System: Windows Architecture: Unknown/Multiple | Type of failure: None/Unknown Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: ----------------------------------------+--------------------------------- When trying to kill a thread, the program (which uses a thread) hangs if there is another process trying to read from a handle. This bug can be reproduced with using [https://github.com/capitanbatata/sandbox/tree/master/dangling-connections this sample code]. I'll explain the relevant details below. I have the following Haskell code: {{{#!hs someFuncWithChans :: IO () someFuncWithChans = withSocketsDo $ do h <- connectTo "localhost" (PortNumber 9090) hSetBuffering h NoBuffering ch <- newChan putStrLn "Starting the handler reader" readerTid <- forkIO $ handleReader h ch cmdsHandler h ch putStrLn "Killing the handler reader" killThread readerTid putStrLn "Closing the handle" hClose h cmdsHandler :: Handle -> Chan Action -> IO () cmdsHandler h ch = do act <- readChan ch case act of Quit -> putStrLn "Bye bye" Line line -> do hPutStrLn h (reverse line) cmdsHandler h ch handleReader :: Handle -> Chan Action -> IO () handleReader h ch = forever $ do line <- strip <$> hGetLine h case line of "quit" -> writeChan ch Quit _ -> writeChan ch (Line line) data Action = Quit | Line String }}} Is the function `someFuncWithChans` is run along with the following Java program, then the former will block while killing the handler reader (`readerTid`). {{{#!java public static void main(String[] args) throws IOException, InterruptedException { ServerSocket serverSock = new ServerSocket(9090); Socket sock = serverSock.accept(); InputStream inStream = sock.getInputStream(); BufferedReader sockIn = new BufferedReader(new InputStreamReader(inStream)); OutputStream outStream = sock.getOutputStream(); PrintWriter sockOut = new PrintWriter(new OutputStreamWriter(outStream)); while (true) { Thread.sleep(1000); System.out.println("Sending foo"); sockOut.println("foo"); sockOut.flush(); String s = sockIn.readLine(); System.out.println("Got " + s ); Thread.sleep(1000); System.out.println("Sending bar"); sockOut.println("bar"); sockOut.flush(); s = sockIn.readLine(); System.out.println("Got " + s ); Thread.sleep(1000); System.out.println("Sending quit"); sockOut.println("quit"); sockOut.flush(); // This will cause someFuncWithChans to block when killing the // reader thread. s = sockIn.readLine(); System.out.println("Got " + s ); } } }}} If the `sockIn.readLine()` is commented out, then killing the thread will succeed. This problem appears only on my Windows machine (at work), whereas it does not on my personal Linux machine. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: libraries/base | Version: 8.0.2 Resolution: | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by dnadales): * component: Compiler => libraries/base Comment: The problem can be easily reproduced by running these two programs in parallel: {{{#!hs readWithinNSecs :: IO () readWithinNSecs = withSocketsDo $ do h <- connectTo "localhost" (PortNumber 9090) hSetBuffering h NoBuffering readerTid <- forkIO $ reader h threadDelay $ 2 * 10^6 putStrLn "Killing the reader" killThread readerTid putStrLn "Reader thread killed" where reader h = do line <- strip <$> hGetLine h putStrLn $ "Got " ++ line }}} {{{#!java import java.net.*; import java.io.*; public class Reader { public static void main(String[] args) throws IOException, InterruptedException { ServerSocket serverSock = new ServerSocket(9090); Socket sock = serverSock.accept(); InputStream inStream = sock.getInputStream(); BufferedReader sockIn = new BufferedReader(new InputStreamReader(inStream)); while (true) { Thread.sleep(1000); System.out.println("Trying to read something..."); String s = sockIn.readLine(); System.out.println("Got " + s ); } } } }}} -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: libraries/base | Version: 8.0.2 Resolution: | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by bgamari): Thanks for the repro! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a
handle
-------------------------------------+-------------------------------------
Reporter: dnadales | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: libraries/base | Version: 8.0.2
Resolution: | Keywords:
Operating System: Windows | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by bgamari):
Here is an complete all-Haskell repro,
{{{#!hs
-- Server.hs
import System.IO
import Network
import Control.Monad
import Control.Concurrent
main = withSocketsDo $ do
sock <- listenOn $ PortNumber 9090
(h, _, _) <- accept sock
forever $ do
threadDelay 100000
putStrLn "Trying to read something"
s <- hGetLine h
putStrLn $ "Got "++s
}}}
{{{#!hs
-- Client.hs
import Network
import System.IO
import Control.Concurrent
main = readWithinNSecs
readWithinNSecs :: IO ()
readWithinNSecs = withSocketsDo $ do
h <- connectTo "localhost" (PortNumber 9090)
hSetBuffering h NoBuffering
readerTid <- forkIO $ reader h
threadDelay $ 2 * 10^6
putStrLn "Killing the reader"
killThread readerTid
putStrLn "Reader thread killed"
where
reader h = do
line <- hGetLine h
putStrLn $ "Got " ++ line
}}}
Expected output (as seen on Debian 9 with GHC 8.2.2),
{{{
$ ghc Server.hs
$ ghc Client.hs
$ ./Server & ./Client
Trying to read something
Killing the reader
Reader thread killed
Server:

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * milestone: => 8.2.1 Comment: Ahh, but indeed it hangs when `Client` is built with 8.0.2. It looks like this is fixed. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: fixed | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * status: new => closed * resolution: => fixed Comment: Unfortunately it's not entirely clear how to add a testcase for this as it seems to require a socket. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: fixed | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by dnadales): Thanks for checking this out. I've tried your example with GHC 8.2.1, and it also hangs when killing the reader. I'm also running a 64-bit version of Windows 10, so I don't know what might be happening. How did you launch the two processes? On a Unix like terminal emulator? (I've tried on MINGW64, but there it is even worse since I don't see any output). In my case I was using Windows Power Shell, but I get the same result in the old Windows Command Prompt. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: fixed | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by dnadales): I'm using stack as build tool. To discard the possibility of any noise introduced by it, I downloaded the 8.2.1 version of the Haskell platform and I still get the same results. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: fixed | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by dnadales): And regarding the tests, isn't it possible to include the example you posted above as an integration test for the Control.Concurrent library? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: closed Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: fixed | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by dnadales): Reading from sockets leads to the same behavior. However, closing the socket before killing the thread works: {{{#!hs import qualified Data.ByteString.Char8 as C import Network.Socket hiding (recv, recvFrom, send, sendTo) import Network.Socket.ByteString readWithinNSecsBinary :: IO () readWithinNSecsBinary = withSocketsDo $ do addrinfos <- getAddrInfo Nothing (Just "") (Just "9090") let serveraddr = head addrinfos sock <- socket (addrFamily serveraddr) Stream defaultProtocol connect sock (addrAddress serveraddr) readerTid <- forkIO $ sockReader sock threadDelay (3 * 10^6) putStrLn "Killing the binary reader" putStrLn "Closing the socket" close sock -- Wihout this line the client will block putStrLn "Socket closed!" killThread readerTid putStrLn "Binary reader thread killed" where sockReader sock = do putStrLn "Receiving ..." msg <- recv sock 1024 putStr "Received " C.putStrLn msg }}} This results in {{{#!text Receiving ... Killing the binary reader Closing the socket Socket closed! dangling-connections-exe.EXE: Network.Socket.recvBuf: failed (No error) Binary reader thread killed }}} -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * status: closed => new * resolution: fixed => Comment: Reopening as the reporter still sees this in 8.2.1.
How did you launch the two processes? On a Unix like terminal emulator? (I've tried on MINGW64, but there it is even worse since I don't see any output). In my case I was using Windows Power Shell, but I get the same result in the old Windows Command Prompt.
And regarding the tests, isn't it possible to include the example you
Yes, in a mingw64 msys2 environment under `bash`. posted above as an integration test for the `Control.Concurrent` library? We could propose that it be added to `async`, but that won't help catch if GHC regresses. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#14503: Killing a thread will block if there is another process reading from a handle -------------------------------------+------------------------------------- Reporter: dnadales | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.2.1 Component: libraries/base | Version: 8.0.2 Resolution: | Keywords: Operating System: Windows | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by dnadales): Sorry that I don't quote your message but I'm replying from the phone. Could you see if the bug is reproducible from a windows command prompt? And for adding tests I guess it should not be added to async since it is not related to it (I would say). I don't know if I can help somehow. I'll be happy to write a small test, but if we cannot use sockets in the test then there's nothing I can do (besides setting up appveyor in a personal repository and check for this kind of regressions periodically). -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/14503#comment:11 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC