@@ -27,8 +27,10 @@ import Foreign.Ptr (Ptr, ptrToWordPtr, wordPtrToPtr)
2727import Foreign.Storable (Storable (peek ))
2828import GHC.IO.Handle.FD (fdToHandle )
2929import GHC.IO.IOMode (IOMode (ReadMode , WriteMode ))
30+ import GHC.Windows (throwGetLastError )
3031## if defined(__IO_MANAGER_WINIO__)
3132import Control.Exception (catch , throwIO )
33+ import Foreign.Ptr (nullPtr )
3234import GHC.IO (onException )
3335import GHC.IO.Device as IODevice (close , devType )
3436import GHC.IO.Encoding (getLocaleEncoding )
@@ -147,15 +149,16 @@ openCommunicationHandleRead = useCommunicationHandle True
147149openCommunicationHandleWrite :: CommunicationHandle -> IO Handle
148150openCommunicationHandleWrite = useCommunicationHandle False
149151
150- -- | Internal function used to define 'openCommunicationHandleRead' and
151- -- openCommunicationHandleWrite.
152+ -- | Internal function for 'openCommunicationHandleRead' and
153+ -- ' openCommunicationHandleWrite' .
152154useCommunicationHandle :: Bool -> CommunicationHandle -> IO Handle
153- useCommunicationHandle wantToRead (CommunicationHandle ch) = do
155+ useCommunicationHandle _wantToRead (CommunicationHandle ch) = do
156+ ch' <-
157+ return ch
154158##if defined(__IO_MANAGER_WINIO__)
155- return ()
156- <!> associateHandleWithFallback wantToRead ch
159+ <!> associateHandleWithFallback _wantToRead ch
157160##endif
158- getGhcHandle ch
161+ getGhcHandle ch'
159162
160163##if defined(__IO_MANAGER_WINIO__)
161164-- Internal function used when associating a 'HANDLE' with the current process.
@@ -165,27 +168,51 @@ useCommunicationHandle wantToRead (CommunicationHandle ch) = do
165168--
166169-- In a child process, we don't necessarily know which kind of handle we will receive,
167170-- so we try to associate it (in case it is an asynchronous handle). This might
168- -- fail (if the handle is synchronous), in which case we continue in synchronous
169- -- mode (without associating).
171+ -- fail (if the handle is synchronous), in which case we try to re-open the handle
172+ -- in asynchronous mode. If this succeeds, we associate the handle, otherwise
173+ -- we continue in synchronous mode (without associating).
170174--
171175-- With the current API, inheritable handles in WinIO created with mkNamedPipe
172176-- are synchronous, but it's best to be safe in case the child receives an
173177-- asynchronous handle anyway.
174- associateHandleWithFallback :: Bool -> HANDLE -> IO ()
175- associateHandleWithFallback _wantToRead h =
176- associateHandle' h `catch` handler
178+ associateHandleWithFallback :: Bool -> HANDLE -> IO HANDLE
179+ associateHandleWithFallback wantToRead = go True
177180 where
178- handler :: IOError -> IO ()
179- handler ioErr@ (IOError { ioe_handle = _mbErrHandle, ioe_type = errTy, ioe_errno = mbErrNo })
180- -- Catches the following error that occurs when attemping to associate
181- -- a HANDLE that does not have OVERLAPPING mode set:
182- --
183- -- associateHandleWithIOCP: invalid argument (The parameter is incorrect.)
184- | InvalidArgument <- errTy
185- , Just 22 <- mbErrNo
186- = return ()
187- | otherwise
188- = throwIO ioErr
181+ go :: Bool -> HANDLE -> IO HANDLE
182+ go tryReOpening h = do
183+ ( associateHandle' h *> return h ) `catch` ( handler tryReOpening h )
184+ handler :: Bool -> HANDLE -> IOError -> IO HANDLE
185+ handler tryReOpening h
186+ ioErr@ (IOError { ioe_handle = _mbErrHandle, ioe_type = errTy, ioe_errno = mbErrNo })
187+ -- Catches the following error that occurs when attemping to associate
188+ -- a HANDLE that does not have OVERLAPPING mode set:
189+ --
190+ -- associateHandleWithIOCP: invalid argument (The parameter is incorrect.)
191+ | InvalidArgument <- errTy
192+ , Just 22 <- mbErrNo
193+ = if tryReOpening
194+ then do
195+ -- Try to re-open the HANDLE in overlapped mode.
196+ --
197+ -- TODO: this seems to never actual works; we get:
198+ --
199+ -- > permission denied (Access is denied.)
200+ --
201+ -- It seems we can't re-open one side of a pipe created with
202+ -- mkNamedPipe, even without FILE_FLAG_FIRST_PIPE_INSTANCE and
203+ -- with PIPE_UNLIMITED_INSTANCES.
204+ h' <- reOpenFileOverlapped h wantToRead
205+ if h' /= nullPtr
206+ -- re-opening succeeded; now try associating the new handle
207+ then go False h'
208+ -- re-opening failed
209+ else throwGetLastError " reOpenFileOverlapped"
210+ else return h
211+ | otherwise
212+ = throwIO ioErr
213+
214+ foreign import ccall " reOpenFileOverlapped"
215+ reOpenFileOverlapped :: HANDLE -> Bool -> IO HANDLE
189216##endif
190217
191218-- | Close a 'CommunicationHandle'.
0 commit comments