@@ -27,8 +27,10 @@ import Foreign.Ptr (Ptr, ptrToWordPtr, wordPtrToPtr)
27
27
import Foreign.Storable (Storable (peek ))
28
28
import GHC.IO.Handle.FD (fdToHandle )
29
29
import GHC.IO.IOMode (IOMode (ReadMode , WriteMode ))
30
+ import GHC.Windows (throwGetLastError )
30
31
## if defined(__IO_MANAGER_WINIO__)
31
32
import Control.Exception (catch , throwIO )
33
+ import Foreign.Ptr (nullPtr )
32
34
import GHC.IO (onException )
33
35
import GHC.IO.Device as IODevice (close , devType )
34
36
import GHC.IO.Encoding (getLocaleEncoding )
@@ -147,15 +149,16 @@ openCommunicationHandleRead = useCommunicationHandle True
147
149
openCommunicationHandleWrite :: CommunicationHandle -> IO Handle
148
150
openCommunicationHandleWrite = useCommunicationHandle False
149
151
150
- -- | Internal function used to define 'openCommunicationHandleRead' and
151
- -- openCommunicationHandleWrite.
152
+ -- | Internal function for 'openCommunicationHandleRead' and
153
+ -- ' openCommunicationHandleWrite' .
152
154
useCommunicationHandle :: Bool -> CommunicationHandle -> IO Handle
153
- useCommunicationHandle wantToRead (CommunicationHandle ch) = do
155
+ useCommunicationHandle _wantToRead (CommunicationHandle ch) = do
156
+ ch' <-
157
+ return ch
154
158
##if defined(__IO_MANAGER_WINIO__)
155
- return ()
156
- <!> associateHandleWithFallback wantToRead ch
159
+ <!> associateHandleWithFallback _wantToRead ch
157
160
##endif
158
- getGhcHandle ch
161
+ getGhcHandle ch'
159
162
160
163
##if defined(__IO_MANAGER_WINIO__)
161
164
-- Internal function used when associating a 'HANDLE' with the current process.
@@ -165,27 +168,51 @@ useCommunicationHandle wantToRead (CommunicationHandle ch) = do
165
168
--
166
169
-- In a child process, we don't necessarily know which kind of handle we will receive,
167
170
-- 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).
170
174
--
171
175
-- With the current API, inheritable handles in WinIO created with mkNamedPipe
172
176
-- are synchronous, but it's best to be safe in case the child receives an
173
177
-- 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
177
180
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
189
216
##endif
190
217
191
218
-- | Close a 'CommunicationHandle'.
0 commit comments