Skip to content

Commit 692df89

Browse files
committed
CommunicationHandle: always use mkNamedPipe on Windows
1 parent de7379d commit 692df89

File tree

3 files changed

+97
-105
lines changed

3 files changed

+97
-105
lines changed

System/Process/CommunicationHandle.hsc

Lines changed: 85 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,25 @@ import Control.Arrow ( first )
2121
import Foreign.C (CInt(..), throwErrnoIf_)
2222
import GHC.IO.Handle (Handle())
2323
#if defined(mingw32_HOST_OS)
24+
import Foreign.Marshal (alloca)
2425
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)
2729
import GHC.IO.Handle.FD (fdToHandle)
28-
import GHC.IO.Device as IODevice
2930
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))
3232
## if defined(__IO_MANAGER_WINIO__)
33-
import Foreign.Marshal
3433
import Control.Exception (catch, throwIO)
34+
import GHC.IO (onException)
35+
import GHC.IO.Device as IODevice (close, devType)
3536
import GHC.IO.Exception (IOException(..), IOErrorType(InvalidArgument))
37+
import GHC.IO.IOMode (IOMode(ReadWriteMode))
38+
import GHC.IO.Handle.Windows (mkHandleFromHANDLE)
3639
import GHC.IO.SubSystem ((<!>))
37-
import GHC.IO.Handle.Windows (handleToHANDLE, mkHandleFromHANDLE)
40+
import GHC.IO.Windows.Handle (Io, NativeHandle, fromHANDLE)
3841
import 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

5558
import 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
107115
instance 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)
179187
getGhcHandle :: HANDLE -> IO Handle
180-
getGhcHandle = getGhcHandlePOSIX <!> getGhcHandleNative
188+
getGhcHandle =
189+
getGhcHandlePOSIX
190+
## if defined(__IO_MANAGER_WINIO__)
191+
<!> getGhcHandleNative
192+
## endif
181193

182194
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)
185199

186200
foreign 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__)
189204
getGhcHandleNative :: HANDLE -> IO Handle
190205
getGhcHandleNative 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
198214
getGhcHandle :: Fd -> IO Handle
199215
getGhcHandle fd = fdToHandle fd
@@ -228,94 +244,70 @@ createCommunicationPipe
228244
:: ( forall a. (a, a) -> (a, a) )
229245
-> IO (Handle, CommunicationHandle)
230246
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)
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

299289
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
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

System/Process/Windows.hsc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,14 +515,14 @@ createPipeInternalHANDLE =
515515
alloca $ \ pfdStdInput ->
516516
alloca $ \ pfdStdOutput -> do
517517
throwErrnoIf_ (==False) "c_mkNamedPipe" $
518-
c_mkNamedPipe pfdStdInput True pfdStdOutput True
518+
c_mkNamedPipe pfdStdInput True False pfdStdOutput True False
519519
Just hndStdInput <- mbPipeHANDLE CreatePipe pfdStdInput ReadMode
520520
Just hndStdOutput <- mbPipeHANDLE CreatePipe pfdStdOutput WriteMode
521521
return (hndStdInput, hndStdOutput)
522522

523523

524524
foreign import ccall "mkNamedPipe" c_mkNamedPipe ::
525-
Ptr HANDLE -> Bool -> Ptr HANDLE -> Bool -> IO Bool
525+
Ptr HANDLE -> Bool -> Bool -> Ptr HANDLE -> Bool -> Bool -> IO Bool
526526
##endif
527527

528528
close' :: CInt -> IO ()

cbits/win32/runProcess.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
8888
* asynchronously while anonymous pipes require blocking calls.
8989
*/
9090
BOOL
91-
mkNamedPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
92-
HANDLE* pHandleOut, BOOL isInheritableOut)
91+
mkNamedPipe (HANDLE* pHandleIn, BOOL isInheritableIn, BOOL isOverlappedIn,
92+
HANDLE* pHandleOut, BOOL isInheritableOut, BOOL isOverlappedOut)
9393
{
9494
HANDLE hTemporaryIn = INVALID_HANDLE_VALUE;
9595
HANDLE hTemporaryOut = INVALID_HANDLE_VALUE;
@@ -142,7 +142,7 @@ mkNamedPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
142142
bytes and the error ERROR_NO_DATA."[0]
143143
144144
[0] https://devblogs.microsoft.com/oldnewthing/20110114-00/?p=11753 */
145-
DWORD inAttr = isInheritableIn ? 0 : FILE_FLAG_OVERLAPPED;
145+
DWORD inAttr = isOverlappedIn ? FILE_FLAG_OVERLAPPED : 0;
146146
hTemporaryIn
147147
= CreateNamedPipeW (pipeName,
148148
PIPE_ACCESS_INBOUND | inAttr | FILE_FLAG_FIRST_PIPE_INSTANCE,
@@ -162,9 +162,9 @@ mkNamedPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
162162
FILE_SHARE_WRITE,
163163
&secAttr,
164164
OPEN_EXISTING,
165-
isInheritableOut
166-
? FILE_ATTRIBUTE_NORMAL
167-
: FILE_FLAG_OVERLAPPED,
165+
isOverlappedOut
166+
? FILE_FLAG_OVERLAPPED
167+
: FILE_ATTRIBUTE_NORMAL,
168168
NULL);
169169

170170
if (hTemporaryOut == INVALID_HANDLE_VALUE)
@@ -244,21 +244,21 @@ createJob ()
244244
static inline bool
245245
setStdHandleInfo (LPHANDLE destination, HANDLE _stdhandle,
246246
LPHANDLE hStdRead, LPHANDLE hStdWrite, HANDLE defaultStd,
247-
BOOL isInhertibleIn, BOOL isInhertibleOut, BOOL asynchronous)
247+
BOOL isInheritableIn, BOOL isInheritableOut, BOOL asynchronous)
248248
{
249249
BOOL status;
250250
assert (destination);
251251
assert (hStdRead);
252252
assert (hStdWrite);
253253

254-
LPHANDLE tmpHandle = isInhertibleOut ? hStdWrite : hStdRead;
254+
LPHANDLE tmpHandle = isInheritableOut ? hStdWrite : hStdRead;
255255

256256
if (_stdhandle == (HANDLE)-1) {
257257
if (!asynchronous
258-
&& !mkAnonPipe(hStdRead, isInhertibleIn, hStdWrite, isInhertibleOut))
258+
&& !mkAnonPipe(hStdRead, isInheritableIn, hStdWrite, isInheritableOut))
259259
return false;
260260
if (asynchronous
261-
&& !mkNamedPipe(hStdRead, isInhertibleIn, hStdWrite, isInhertibleOut))
261+
&& !mkNamedPipe(hStdRead, isInheritableIn, !isInheritableIn, hStdWrite, isInheritableOut, !isInheritableOut))
262262
return false;
263263
*destination = *tmpHandle;
264264
} else if (_stdhandle == (HANDLE)-2) {

0 commit comments

Comments
 (0)