Skip to content

Commit 79cf59a

Browse files
committed
WIP: use ReOpenFile to re-open CommunicationHandle in overlapped mode on WinIO
1 parent 7710b6e commit 79cf59a

File tree

2 files changed

+71
-24
lines changed

2 files changed

+71
-24
lines changed

System/Process/CommunicationHandle.hsc

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ import Foreign.Ptr (Ptr, ptrToWordPtr, wordPtrToPtr)
2727
import Foreign.Storable (Storable(peek))
2828
import GHC.IO.Handle.FD (fdToHandle)
2929
import GHC.IO.IOMode (IOMode(ReadMode, WriteMode))
30+
import GHC.Windows (throwGetLastError)
3031
## if defined(__IO_MANAGER_WINIO__)
3132
import Control.Exception (catch, throwIO)
33+
import Foreign.Ptr (nullPtr)
3234
import GHC.IO (onException)
3335
import GHC.IO.Device as IODevice (close, devType)
3436
import GHC.IO.Encoding (getLocaleEncoding)
@@ -147,15 +149,16 @@ openCommunicationHandleRead = useCommunicationHandle True
147149
openCommunicationHandleWrite :: CommunicationHandle -> IO Handle
148150
openCommunicationHandleWrite = useCommunicationHandle False
149151

150-
-- | Internal function used to define 'openCommunicationHandleRead' and
151-
-- openCommunicationHandleWrite.
152+
-- | Internal function for 'openCommunicationHandleRead' and
153+
-- 'openCommunicationHandleWrite'.
152154
useCommunicationHandle :: 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'.

cbits/win32/runProcess.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ mkNamedPipe (HANDLE* pHandleIn, BOOL isInheritableIn, BOOL isOverlappedIn,
145145
DWORD inAttr = isOverlappedIn ? FILE_FLAG_OVERLAPPED : 0;
146146
hTemporaryIn
147147
= CreateNamedPipeW (pipeName,
148-
PIPE_ACCESS_INBOUND | inAttr | FILE_FLAG_FIRST_PIPE_INSTANCE,
148+
PIPE_ACCESS_INBOUND | inAttr, // | FILE_FLAG_FIRST_PIPE_INSTANCE,
149149
PIPE_TYPE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS | PIPE_READMODE_MESSAGE | PIPE_WAIT,
150-
1, buffer_size, buffer_size,
150+
PIPE_UNLIMITED_INSTANCES, buffer_size, buffer_size,
151151
0,
152152
&secAttr);
153153
if (hTemporaryIn == INVALID_HANDLE_VALUE)
@@ -209,6 +209,26 @@ mkNamedPipe (HANDLE* pHandleIn, BOOL isInheritableIn, BOOL isOverlappedIn,
209209
return FALSE;
210210
}
211211

212+
/*
213+
* Function: reOpenFileOverlapped
214+
*
215+
* Purpose: attempts to re-open a HANDLE with the FILE_FLAG_OVERLAPPED flag
216+
* set, in order to allow asynchronous IO on the HANDLE.
217+
*/
218+
HANDLE
219+
reOpenFileOverlapped (HANDLE h, BOOL wantToRead)
220+
{ HANDLE h2 = ReOpenFile( h
221+
, wantToRead ? GENERIC_READ : GENERIC_WRITE
222+
, wantToRead ? FILE_SHARE_READ : FILE_SHARE_WRITE
223+
, FILE_FLAG_OVERLAPPED
224+
);
225+
if (h2 == INVALID_HANDLE_VALUE) {
226+
maperrno();
227+
return NULL;
228+
}
229+
return h2;
230+
}
231+
212232
static HANDLE
213233
createJob ()
214234
{

0 commit comments

Comments
 (0)