{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
#include "HsNetDef.h"
module Network.Socket.Shutdown (
ShutdownCmd(..)
, shutdown
, gracefulClose
) where
import qualified Control.Exception as E
import Foreign.Marshal.Alloc (mallocBytes, free)
import Control.Concurrent (threadDelay)
#if !defined(mingw32_HOST_OS)
import Control.Concurrent (putMVar, takeMVar, newEmptyMVar)
import qualified GHC.Event as Ev
import System.Posix.Types (Fd(..))
#endif
import Network.Socket.Buffer
import Network.Socket.Imports
import Network.Socket.Internal
import Network.Socket.Types
data ShutdownCmd = ShutdownReceive
| ShutdownSend
| ShutdownBoth
deriving Typeable
sdownCmdToInt :: ShutdownCmd -> CInt
sdownCmdToInt ShutdownReceive = 0
sdownCmdToInt ShutdownSend = 1
sdownCmdToInt ShutdownBoth = 2
shutdown :: Socket -> ShutdownCmd -> IO ()
shutdown s stype = void $ withFdSocket s $ \fd ->
throwSocketErrorIfMinus1Retry_ "Network.Socket.shutdown" $
c_shutdown fd $ sdownCmdToInt stype
foreign import CALLCONV unsafe "shutdown"
c_shutdown :: CInt -> CInt -> IO CInt
#if !defined(mingw32_HOST_OS)
data Wait = MoreData | TimeoutTripped
#endif
gracefulClose :: Socket -> Int -> IO ()
gracefulClose s tmout = sendRecvFIN `E.finally` close s
where
sendRecvFIN = do
shutdown s ShutdownSend
#if defined(mingw32_HOST_OS)
recvEOFloop
#else
mevmgr <- Ev.getSystemEventManager
case mevmgr of
Nothing -> recvEOFloop
Just evmgr -> recvEOFev evmgr
#endif
clock = 200
recvEOFloop = E.bracket (mallocBytes bufSize) free $ loop 0
where
loop delay buf = do
r <- recvBufNoWait s buf bufSize
let delay' = delay + clock
when (r == -1 && delay' < tmout) $ do
threadDelay (clock * 1000)
loop delay' buf
#if !defined(mingw32_HOST_OS)
recvEOFev evmgr = do
tmmgr <- Ev.getSystemTimerManager
mvar <- newEmptyMVar
E.bracket (register evmgr tmmgr mvar) (unregister evmgr tmmgr) $ \_ -> do
wait <- takeMVar mvar
case wait of
TimeoutTripped -> return ()
MoreData -> E.bracket (mallocBytes bufSize)
free
(\buf -> void $ recvBufNoWait s buf bufSize)
register evmgr tmmgr mvar = do
key1 <- Ev.registerTimeout tmmgr (tmout * 1000) $
putMVar mvar TimeoutTripped
key2 <- withFdSocket s $ \fd' -> do
let callback _ _ = putMVar mvar MoreData
fd = Fd fd'
#if __GLASGOW_HASKELL__ < 709
Ev.registerFd evmgr callback fd Ev.evtRead
#else
Ev.registerFd evmgr callback fd Ev.evtRead Ev.OneShot
#endif
return (key1, key2)
unregister evmgr tmmgr (key1,key2) = do
Ev.unregisterTimeout tmmgr key1
Ev.unregisterFd evmgr key2
#endif
bufSize = 1024