@@ -21,22 +21,25 @@ import Control.Arrow ( first )
2121import Foreign.C (CInt (.. ), throwErrnoIf_ )
2222import GHC.IO.Handle (Handle ())
2323#if defined(mingw32_HOST_OS)
24+ import Foreign.Marshal (alloca )
2425import Foreign.Ptr (Ptr , ptrToWordPtr , wordPtrToPtr )
25- import GHC.IO (onException )
26- import GHC.Windows (HANDLE )
26+ import Foreign.Storable (Storable (peek ))
27+ import GHC.IO.FD (mkFD )
28+ import GHC.IO.Handle (mkFileHandle , nativeNewlineMode )
2729import GHC.IO.Handle.FD (fdToHandle )
28- import GHC.IO.Device as IODevice
2930import GHC.IO.Encoding (getLocaleEncoding )
30- import GHC.IO.IOMode (IOMode (ReadMode , WriteMode , ReadWriteMode ))
31- import GHC.IO.Windows.Handle (fromHANDLE , Io (), NativeHandle ())
31+ import GHC.IO.IOMode (IOMode (ReadMode , WriteMode ))
3232## if defined(__IO_MANAGER_WINIO__)
33- import Foreign.Marshal
3433import Control.Exception (catch , throwIO )
34+ import GHC.IO (onException )
35+ import GHC.IO.Device as IODevice (close , devType )
3536import GHC.IO.Exception (IOException (.. ), IOErrorType (InvalidArgument ))
37+ import GHC.IO.IOMode (IOMode (ReadWriteMode ))
38+ import GHC.IO.Handle.Windows (mkHandleFromHANDLE )
3639import GHC.IO.SubSystem ((<!>) )
37- import GHC.IO.Handle. Windows ( handleToHANDLE , mkHandleFromHANDLE )
40+ import GHC.IO.Windows.Handle ( Io , NativeHandle , fromHANDLE )
3841import GHC.Event.Windows (associateHandle' )
39- import System.Process.Common ( StdStream ( CreatePipe ), mbPipeHANDLE )
42+ import GHC.Windows ( HANDLE )
4043## endif
4144
4245#include <fcntl.h> /* for _O_BINARY */
@@ -54,9 +57,7 @@ import GHC.IO.Handle.FD (handleToFd)
5457
5558import System.Process.Internals
5659 ( CreateProcess (.. ), ignoreSigPipe , withForkWait ,
57- ##if defined(mingw32_HOST_OS)
58- createPipeFd ,
59- ##else
60+ ##if !defined(mingw32_HOST_OS)
6061 createPipe
6162##endif
6263 )
@@ -103,6 +104,13 @@ newtype CommunicationHandle =
103104##endif
104105 deriving ( Eq , Ord )
105106
107+ #if defined(mingw32_HOST_OS)
108+ type Fd = CInt
109+ ## if !defined(__IO_MANAGER_WINIO__)
110+ type HANDLE = Ptr ()
111+ ## endif
112+ #endif
113+
106114-- @since 1.7.0.0
107115instance Show CommunicationHandle where
108116 showsPrec p (CommunicationHandle h) =
@@ -158,7 +166,7 @@ handleAssociateHandleIOError
158166 -- associateHandleWithIOCP: invalid argument (The parameter is incorrect.)
159167 | InvalidArgument <- errTy
160168 , Just 22 <- mbErrNo
161- = return ()
169+ = return () -- TODO: we could try to re-open the HANDLE in asynchronous mode.
162170 | otherwise
163171 = throwIO ioErr
164172##endif
@@ -177,15 +185,22 @@ closeCommunicationHandle (CommunicationHandle ch) =
177185
178186#if defined(mingw32_HOST_OS)
179187getGhcHandle :: HANDLE -> IO Handle
180- getGhcHandle = getGhcHandlePOSIX <!> getGhcHandleNative
188+ getGhcHandle =
189+ getGhcHandlePOSIX
190+ ## if defined(__IO_MANAGER_WINIO__)
191+ <!> getGhcHandleNative
192+ ## endif
181193
182194getGhcHandlePOSIX :: HANDLE -> IO Handle
183- getGhcHandlePOSIX handle =
184- _open_osfhandle handle (# const _O_BINARY) >>= fdToHandle
195+ getGhcHandlePOSIX handle = openHANDLE handle >>= fdToHandle
196+
197+ openHANDLE :: HANDLE -> IO Fd
198+ openHANDLE handle = _open_osfhandle handle (# const _O_BINARY)
185199
186200foreign import ccall " io.h _open_osfhandle"
187- _open_osfhandle :: HANDLE -> CInt -> IO CInt
201+ _open_osfhandle :: HANDLE -> CInt -> IO Fd
188202
203+ ## if defined(__IO_MANAGER_WINIO__)
189204getGhcHandleNative :: HANDLE -> IO Handle
190205getGhcHandleNative hwnd =
191206 do mb_codec <- fmap Just getLocaleEncoding
@@ -194,6 +209,7 @@ getGhcHandleNative hwnd =
194209 hw_type <- IODevice. devType $ native_handle
195210 mkHandleFromHANDLE native_handle hw_type (show hwnd) iomode mb_codec
196211 `onException` IODevice. close native_handle
212+ ## endif
197213#else
198214getGhcHandle :: Fd -> IO Handle
199215getGhcHandle fd = fdToHandle fd
@@ -228,94 +244,70 @@ createCommunicationPipe
228244 :: ( forall a . (a , a ) -> (a , a ) )
229245 -> IO (Handle , CommunicationHandle )
230246createCommunicationPipe mbSwap = do
231- -- On Windows:
232- -- - without WinIO, use FDs.
233- -- - with WinIO, use pipes.
234- -- On POSIX: use pipes.
235- ##if defined(mingw32_HOST_OS)
236- usingFDs
237- ## if defined(__IO_MANAGER_WINIO__)
238- <!> usingPipes
239- ## endif
247+ ##if !defined(mingw32_HOST_OS)
248+ (ourHandle, theirHandle) <- mbSwap <$> createPipe
249+ -- Don't allow the child process to inherit a parent file descriptor
250+ -- (such inheritance happens by default on Unix).
251+ ourFD <- Fd . fdFD <$> handleToFd ourHandle
252+ setFdOption ourFD CloseOnExec True
253+ theirFD <- Fd . fdFD <$> handleToFd theirHandle
254+ return (ourHandle, CommunicationHandle theirFD)
240255##else
241- usingPipes
242- ##endif
243- where
244- ##if !defined(mingw32_HOST_OS) || defined(__IO_MANAGER_WINIO__)
245- usingPipes :: IO (Handle , CommunicationHandle )
246- usingPipes = do
247- (hUs, hThem) <- createPipeEnds mbSwap
248- chThem <-
249- CommunicationHandle <$>
250- ## if defined(__IO_MANAGER_WINIO__)
251- handleToHANDLE hThem
252- ## else
253- (Fd . fdFD <$> handleToFd hThem)
256+ trueForWinIO <-
257+ return False
258+ ## if defined (__IO_MANAGER_WINIO__)
259+ <!> return True
254260## endif
255- associateToCurrentProcess hUs
256- return (hUs, chThem)
257- ##endif
258- ##if defined(mingw32_HOST_OS)
259- usingFDs :: IO (Handle , CommunicationHandle )
260- usingFDs = do
261- (fdRead, fdWrite) <- createPipeFd
262- let (fdUs, fdThem) = mbSwap (fdRead, fdWrite)
263- chThem <-
264- CommunicationHandle <$>
265- _get_osfhandle fdThem
266- hUs <- fdToHandle fdUs `onException` c__close fdUs
267- return (hUs, chThem)
268-
269- foreign import ccall unsafe " io.h _get_osfhandle"
270- _get_osfhandle :: CInt -> IO HANDLE
271-
272- foreign import ccall " io.h _close"
273- c__close :: CInt -> IO CInt
274- ##endif
275-
276- -- | Internal: create two ends of a pipe. The first result is the parent 'Handle',
277- -- while the second is a 'Handle' to be inherited by a child process.
278- --
279- -- The argument can be either @id@ (ours = read, theirs = write) or @swap@
280- -- (ours = write, theirs = read).
281- createPipeEnds :: ( forall a . (a , a ) -> (a , a ) )
282- -> IO (Handle , Handle )
283- createPipeEnds mbSwap =
284- ##if !defined(__IO_MANAGER_WINIO__)
285- mbSwap <$> createPipe
286- ##else
261+ -- On Windows, use mkNamedPipe to create the two pipe ends.
287262 alloca $ \ pfdStdInput ->
288263 alloca $ \ pfdStdOutput -> do
289264 let (inheritRead, inheritWrite) = mbSwap (False , True )
265+ -- If we're using WinIO, make the parent pipe end overlapped,
266+ -- otherwise make both pipe ends synchronous.
267+ overlappedRead = if inheritRead then False else trueForWinIO
268+ overlappedWrite = if inheritWrite then False else trueForWinIO
290269 throwErrnoIf_ (== False ) " c_mkNamedPipe" $
291270 -- Create one end to be un-inheritable and the other
292- -- to be inheritable, which ensures the un-inheritable part
293- -- can be properly associated with the parent process.
294- c_mkNamedPipe pfdStdInput inheritRead pfdStdOutput inheritWrite
295- Just hndStdInput <- mbPipeHANDLE CreatePipe pfdStdInput ReadMode
296- Just hndStdOutput <- mbPipeHANDLE CreatePipe pfdStdOutput WriteMode
297- return $ mbSwap (hndStdInput, hndStdOutput)
271+ -- to be inheritable, which ensures the parent end can be properly
272+ -- associated with the parent process.
273+ c_mkNamedPipe
274+ pfdStdInput inheritRead overlappedRead
275+ pfdStdOutput inheritWrite overlappedWrite
276+ let ((ourPfd, ourMode), (theirPfd, _theirMode)) =
277+ mbSwap ((pfdStdInput, ReadMode ), (pfdStdOutput, WriteMode ))
278+ ourHANDLE <- peek ourPfd
279+ theirHANDLE <- peek theirPfd
280+ -- With WinIO, we need to associate any handles we are going to use in
281+ -- the current process before being able to use them.
282+ return ()
283+ ## if defined (__IO_MANAGER_WINIO__)
284+ <!> associateHandle' ourHANDLE
285+ ## endif
286+ ourHandle <- createNonDuplexPipeHandle ourMode ourHANDLE
287+ return $ (ourHandle, CommunicationHandle theirHANDLE)
298288
299289foreign import ccall " mkNamedPipe" c_mkNamedPipe ::
300- Ptr HANDLE -> Bool -> Ptr HANDLE -> Bool -> IO Bool
301- ##endif
290+ Ptr HANDLE -> Bool -> Bool -> Ptr HANDLE -> Bool -> Bool -> IO Bool
302291
303- -- | Internal: associate the 'Handle' to the current process. This operation
304- -- ensures the handle can be properly read from/written to,
305- -- within the current process.
306- associateToCurrentProcess :: Handle -> IO ()
307- associateToCurrentProcess _h = do
308- ##if !defined(mingw32_HOST_OS)
309- fd <- Fd . fdFD <$> handleToFd _h
310- -- Don't allow the child process to inherit a parent file descriptor
311- -- (such inheritance happens by default on Unix).
312- setFdOption fd CloseOnExec True
313- ##else
314- return ()
292+ createNonDuplexPipeHandle :: IOMode -> HANDLE -> IO Handle
293+ createNonDuplexPipeHandle iomode raw_handle = do
294+ createNonDuplexPipeHandleFD
295+ ## if defined (__IO_MANAGER_WINIO__)
296+ <!> createNonDuplexPipeHandleNative
297+ ## endif
298+ where
299+ ident = " hwnd:" ++ show raw_handle
300+ createNonDuplexPipeHandleFD = do
301+ enc <- getLocaleEncoding
302+ fd <- openHANDLE raw_handle
303+ (dev, _) <- mkFD fd iomode Nothing False False
304+ mkFileHandle dev ident iomode (Just enc) nativeNewlineMode
315305## if defined (__IO_MANAGER_WINIO__)
316- -- With WinIO, we need to associate any handles we are going to use in
317- -- the current process before being able to use them.
318- <!> (associateHandle' =<< handleToHANDLE _h)
306+ createNonDuplexPipeHandleNative = do
307+ enc <- getLocaleEncoding
308+ let dev :: Io NativeHandle
309+ dev = fromHANDLE raw_handle
310+ mkFileHandle dev ident iomode (Just enc) nativeNewlineMode
319311## endif
320312##endif
321313
0 commit comments