Skip to content

Commit 17a28d6

Browse files
committed
Add experimental WinIO support
Another attempt at #364, #509, #602 with the following design choices: 1. On Windows systems, `Socket` becomes an `Either` over the existing "Posix" socket implemntation and a Windows equivalent using its `SOCKET` type. 2. On Posix systems, or without enabling the WinIO manager with `--io-manager=native`, the current behavior is unchanged. 3. *With* the WinIO manager, a greenfield implementation around the relevant syscalls is used, using IO Completion Ports (AKA "Overlapped IO") through GHC's `withOverlapped` For the places we need to mux between the two (`Network.Socket.Syscall`, `Network.Socket.Buffer`, etc.) the existing implementation is moved into a `Posix.hsc` and the WinIO one is defined in `WinIO.hsc`. There's certainly some cleanup and style improvements that could be done, but it passes all existing tests, and hopefully provides a clean separation between the existing code (which can stay unchanged) and direct use of Windows syscalls through `withOverlapped`.
1 parent 7a177c1 commit 17a28d6

File tree

18 files changed

+1550
-279
lines changed

18 files changed

+1550
-279
lines changed

Network/Socket/Buffer.hs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{-# LANGUAGE CPP #-}
2+
3+
#include "HsNetDef.h"
4+
5+
module Network.Socket.Buffer (
6+
sendBufTo
7+
, sendBuf
8+
, recvBufFrom
9+
, recvBuf
10+
, recvBufNoWait
11+
, sendBufMsg
12+
, recvBufMsg
13+
) where
14+
15+
import Network.Socket.Imports
16+
import Network.Socket.Types
17+
import Network.Socket.Flag
18+
19+
#if defined(mingw32_HOST_OS)
20+
import Network.Socket.Win32.CmsgHdr
21+
#else
22+
import Network.Socket.Posix.CmsgHdr
23+
#endif
24+
25+
import qualified Network.Socket.Buffer.Posix as Posix
26+
#if defined(mingw32_HOST_OS)
27+
import qualified Network.Socket.Buffer.WinIO as Win
28+
#endif
29+
30+
sendBufTo :: SocketAddress sa => Socket -> Ptr a -> Int -> sa -> IO Int
31+
#if defined(mingw32_HOST_OS)
32+
sendBufTo = eitherSocket Posix.sendBufTo Win.sendBufTo
33+
#else
34+
sendBufTo = Posix.sendBufTo
35+
#endif
36+
37+
sendBuf :: Socket -> Ptr Word8 -> Int -> IO Int
38+
#if defined(mingw32_HOST_OS)
39+
sendBuf = eitherSocket Posix.sendBuf Win.sendBuf
40+
#else
41+
sendBuf = Posix.sendBuf
42+
#endif
43+
44+
recvBufFrom :: SocketAddress sa => Socket -> Ptr a -> Int -> IO (Int, sa)
45+
#if defined(mingw32_HOST_OS)
46+
recvBufFrom = eitherSocket Posix.recvBufFrom Win.recvBufFrom
47+
#else
48+
recvBufFrom = Posix.recvBufFrom
49+
#endif
50+
51+
recvBuf :: Socket -> Ptr Word8 -> Int -> IO Int
52+
#if defined(mingw32_HOST_OS)
53+
recvBuf = eitherSocket Posix.recvBuf Win.recvBuf
54+
#else
55+
recvBuf = Posix.recvBuf
56+
#endif
57+
58+
recvBufNoWait :: Socket -> Ptr Word8 -> Int -> IO Int
59+
#if defined(mingw32_HOST_OS)
60+
recvBufNoWait = eitherSocket Posix.recvBufNoWait Win.recvBufNoWait
61+
#else
62+
recvBufNoWait = Posix.recvBufNoWait
63+
#endif
64+
65+
sendBufMsg :: SocketAddress sa
66+
=> Socket -> sa -> [(Ptr Word8,Int)] -> [Cmsg] -> MsgFlag -> IO Int
67+
#if defined(mingw32_HOST_OS)
68+
sendBufMsg = eitherSocket Posix.sendBufMsg Win.sendBufMsg
69+
#else
70+
sendBufMsg = Posix.sendBufMsg
71+
#endif
72+
73+
recvBufMsg :: SocketAddress sa
74+
=> Socket -> [(Ptr Word8,Int)] -> Int -> MsgFlag
75+
-> IO (sa,Int,[Cmsg],MsgFlag)
76+
#if defined(mingw32_HOST_OS)
77+
recvBufMsg = eitherSocket Posix.recvBufMsg Win.recvBufMsg
78+
#else
79+
recvBufMsg = Posix.recvBufMsg
80+
#endif
Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# include "windows.h"
66
#endif
77

8-
module Network.Socket.Buffer (
8+
module Network.Socket.Buffer.Posix (
99
sendBufTo
1010
, sendBuf
1111
, recvBufFrom
@@ -17,8 +17,6 @@ module Network.Socket.Buffer (
1717

1818
#if !defined(mingw32_HOST_OS)
1919
import Foreign.C.Error (getErrno, eAGAIN, eWOULDBLOCK)
20-
#else
21-
import Foreign.Ptr (nullPtr)
2220
#endif
2321
import Foreign.Marshal.Alloc (alloca, allocaBytes)
2422
import Foreign.Marshal.Utils (with)
@@ -27,6 +25,7 @@ import System.IO.Error (mkIOError, ioeSetErrorString, catchIOError)
2725

2826
#if defined(mingw32_HOST_OS)
2927
import GHC.IO.FD (FD(..), readRawBufferPtr, writeRawBufferPtr)
28+
import qualified Network.Socket.Types as Generic
3029
import Network.Socket.Win32.CmsgHdr
3130
import Network.Socket.Win32.MsgHdr
3231
import Network.Socket.Win32.WSABuf
@@ -37,14 +36,45 @@ import Network.Socket.Posix.IOVec
3736
#endif
3837

3938
import Network.Socket.Imports
39+
#if defined(mingw32_HOST_OS)
40+
import Network.Socket.Internal hiding (throwSocketErrorWaitRead, throwSocketErrorWaitWrite, throwSocketErrorWaitReadBut)
41+
#else
4042
import Network.Socket.Internal
41-
import Network.Socket.Name
42-
import Network.Socket.Types
43+
#endif
44+
import Network.Socket.Name (getPeerName)
45+
import Network.Socket.Types (
46+
SocketAddress,
47+
withSocketAddress,
48+
withNewSocketAddress,
49+
peekSocketAddress,
50+
)
51+
import Network.Socket.Types.Posix
4352
import Network.Socket.Flag
4453

4554
#if defined(mingw32_HOST_OS)
4655
type DWORD = Word32
4756
type LPDWORD = Ptr DWORD
57+
58+
-- On Windows, the generic throwSocketErrorWait* functions from Internal
59+
-- take the generic Socket type, not Posix.Socket. But on Windows these
60+
-- functions simply retry (the wait action is discarded), so the Socket
61+
-- parameter is unused. We define local versions for Posix.Socket.
62+
throwSocketErrorWaitRead :: (Eq a, Num a) => Socket -> String -> IO a -> IO a
63+
throwSocketErrorWaitRead _ = throwSocketErrorIfMinus1Retry
64+
65+
throwSocketErrorWaitWrite :: (Eq a, Num a) => Socket -> String -> IO a -> IO a
66+
throwSocketErrorWaitWrite _ = throwSocketErrorIfMinus1Retry
67+
68+
throwSocketErrorWaitReadBut :: (Eq a, Num a) => (CInt -> Bool) -> Socket -> String -> IO a -> IO a
69+
throwSocketErrorWaitReadBut exempt _ = throwSocketErrorIfMinus1ButRetry exempt
70+
71+
-- getPeerName takes the generic Socket; wrap our Posix.Socket.
72+
getPeerName' :: SocketAddress sa => Socket -> IO sa
73+
getPeerName' s = getPeerName (Generic.Socket (Left s))
74+
#else
75+
-- On non-Windows, Socket = Posix.Socket, so the generic versions work.
76+
getPeerName' :: SocketAddress sa => Socket -> IO sa
77+
getPeerName' = getPeerName
4878
#endif
4979

5080
-- | Send data to the socket. The recipient can be specified
@@ -122,7 +152,7 @@ recvBufFrom s ptr nbytes
122152
len <- throwSocketErrorWaitRead s "Network.Socket.recvBufFrom" $
123153
c_recvfrom fd ptr cnbytes flags ptr_sa ptr_len
124154
sockaddr <- peekSocketAddress ptr_sa
125-
`catchIOError` \_ -> getPeerName s
155+
`catchIOError` \_ -> getPeerName' s
126156
return (fromIntegral len, sockaddr)
127157

128158
-- | Receive data from the socket. The socket must be in a connected
@@ -290,11 +320,11 @@ recvBufMsg s bufsizs clen flags = do
290320
c_recvmsg fd msgHdrPtr len_ptr nullPtr nullPtr
291321
peek len_ptr
292322
#endif
293-
sockaddr <- peekSocketAddress addrPtr `catchIOError` \_ -> getPeerName s
323+
sockaddr <- peekSocketAddress addrPtr `catchIOError` \_ -> getPeerName' s
294324
hdr <- peek msgHdrPtr
295-
cmsgs <- parseCmsgs msgHdrPtr
325+
cmsgs' <- parseCmsgs msgHdrPtr
296326
let flags' = MsgFlag $ fromIntegral $ msgFlags hdr
297-
return (sockaddr, len, cmsgs, flags')
327+
return (sockaddr, len, cmsgs', flags')
298328

299329
#if !defined(mingw32_HOST_OS)
300330
foreign import ccall unsafe "send"
@@ -321,4 +351,3 @@ foreign import CALLCONV SAFE_ON_WIN "sendto"
321351
c_sendto :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> CInt -> IO CInt
322352
foreign import CALLCONV SAFE_ON_WIN "recvfrom"
323353
c_recvfrom :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> Ptr CInt -> IO CInt
324-

0 commit comments

Comments
 (0)