{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
module Hmssql.Connection.Backend.Windows
#if !defined(mingw32_HOST_OS)
() where
#else
( HandleIO
, hioHandle
, newHandleIO
, hioRecv
, hioSend
, hioSendMany
, hioStopReceiving
, hioShutdown
, hioClose
, backendHandleCount
, countBackendHandle
, openPipe
, openPipeWith
, createPipeClient
, createPipeInstance
, PipeListener
, pipeListenerOpen
, pipeListenerAccept
, pipeListenerClose
, pipeDisconnect
, abandonable
, lookupAddresses
, connectOnWorker
, requireRuntime
, throwWindows
, ioFailure
, stoppedError
, eRROR_HANDLE_EOF
, eRROR_BROKEN_PIPE
, eRROR_NO_DATA
, eRROR_PIPE_CONNECTED
, eRROR_OPERATION_ABORTED
, eRROR_IO_PENDING
, c_ReadFile
, c_WriteFile
, c_RtlNtStatusToDosError
, c_CancelIoEx
, c_ConnectNamedPipe
, c_DisconnectNamedPipe
, c_CloseHandle
) where
import Control.Concurrent (forkIO, forkOS, rtsSupportsBoundThreads, threadDelay)
import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
import Control.Concurrent.MVar
import Control.Exception
import Control.Monad (forever, join, unless, void, when)
import Data.Bits ((.|.))
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as BI
import Data.IORef
import Data.Word (Word32, Word8)
import Foreign.C.Error (Errno (..), eCONNABORTED, eCONNRESET, ePIPE)
import Foreign.C.String (CString, CWString, withCString, withCWString)
import Foreign.C.Types (CInt (..), CUIntPtr (..))
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Marshal.Alloc (alloca, allocaBytes, callocBytes, free)
import Foreign.Ptr (FunPtr, Ptr, castPtr, intPtrToPtr, nullPtr, plusPtr)
import Foreign.Storable (peek, peekByteOff, poke, pokeByteOff, sizeOf)
import GHC.Conc (labelThread)
import GHC.Conc.Windows (asyncDoProc)
import GHC.Exts (keepAlive#)
import GHC.IO (IO (..))
import GHC.IO.Exception (IOErrorType (..), IOException (..))
import GHC.IO.SubSystem (isWindowsNativeIO)
import Network.Socket (Socket)
import qualified Network.Socket as Socket
import qualified Network.Socket.Address as Socket (SocketAddress (..))
import qualified Network.Socket.Internal as Socket (throwSocketErrorCode)
import System.IO.Unsafe (unsafePerformIO)
import GHC.Windows (DWORD, ErrCode, HANDLE, errCodeToIOError, getLastError, iNFINITE)
data HandleIO = HandleIO
{ hioHandle :: !HANDLE
, hioReadOv :: !(Ptr ())
, hioWriteOv :: !(Ptr ())
, hioReadEvent :: !HANDLE
, hioWriteEvent :: !HANDLE
, hioReadWait :: !(Ptr Wait)
, hioWriteWait :: !(Ptr Wait)
, hioReadLock :: !(MVar ())
, hioWriteLock :: !(MVar ())
, hioStopRecv :: !(IORef Bool)
, hioShut :: !(IORef Bool)
, hioDead :: !(IORef Bool)
, hioClosed :: !(MVar Bool)
, hioPipe :: !Bool
, hioOnShutdown :: !(IO ())
, hioCloseHandle :: !(IO ())
}
newHandleIO :: HANDLE -> Bool -> IO () -> IO () -> IO HandleIO
newHandleIO h isPipe onShutdown closeIt = mask_ $ do
readEvent <- newEvent
writeEvent <- newEvent `onException` c_CloseHandle readEvent
readOv <- newOverlapped readEvent
writeOv <- newOverlapped writeEvent
let undo = do
_ <- c_CloseHandle readEvent
_ <- c_CloseHandle writeEvent
free readOv
free writeOv
hioReadWait <- newWait readOv readEvent `onException` undo
hioWriteWait <- newWait writeOv writeEvent `onException` (freeWait hioReadWait >> undo)
hioReadLock <- newMVar ()
hioWriteLock <- newMVar ()
hioStopRecv <- newIORef False
hioShut <- newIORef False
hioDead <- newIORef False
hioClosed <- newMVar False
countBackendHandle 1
pure HandleIO
{ hioHandle = h
, hioReadOv = readOv
, hioWriteOv = writeOv
, hioReadEvent = readEvent
, hioWriteEvent = writeEvent
, hioPipe = isPipe
, hioOnShutdown = onShutdown
, hioCloseHandle = closeIt
, ..
}
where
newEvent = do
ev <- c_CreateEventW nullPtr 1 0 nullPtr
when (ev == nullPtr) $ getLastError >>= throwWindows "CreateEvent"
pure ev
newOverlapped ev = do
ov <- callocBytes overlappedSize
pokeByteOff ov overlappedEventOffset (ev `plusPtr` 1)
pure ov
newWait ov ev
| rtsSupportsBoundThreads = pure nullPtr
| otherwise = do
w <- c_hmssql_wait_new h ov ev
when (w == nullPtr) $ getLastError >>= throwWindows "CreateEvent"
pure w
freeWait :: Ptr Wait -> IO ()
freeWait w = unless (w == nullPtr) (c_hmssql_wait_free w)
overlappedSize, overlappedEventOffset :: Int
overlappedSize = overlappedEventOffset + sizeOf (nullPtr :: Ptr ())
overlappedEventOffset = 2 * sizeOf (nullPtr :: Ptr ()) + 8
hioRecv :: HandleIO -> Ptr Word8 -> Int -> IO Int
hioRecv hio buf size = keepAlive hio $ withMVar (hioReadLock hio) $ \() -> do
refuse hio (hioStopRecv hio) "receive"
let ov = hioReadOv hio
r <- overlapped hio ov (hioReadEvent hio) (hioReadWait hio) (hioStopRecv hio) $
c_ReadFile (hioHandle hio) buf (fromIntegral size) nullPtr ov
case r of
Right n -> pure n
Left err
| err == eRROR_BROKEN_PIPE || err == eRROR_HANDLE_EOF -> pure 0
| otherwise -> failed hio (hioStopRecv hio) "receive" err
hioSend :: HandleIO -> B.ByteString -> IO ()
hioSend hio bs = hioSendMany hio [[bs]]
hioSendMany :: HandleIO -> [[B.ByteString]] -> IO ()
hioSendMany hio writes0 = case shape writes0 of
[] -> pure ()
writes -> keepAlive hio $ withMVar (hioWriteLock hio) $ \() -> mapM_ sendOne writes
where
ov = hioWriteOv hio
shape
| hioPipe hio = filter (not . B.null) . map B.concat
| otherwise = coalesce . filter (not . B.null) . concat
limit = if hioPipe hio then maxBound else maxWrite
sendOne (BI.BS fp len) = withForeignPtr fp $ \p -> go p len
go :: Ptr Word8 -> Int -> IO ()
go p remaining
| remaining <= 0 = pure ()
| otherwise = do
refuse hio (hioShut hio) "send"
r <- overlapped hio ov (hioWriteEvent hio) (hioWriteWait hio) (hioShut hio) $
c_WriteFile (hioHandle hio) p (fromIntegral (min remaining limit)) nullPtr ov
case r of
Right n | n > 0 -> go (p `plusPtr` n) (remaining - n)
Right _ -> throwIO (ioFailure ResourceVanished "send" "the handle accepted no bytes")
Left err -> failed hio (hioShut hio) "send" err
maxWrite :: Int
maxWrite = 65536
coalesce :: [B.ByteString] -> [B.ByteString]
coalesce = go [] 0
where
go acc _ [] = flush acc
go acc size (c : cs)
| size + B.length c > maxWrite = flush acc ++ go [c] (B.length c) cs
| otherwise = go (c : acc) (size + B.length c) cs
flush [] = []
flush [c] = [c]
flush acc = [B.concat (reverse acc)]
overlapped :: HandleIO -> Ptr () -> HANDLE -> Ptr Wait -> IORef Bool -> IO CInt -> IO (Either ErrCode Int)
overlapped hio ov event wait flag start = mask_ $ do
started <- onIssuer $ do
ok <- start
if ok /= 0 then pure Nothing else Just <$> getLastError
case started of
Nothing -> finished
Just err
| err /= eRROR_IO_PENDING -> pure (Left err)
| otherwise -> do
stopped <- readIORef flag
when stopped cancel
awaitEvent wait event cancel
finished
where
cancel = void (c_CancelIoEx (hioHandle hio) ov)
finished = do
status <- peek (castPtr ov) :: IO CUIntPtr
count <- peekByteOff ov (sizeOf (nullPtr :: Ptr ())) :: IO CUIntPtr
let ntStatus = fromIntegral status :: Word32
if ntStatus == 0
then pure (Right (fromIntegral count))
else Left <$> c_RtlNtStatusToDosError ntStatus
onIssuer :: IO a -> IO a
onIssuer act
| not rtsSupportsBoundThreads = act
| otherwise = do
reply <- newEmptyMVar
writeChan issuerQueue (try act >>= putMVar reply)
r <- uninterruptibleMask_ (takeMVar reply)
either (throwIO :: SomeException -> IO a) pure r
issuerQueue :: Chan (IO ())
issuerQueue = unsafePerformIO $ do
queue <- newChan
issuer <- forkOS (forever (join (readChan queue)))
labelThread issuer "hmssql: I/O issuer"
pure queue
{-# NOINLINE issuerQueue #-}
awaitEvent :: Ptr Wait -> HANDLE -> IO () -> IO ()
awaitEvent wait event cancel = do
now <- c_WaitForSingleObjectNow event 0
unless (now == wAIT_OBJECT_0) $
if rtsSupportsBoundThreads then onHelper else onWorkerThread
where
onHelper = do
done <- newEmptyMVar
helper <- forkIO (c_WaitForSingleObject event iNFINITE >>= putMVar done)
labelThread helper "hmssql: I/O wait"
r <- takeMVar done `catch` \(e :: SomeException) -> do
cancel
_ <- uninterruptibleMask_ (takeMVar done)
throwIO e
unless (r == wAIT_OBJECT_0) $
throwIO (ioFailure OtherError "wait" ("WaitForSingleObject returned " ++ show r))
onWorkerThread = do
c_hmssql_wait_reset wait
r <- asyncDoProc p_hmssql_wait_operation wait `catch` \(e :: SomeException) -> do
c_hmssql_wait_cancel wait
_ <- uninterruptibleMask_ (asyncDoProc p_hmssql_await_done (castPtr wait))
throwIO e
unless (r == 0) $
throwIO (ioFailure OtherError "wait" ("the wait for the operation returned " ++ show r))
refuse :: HandleIO -> IORef Bool -> String -> IO ()
refuse hio flag what = do
dead <- readIORef (hioDead hio)
stopped <- readIORef flag
when (dead || stopped) $ throwIO (stoppedError what)
failed :: HandleIO -> IORef Bool -> String -> ErrCode -> IO a
failed hio flag what err = do
stopped <- readIORef flag
if stopped && err == eRROR_OPERATION_ABORTED
then throwIO (stoppedError what)
else throwWindows what (if hioPipe hio then err else winsockCode err)
winsockCode :: ErrCode -> ErrCode
winsockCode err
| err == eRROR_NETNAME_DELETED = wSAECONNRESET
| err == eRROR_CONNECTION_ABORTED = wSAECONNABORTED
| otherwise = err
stoppedError :: String -> IOException
stoppedError what = ioFailure ResourceVanished what "the connection was shut down"
ioFailure :: IOErrorType -> String -> String -> IOException
ioFailure ty what why = IOError Nothing ty ("hmssql: " ++ what) why Nothing Nothing
throwWindows :: String -> ErrCode -> IO a
throwWindows what err = do
e <- errCodeToIOError ("hmssql: " ++ what) err
throwIO $ case lookup err peerGone of
Just (Errno errno) -> e { ioe_type = ResourceVanished, ioe_errno = Just errno }
Nothing -> e
where
peerGone =
[ (eRROR_NETNAME_DELETED, eCONNRESET)
, (wSAECONNRESET, eCONNRESET)
, (eRROR_PIPE_NOT_CONNECTED, eCONNRESET)
, (eRROR_CONNECTION_ABORTED, eCONNABORTED)
, (wSAECONNABORTED, eCONNABORTED)
, (eRROR_BROKEN_PIPE, ePIPE)
, (eRROR_NO_DATA, ePIPE)
, (wSAESHUTDOWN, ePIPE)
]
hioStopReceiving :: HandleIO -> IO ()
hioStopReceiving hio = mask_ $ do
atomicWriteIORef (hioStopRecv hio) True
withMVar (hioClosed hio) $ \closed ->
unless closed $ void (c_CancelIoEx (hioHandle hio) (hioReadOv hio))
hioShutdown :: HandleIO -> IO ()
hioShutdown hio
| hioPipe hio = hioClose hio
| otherwise = mask_ $ do
atomicWriteIORef (hioStopRecv hio) True
atomicWriteIORef (hioShut hio) True
withMVar (hioClosed hio) $ \closed -> unless closed $ do
hioOnShutdown hio `catch` \(_ :: IOException) -> pure ()
void (c_CancelIoEx (hioHandle hio) nullPtr)
hioClose :: HandleIO -> IO ()
hioClose hio = mask_ $ modifyMVar_ (hioClosed hio) $ \closed -> do
unless closed $ do
atomicWriteIORef (hioStopRecv hio) True
atomicWriteIORef (hioShut hio) True
_ <- c_CancelIoEx (hioHandle hio) nullPtr
uninterruptibleMask_ $ takeMVar (hioReadLock hio) >> takeMVar (hioWriteLock hio)
writeIORef (hioDead hio) True
hioCloseHandle hio
_ <- c_CloseHandle (hioReadEvent hio)
_ <- c_CloseHandle (hioWriteEvent hio)
free (hioReadOv hio)
free (hioWriteOv hio)
freeWait (hioReadWait hio)
freeWait (hioWriteWait hio)
countBackendHandle (-1)
putMVar (hioReadLock hio) ()
putMVar (hioWriteLock hio) ()
pure True
backendHandleCount :: IO Int
backendHandleCount = readIORef openHandles
countBackendHandle :: Int -> IO ()
countBackendHandle d = atomicModifyIORef' openHandles (\n -> (n + d, ()))
openHandles :: IORef Int
openHandles = unsafePerformIO (newIORef 0)
{-# NOINLINE openHandles #-}
openPipe :: FilePath -> IO HandleIO
openPipe path = do
requireRuntime
h <- openPipeWith path attempt
newHandleIO h True (pure ()) (void (c_CloseHandle h)) `onException` c_CloseHandle h
where
attempt
| rtsSupportsBoundThreads = abandonable (createPipeClient path) (either (\_ -> pure ()) (void . c_CloseHandle))
| otherwise = mask_ $ do
req <- withCWString path $ \p -> c_hmssql_open_new p pipeAccess pipeFlags
when (req == nullPtr) $ throwIO (outOfMemory "open")
_ <- onWorker p_hmssql_open_run req (c_hmssql_open_free req 0)
h <- c_hmssql_open_result req
err <- c_hmssql_open_error req
c_hmssql_open_free req 1
pure (if h == iNVALID_HANDLE_VALUE then Left err else Right h)
openPipeWith :: FilePath -> IO (Either ErrCode HANDLE) -> IO HANDLE
openPipeWith path attempt = go (pipeBusyWait `div` 10_000)
where
go :: Int -> IO HANDLE
go tries = attempt >>= \case
Right h -> pure h
Left err
| err == eRROR_PIPE_BUSY && tries > 0 -> threadDelay 10_000 >> go (tries - 1)
| otherwise -> throwWindows ("open " ++ path) err
createPipeClient :: FilePath -> IO (Either ErrCode HANDLE)
createPipeClient path = withCWString path $ \p -> do
h <- c_CreateFileW p pipeAccess 0 nullPtr oPEN_EXISTING pipeFlags nullPtr
if h == iNVALID_HANDLE_VALUE then Left <$> getLastError else pure (Right h)
pipeAccess, pipeFlags :: DWORD
pipeAccess = gENERIC_READ .|. gENERIC_WRITE
pipeFlags = fILE_FLAG_OVERLAPPED .|. sECURITY_SQOS_PRESENT .|. sECURITY_IDENTIFICATION
pipeBusyWait :: Int
pipeBusyWait = 30_000_000
data PipeListener = PipeListener
{ plPath :: !FilePath
, plPending :: !(MVar (Maybe HandleIO))
}
pipeListenerOpen :: FilePath -> IO PipeListener
pipeListenerOpen path = do
requireRuntime
mask_ $ do
first <- newInstance path True
PipeListener path <$> newMVar (Just first)
newInstance :: FilePath -> Bool -> IO HandleIO
newInstance path first = do
h <- createPipeInstance path first
newHandleIO h True (pure ()) (void (c_CloseHandle h)) `onException` c_CloseHandle h
createPipeInstance :: FilePath -> Bool -> IO HANDLE
createPipeInstance path first = do
h <- withCWString path $ \p -> c_CreateNamedPipeW p
(pIPE_ACCESS_DUPLEX .|. fILE_FLAG_OVERLAPPED .|. (if first then fILE_FLAG_FIRST_PIPE_INSTANCE else 0))
pIPE_REJECT_REMOTE_CLIENTS pIPE_UNLIMITED_INSTANCES 65536 65536 0 nullPtr
when (h == iNVALID_HANDLE_VALUE) $
getLastError >>= throwWindows ("create " ++ path)
pure h
pipeListenerAccept :: PipeListener -> IO HandleIO
pipeListenerAccept pl = mask_ $ takeMVar pending >>= \case
Nothing -> do
putMVar pending Nothing
throwIO (ioFailure IllegalOperation "accept" "the pipe listener is closed")
Just hio -> do
never <- newIORef False
let ov = hioReadOv hio
asItWas = putMVar pending (Just hio)
r <- overlapped hio ov (hioReadEvent hio) (hioReadWait hio) never (c_ConnectNamedPipe (hioHandle hio) ov)
`onException` asItWas
case r of
Right _ -> pure ()
Left err
| err == eRROR_PIPE_CONNECTED -> pure ()
| err == eRROR_NO_DATA -> pure ()
| otherwise -> asItWas >> throwWindows "accept" err
try (newInstance (plPath pl) False) >>= \case
Right next -> hio <$ putMVar pending (Just next)
Left (e :: SomeException) -> do
hioClose hio
putMVar pending Nothing
throwIO e
where
pending = plPending pl
pipeListenerClose :: PipeListener -> IO ()
pipeListenerClose pl = mask_ $ modifyMVar_ (plPending pl) $ \pending -> do
mapM_ hioClose pending
pure Nothing
pipeDisconnect :: HandleIO -> IO ()
pipeDisconnect hio = mask_ $ withMVar (hioClosed hio) $ \closed ->
unless closed $ void (c_DisconnectNamedPipe (hioHandle hio))
abandonable :: IO a -> (a -> IO ()) -> IO a
abandonable act cleanup = mask_ $ do
result <- newEmptyMVar
abandoned <- newMVar False
helper <- forkIO $ do
r <- try act
gone <- takeMVar abandoned
case r of
Right a | gone -> cleanup a `catch` \(_ :: SomeException) -> pure ()
_ -> pure ()
putMVar result r
putMVar abandoned gone
labelThread helper "hmssql: blocking call"
r <- takeMVar result `catch` \(e :: SomeException) -> uninterruptibleMask_ $ do
gone <- takeMVar abandoned
done <- tryTakeMVar result
putMVar abandoned True
case done of
Just (Right a) | not gone -> cleanup a `catch` \(_ :: SomeException) -> pure ()
_ -> pure ()
throwIO e
either (throwIO :: SomeException -> IO a) pure r
requireRuntime :: IO ()
requireRuntime = when (not rtsSupportsBoundThreads && isWindowsNativeIO) $
throwIO $ ioFailure UnsupportedOperation "connect"
("on Windows without -threaded, GHC's native I/O manager needs hmssql built against network 3.3 or later "
++ "with a GHC whose native manager network supports: link the program with -threaded, "
++ "or run it without --io-manager=native")
onWorker :: FunPtr (Ptr r -> IO Int) -> Ptr r -> IO () -> IO Int
onWorker proc req abandoned = asyncDoProc proc req `catch` \(e :: SomeException) -> do
helper <- forkIO $ uninterruptibleMask_ $ do
_ <- asyncDoProc p_hmssql_await_done (castPtr req)
abandoned
labelThread helper "hmssql: abandoned call"
throwIO e
lookupAddresses :: Socket.AddrInfo -> String -> String -> IO [Socket.AddrInfo]
lookupAddresses hints host port
| rtsSupportsBoundThreads = abandonable (Socket.getAddrInfo (Just hints) (Just host) (Just port)) (\_ -> pure ())
| otherwise = Socket.withSocketsDo $ mask_ $ do
requireRuntime
req <- withCString host $ \h -> withCString port $ \p -> alloca $ \hp -> do
poke hp hints
c_hmssql_lookup_new h p hp
when (req == nullPtr) $ throwIO (outOfMemory "getAddrInfo")
_ <- onWorker p_hmssql_lookup_run req (c_hmssql_lookup_free req)
flip finally (c_hmssql_lookup_free req) $ do
err <- c_hmssql_lookup_error req
unless (err == 0) $ do
e <- errCodeToIOError ("hmssql: getAddrInfo (host name " ++ show host ++ ", service name " ++ show port ++ ")")
(fromIntegral err)
throwIO e { ioe_type = NoSuchThing }
c_hmssql_lookup_result req >>= follow
where
follow ai
| ai == nullPtr = pure []
| otherwise = (:) <$> peek ai <*> (c_hmssql_addrinfo_next ai >>= follow)
connectOnWorker :: (Socket -> IO ()) -> Socket -> Socket.SockAddr -> IO ()
connectOnWorker closeIt sock addr = mask_ $ do
requireRuntime `onException` closeIt sock
fd <- Socket.unsafeFdSocket sock
let size = Socket.sizeOfSocketAddress addr
req <- allocaBytes size $ \p -> do
Socket.pokeSocketAddress p addr
c_hmssql_connect_new (fromIntegral fd) p (fromIntegral size)
when (req == nullPtr) $ closeIt sock >> throwIO (outOfMemory "connect")
_ <- onWorker p_hmssql_connect_run req (closeIt sock >> c_hmssql_connect_free req)
err <- c_hmssql_connect_error req
c_hmssql_connect_free req
unless (err == 0) $ do
closeIt sock
Socket.throwSocketErrorCode ("Network.Socket.connect: <socket: " ++ show fd ++ ">") err
outOfMemory :: String -> IOException
outOfMemory what = ioFailure ResourceExhausted what "out of memory"
keepAlive :: a -> IO b -> IO b
keepAlive x (IO act) = IO (\s -> keepAlive# x s act)
gENERIC_READ, gENERIC_WRITE, oPEN_EXISTING, fILE_FLAG_OVERLAPPED, fILE_FLAG_FIRST_PIPE_INSTANCE :: DWORD
gENERIC_READ = 0x80000000
gENERIC_WRITE = 0x40000000
oPEN_EXISTING = 3
fILE_FLAG_OVERLAPPED = 0x40000000
fILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
sECURITY_SQOS_PRESENT, sECURITY_IDENTIFICATION :: DWORD
sECURITY_SQOS_PRESENT = 0x00100000
sECURITY_IDENTIFICATION = 0x00010000
pIPE_ACCESS_DUPLEX, pIPE_REJECT_REMOTE_CLIENTS, pIPE_UNLIMITED_INSTANCES :: DWORD
pIPE_ACCESS_DUPLEX = 3
pIPE_REJECT_REMOTE_CLIENTS = 8
pIPE_UNLIMITED_INSTANCES = 255
wAIT_OBJECT_0 :: DWORD
wAIT_OBJECT_0 = 0
eRROR_HANDLE_EOF, eRROR_NETNAME_DELETED, eRROR_BROKEN_PIPE, eRROR_PIPE_BUSY, eRROR_NO_DATA,
eRROR_PIPE_NOT_CONNECTED, eRROR_PIPE_CONNECTED, eRROR_OPERATION_ABORTED, eRROR_IO_PENDING,
eRROR_CONNECTION_ABORTED, wSAECONNABORTED, wSAECONNRESET, wSAESHUTDOWN :: ErrCode
eRROR_HANDLE_EOF = 38
eRROR_NETNAME_DELETED = 64
eRROR_BROKEN_PIPE = 109
eRROR_PIPE_BUSY = 231
eRROR_NO_DATA = 232
eRROR_PIPE_NOT_CONNECTED = 233
eRROR_PIPE_CONNECTED = 535
eRROR_OPERATION_ABORTED = 995
eRROR_IO_PENDING = 997
eRROR_CONNECTION_ABORTED = 1236
wSAECONNABORTED = 10053
wSAECONNRESET = 10054
wSAESHUTDOWN = 10058
iNVALID_HANDLE_VALUE :: HANDLE
iNVALID_HANDLE_VALUE = intPtrToPtr (-1)
foreign import ccall unsafe "windows.h ReadFile"
c_ReadFile :: HANDLE -> Ptr Word8 -> DWORD -> Ptr DWORD -> Ptr () -> IO CInt
foreign import ccall unsafe "windows.h WriteFile"
c_WriteFile :: HANDLE -> Ptr Word8 -> DWORD -> Ptr DWORD -> Ptr () -> IO CInt
foreign import ccall unsafe "RtlNtStatusToDosError"
c_RtlNtStatusToDosError :: Word32 -> IO ErrCode
foreign import ccall unsafe "windows.h CancelIoEx"
c_CancelIoEx :: HANDLE -> Ptr () -> IO CInt
foreign import ccall unsafe "windows.h CreateEventW"
c_CreateEventW :: Ptr () -> CInt -> CInt -> CWString -> IO HANDLE
foreign import ccall unsafe "windows.h WaitForSingleObject"
c_WaitForSingleObjectNow :: HANDLE -> DWORD -> IO DWORD
foreign import ccall safe "windows.h WaitForSingleObject"
c_WaitForSingleObject :: HANDLE -> DWORD -> IO DWORD
foreign import ccall unsafe "windows.h ConnectNamedPipe"
c_ConnectNamedPipe :: HANDLE -> Ptr () -> IO CInt
foreign import ccall safe "windows.h CloseHandle"
c_CloseHandle :: HANDLE -> IO CInt
foreign import ccall safe "windows.h CreateFileW"
c_CreateFileW :: CWString -> DWORD -> DWORD -> Ptr () -> DWORD -> DWORD -> HANDLE -> IO HANDLE
foreign import ccall safe "windows.h CreateNamedPipeW"
c_CreateNamedPipeW :: CWString -> DWORD -> DWORD -> DWORD -> DWORD -> DWORD -> DWORD -> Ptr () -> IO HANDLE
foreign import ccall safe "windows.h DisconnectNamedPipe"
c_DisconnectNamedPipe :: HANDLE -> IO CInt
data Wait
data Lookup
data Connect
data Open
foreign import ccall unsafe "&hmssql_await_done"
p_hmssql_await_done :: FunPtr (Ptr () -> IO Int)
foreign import ccall unsafe "hmssql_wait_new"
c_hmssql_wait_new :: HANDLE -> Ptr () -> HANDLE -> IO (Ptr Wait)
foreign import ccall unsafe "hmssql_wait_free"
c_hmssql_wait_free :: Ptr Wait -> IO ()
foreign import ccall unsafe "hmssql_wait_reset"
c_hmssql_wait_reset :: Ptr Wait -> IO ()
foreign import ccall unsafe "hmssql_wait_cancel"
c_hmssql_wait_cancel :: Ptr Wait -> IO ()
foreign import ccall unsafe "&hmssql_wait_operation"
p_hmssql_wait_operation :: FunPtr (Ptr Wait -> IO Int)
foreign import ccall unsafe "hmssql_lookup_new"
c_hmssql_lookup_new :: CString -> CString -> Ptr Socket.AddrInfo -> IO (Ptr Lookup)
foreign import ccall unsafe "&hmssql_lookup_run"
p_hmssql_lookup_run :: FunPtr (Ptr Lookup -> IO Int)
foreign import ccall unsafe "hmssql_lookup_error"
c_hmssql_lookup_error :: Ptr Lookup -> IO CInt
foreign import ccall unsafe "hmssql_lookup_result"
c_hmssql_lookup_result :: Ptr Lookup -> IO (Ptr Socket.AddrInfo)
foreign import ccall unsafe "hmssql_addrinfo_next"
c_hmssql_addrinfo_next :: Ptr Socket.AddrInfo -> IO (Ptr Socket.AddrInfo)
foreign import ccall unsafe "hmssql_lookup_free"
c_hmssql_lookup_free :: Ptr Lookup -> IO ()
foreign import ccall unsafe "hmssql_connect_new"
c_hmssql_connect_new :: CUIntPtr -> Ptr () -> CInt -> IO (Ptr Connect)
foreign import ccall unsafe "&hmssql_connect_run"
p_hmssql_connect_run :: FunPtr (Ptr Connect -> IO Int)
foreign import ccall unsafe "hmssql_connect_error"
c_hmssql_connect_error :: Ptr Connect -> IO CInt
foreign import ccall unsafe "hmssql_connect_free"
c_hmssql_connect_free :: Ptr Connect -> IO ()
foreign import ccall unsafe "hmssql_open_new"
c_hmssql_open_new :: CWString -> DWORD -> DWORD -> IO (Ptr Open)
foreign import ccall unsafe "&hmssql_open_run"
p_hmssql_open_run :: FunPtr (Ptr Open -> IO Int)
foreign import ccall unsafe "hmssql_open_result"
c_hmssql_open_result :: Ptr Open -> IO HANDLE
foreign import ccall unsafe "hmssql_open_error"
c_hmssql_open_error :: Ptr Open -> IO ErrCode
foreign import ccall unsafe "hmssql_open_free"
c_hmssql_open_free :: Ptr Open -> CInt -> IO ()
#endif