GHC 8.2 GCed too early
The network package uses mkWeakVar so that unreachable Sockets can be GCed. See the master branch of https://github.com/haskell/network
mkSocket :: CInt
-> Family
-> SocketType
-> ProtocolNumber
-> SocketStatus
-> IO Socket
mkSocket fd fam sType pNum stat = do
mStat <- newMVar stat
withSocketsDo $ return ()
let sock = Socket fd fam sType pNum mStat
##if MIN_VERSION_base(4,6,0)
_ <- mkWeakMVar mStat $ close sock
##endif
return sock
The following code cause "threadWait: invalid argument (Bad file descriptor)" in accept:
module Main where
import Control.Concurrent (forkIO)
import Control.Monad (void, forever)
import Network.Socket hiding (recv)
import Network.Socket.ByteString (recv, sendAll)
main :: IO ()
main = do
let localhost = "localhost"
listenPort = "9876"
connectPort = "6789"
proxy localhost listenPort connectPort
proxy :: HostName -> ServiceName -> ServiceName -> IO ()
proxy localhost listenPort connectPort = do
fromClient <- serverSocket localhost listenPort
toServer <- clientSocket localhost connectPort
void $ forkIO $ relay toServer fromClient
relay fromClient toServer
relay :: Socket -> Socket -> IO ()
relay s1 s2 = forever $ do
payload <- recv s1 4096
sendAll s2 payload
serverSocket :: HostName -> ServiceName -> IO Socket
serverSocket host port = do
let hints = defaultHints {
addrFlags = [AI_PASSIVE]
, addrSocketType = Stream
}
addr:_ <- getAddrInfo (Just hints) (Just host) (Just port)
sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
bind sock (addrAddress addr)
listen sock 1
fst <$> accept sock
clientSocket :: HostName -> ServiceName -> IO Socket
clientSocket host port = do
let hints = defaultHints { addrSocketType = Stream }
addr:_ <- getAddrInfo (Just hints) (Just host) (Just port)
sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
connect sock (addrAddress addr)
return sock
GC frees sock while accepting. This occurs with GHC 8.2 or higher.