Skip to content

Commit 1d2f824

Browse files
committed
Apply fix to AsyncIO+Linux for regular file descriptors that do not need epoll
1 parent 7789953 commit 1d2f824

File tree

2 files changed

+21
-2
lines changed

2 files changed

+21
-2
lines changed

Sources/Subprocess/IO/AsyncIO+Linux.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,16 @@ final class AsyncIO: Sendable {
287287
&event
288288
)
289289
if rc != 0 {
290+
if errno == EPERM {
291+
// Special Case:
292+
//
293+
// * EPERM can happen when this is a regular file (not pipe, socket, etc.) which is available right away for read/write,
294+
// so we just go ahead and yield for I/O on the file descriptor. There's no need to wait.
295+
//
296+
continuation.yield(true)
297+
return
298+
}
299+
290300
_registration.withLock { storage in
291301
_ = storage.removeValue(forKey: fileDescriptor.rawValue)
292302
}
@@ -318,7 +328,15 @@ final class AsyncIO: Sendable {
318328
fileDescriptor.rawValue,
319329
nil
320330
)
321-
guard rc == 0 else {
331+
332+
// Special Cases:
333+
//
334+
// * EPERM is set if the file descriptor is a regular file (not pipe, socket, etc.) and so it was never
335+
// registered with epoll.
336+
// * ENOENT is set if the file descriptor is unknown to epoll, so we an just continue and remove it
337+
// from registration.
338+
//
339+
if rc != 0 && errno != EPERM && errno != ENOENT {
322340
throw SubprocessError(
323341
code: .init(
324342
.asyncIOFailed(
@@ -327,6 +345,7 @@ final class AsyncIO: Sendable {
327345
underlyingError: .init(rawValue: errno)
328346
)
329347
}
348+
330349
_registration.withLock { store in
331350
_ = store.removeValue(forKey: fileDescriptor.rawValue)
332351
}

Tests/SubprocessTests/PipeConfigurationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ struct PipeConfigurationTests {
572572
}
573573

574574
// FIXME - These tests are hanging on Linux
575-
#if os(macOS)
575+
#if !os(Windows)
576576
@Test func testSwiftFunctionWithFileDescriptorInput() async throws {
577577
// Create a temporary file with JSON content
578578
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("json_test_\(UUID().uuidString).json")

0 commit comments

Comments
 (0)