Skip to content

Commit d4373e3

Browse files
committed
Clarify .pollNval and add test reading from an invalid descriptor
1 parent a6d4c15 commit d4373e3

3 files changed

Lines changed: 47 additions & 2 deletions

File tree

Sources/System/IORing/IOCompletion.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ public extension IORing.Completion {
5454
}
5555
}
5656

57+
/// The result of the completed operation.
58+
///
59+
/// A non-negative value is the operation's success result: a byte count
60+
/// for a read or a write, an event mask for a poll, and so on.
61+
///
62+
/// A negative value is an `errno` code multiplied by -1. Recover the error
63+
/// by negating it again: `Errno(rawValue: -completion.result)`.
5764
@inlinable var result: Int32 {
5865
get {
5966
rawValue.res

Sources/System/IORing/PollEvents.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ extension IORing.Request {
9797
@_alwaysEmitIntoClient
9898
public static var pollHup: PollEvents { PollEvents(.pollHup) }
9999

100-
/// An event indicating the file descriptor is not open.
100+
/// An event indicating that the object a descriptor refers to is no
101+
/// longer valid.
101102
///
102-
/// This usually means the descriptor was closed, or was never valid.
103+
/// This arises when the descriptor itself resolves, but the thing it
104+
/// refers to has since become invalid. For example, the disconnection
105+
/// of a sound device could cause this event.
106+
///
107+
/// Note that a descriptor which simply does not resolve would
108+
/// return the EBADF error code (Errno.badFileDescriptor).
103109
///
104110
/// The kernel reports this event whether or not it was requested, and
105111
/// requesting it explicitly has no effect. Corresponds to the POSIX

Tests/SystemTests/IORingTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,38 @@ final class IORingTests: XCTestCase {
577577
XCTFail("expected POLLERR, got 0x\(String(result, radix: 16))")
578578
}
579579
}
580+
581+
// A completion's `result` is two things in one field: a non-negative
582+
// value is an event mask, and a negative value is a negated errno. The
583+
// sign is the only thing distinguishing them, so it has to be checked
584+
// before the value is treated as anything else.
585+
func testPollAddOnInvalidDescriptor() throws {
586+
try XCTSkipIf(!uringEnabled, failureMessage)
587+
var ring = try IORing(queueDepth: 8)
588+
589+
let request = IORing.Request.pollAdd(
590+
FileDescriptor(rawValue: -1), pollEvents: .pollIn,
591+
isMultiShot: false, context: 99
592+
)
593+
let success = try ring.submit(linkedRequests: request)
594+
XCTAssertEqual(success, true)
595+
596+
guard let completion = ring.tryConsumeCompletion() else {
597+
XCTFail("expected a completion for the failed poll")
598+
return
599+
}
600+
XCTAssertEqual(completion.context, 99)
601+
602+
// A negative value for `result` marks the completion a a failure.
603+
XCTAssertLessThan(completion.result, 0, "expected a failure")
604+
// The negative value is the error code multiplied by -1.
605+
XCTAssertEqual(Errno(rawValue: -completion.result), .badFileDescriptor)
606+
607+
// A negative result may look like another result code.
608+
// Checking for the error must happen first.
609+
let pollNval = Int32(IORing.Request.PollEvents.pollNval.rawValue)
610+
XCTAssertNotEqual(completion.result & pollNval, 0)
611+
}
580612
}
581613
#endif // os(Linux)
582614
#endif // compiler(>=6.2) && $Lifetimes

0 commit comments

Comments
 (0)