@@ -214,17 +214,25 @@ final class AsyncIO: @unchecked Sendable {
214
214
215
215
// Windows Documentation: The function returns the handle
216
216
// of the existing I/O completion port if successful
217
- guard
218
- CreateIoCompletionPort (
219
- handle, ioPort, completionKey, 0
220
- ) == ioPort
221
- else {
222
- let error = SubprocessError (
223
- code: . init( . asyncIOFailed( " CreateIoCompletionPort failed " ) ) ,
224
- underlyingError: . init( rawValue: GetLastError ( ) )
225
- )
226
- continuation. finish ( throwing: error)
227
- return
217
+ if CreateIoCompletionPort (
218
+ handle, ioPort, completionKey, 0
219
+ ) != ioPort {
220
+ let lastError = GetLastError ( )
221
+ if lastError != ERROR_INVALID_PARAMETER {
222
+ let error = SubprocessError (
223
+ code: . init( . asyncIOFailed( " CreateIoCompletionPort failed " ) ) ,
224
+ underlyingError: . init( rawValue: GetLastError ( ) )
225
+ )
226
+ continuation. finish ( throwing: error)
227
+ return
228
+ } else {
229
+ // Special Case:
230
+ //
231
+ // * ERROR_INVALID_PARAMETER - The handle likely doesn't have FILE_FLAG_OVERLAPPED, which might indicate that it isn't a pipe
232
+ // so we can just signal that it is ready for reading right away.
233
+ //
234
+ continuation. yield ( UInt32 . max)
235
+ }
228
236
}
229
237
// Now save the continuation
230
238
_registration. withLock { storage in
@@ -271,6 +279,7 @@ final class AsyncIO: @unchecked Sendable {
271
279
// We use an empty `_OVERLAPPED()` here because `ReadFile` below
272
280
// only reads non-seekable files, aka pipes.
273
281
var overlapped = _OVERLAPPED ( )
282
+ var bytesReadIfSynchronous = UInt32 ( 0 )
274
283
let succeed = try resultBuffer. withUnsafeMutableBufferPointer { bufferPointer in
275
284
// Get a pointer to the memory at the specified offset
276
285
// Windows ReadFile uses DWORD for target count, which means we can only
@@ -286,7 +295,7 @@ final class AsyncIO: @unchecked Sendable {
286
295
handle,
287
296
offsetAddress,
288
297
targetCount,
289
- nil ,
298
+ & bytesReadIfSynchronous ,
290
299
& overlapped
291
300
)
292
301
}
@@ -312,8 +321,18 @@ final class AsyncIO: @unchecked Sendable {
312
321
}
313
322
314
323
}
315
- // Now wait for read to finish
316
- let bytesRead = try await signalStream. next ( ) ?? 0
324
+
325
+ let bytesRead =
326
+ if bytesReadIfSynchronous == 0 {
327
+ try await signalStream. next ( ) ?? 0
328
+ } else {
329
+ bytesReadIfSynchronous
330
+ }
331
+
332
+ // This handle doesn't support overlapped (asynchronous) I/O, so we must have read it synchronously above
333
+ if bytesRead == UInt32 . max {
334
+ bytesRead = bytesReadIfSynchronous
335
+ }
317
336
318
337
if bytesRead == 0 {
319
338
// We reached EOF. Return whatever's left
0 commit comments