@@ -21,22 +21,25 @@ import Control.Arrow ( first )
21
21
import Foreign.C (CInt (.. ), throwErrnoIf_ )
22
22
import GHC.IO.Handle (Handle ())
23
23
#if defined(mingw32_HOST_OS)
24
+ import Foreign.Marshal (alloca )
24
25
import 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 )
27
29
import GHC.IO.Handle.FD (fdToHandle )
28
- import GHC.IO.Device as IODevice
29
30
import 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 ))
32
32
## if defined(__IO_MANAGER_WINIO__)
33
- import Foreign.Marshal
34
33
import Control.Exception (catch , throwIO )
34
+ import GHC.IO (onException )
35
+ import GHC.IO.Device as IODevice (close , devType )
35
36
import GHC.IO.Exception (IOException (.. ), IOErrorType (InvalidArgument ))
37
+ import GHC.IO.IOMode (IOMode (ReadWriteMode ))
38
+ import GHC.IO.Handle.Windows (mkHandleFromHANDLE )
36
39
import GHC.IO.SubSystem ((<!>) )
37
- import GHC.IO.Handle. Windows ( handleToHANDLE , mkHandleFromHANDLE )
40
+ import GHC.IO.Windows.Handle ( Io , NativeHandle , fromHANDLE )
38
41
import GHC.Event.Windows (associateHandle' )
39
- import System.Process.Common ( StdStream ( CreatePipe ), mbPipeHANDLE )
42
+ import GHC.Windows ( HANDLE )
40
43
## endif
41
44
42
45
#include <fcntl.h> /* for _O_BINARY */
@@ -54,9 +57,7 @@ import GHC.IO.Handle.FD (handleToFd)
54
57
55
58
import System.Process.Internals
56
59
( CreateProcess (.. ), ignoreSigPipe , withForkWait ,
57
- ##if defined(mingw32_HOST_OS)
58
- createPipeFd ,
59
- ##else
60
+ ##if !defined(mingw32_HOST_OS)
60
61
createPipe
61
62
##endif
62
63
)
@@ -103,6 +104,13 @@ newtype CommunicationHandle =
103
104
##endif
104
105
deriving ( Eq , Ord )
105
106
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
+
106
114
-- @since 1.7.0.0
107
115
instance Show CommunicationHandle where
108
116
showsPrec p (CommunicationHandle h) =
@@ -158,7 +166,7 @@ handleAssociateHandleIOError
158
166
-- associateHandleWithIOCP: invalid argument (The parameter is incorrect.)
159
167
| InvalidArgument <- errTy
160
168
, Just 22 <- mbErrNo
161
- = return ()
169
+ = return () -- TODO: we could try to re-open the HANDLE in asynchronous mode.
162
170
| otherwise
163
171
= throwIO ioErr
164
172
##endif
@@ -177,15 +185,22 @@ closeCommunicationHandle (CommunicationHandle ch) =
177
185
178
186
#if defined(mingw32_HOST_OS)
179
187
getGhcHandle :: HANDLE -> IO Handle
180
- getGhcHandle = getGhcHandlePOSIX <!> getGhcHandleNative
188
+ getGhcHandle =
189
+ getGhcHandlePOSIX
190
+ ## if defined(__IO_MANAGER_WINIO__)
191
+ <!> getGhcHandleNative
192
+ ## endif
181
193
182
194
getGhcHandlePOSIX :: 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)
185
199
186
200
foreign import ccall " io.h _open_osfhandle"
187
- _open_osfhandle :: HANDLE -> CInt -> IO CInt
201
+ _open_osfhandle :: HANDLE -> CInt -> IO Fd
188
202
203
+ ## if defined(__IO_MANAGER_WINIO__)
189
204
getGhcHandleNative :: HANDLE -> IO Handle
190
205
getGhcHandleNative hwnd =
191
206
do mb_codec <- fmap Just getLocaleEncoding
@@ -194,6 +209,7 @@ getGhcHandleNative hwnd =
194
209
hw_type <- IODevice. devType $ native_handle
195
210
mkHandleFromHANDLE native_handle hw_type (show hwnd) iomode mb_codec
196
211
`onException` IODevice. close native_handle
212
+ ## endif
197
213
#else
198
214
getGhcHandle :: Fd -> IO Handle
199
215
getGhcHandle fd = fdToHandle fd
@@ -228,94 +244,70 @@ createCommunicationPipe
228
244
:: ( forall a . (a , a ) -> (a , a ) )
229
245
-> IO (Handle , CommunicationHandle )
230
246
createCommunicationPipe 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)
240
255
##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
254
260
## 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.
287
262
alloca $ \ pfdStdInput ->
288
263
alloca $ \ pfdStdOutput -> do
289
264
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
290
269
throwErrnoIf_ (== False ) " c_mkNamedPipe" $
291
270
-- 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)
298
288
299
289
foreign 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
302
291
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
315
305
## 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
319
311
## endif
320
312
##endif
321
313
0 commit comments