Skip to content

Sr 1872 using runloopsource in wait for expectation #314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 7 additions & 23 deletions Sources/XCTest/Public/Asynchronous/XCTWaiter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ open class XCTWaiter {
internal var waitSourceLocation: SourceLocation?
private weak var manager: WaiterManager<XCTWaiter>?
private var runLoop: RunLoop?

private var runLoopSource: RunLoop._Source?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice the RunLoop._Source type and several of its related RunLoop methods are marked as deprecated in Foundation… does that mean this PR will introduce new build warnings to the XCTest codebase? I wonder what we can do about that, since Swift has no way to "silence" those warnings.

private weak var _delegate: XCTWaiterDelegate?
private let delegateQueue = DispatchQueue(label: "org.swift.XCTest.XCTWaiter.delegate")

Expand Down Expand Up @@ -210,7 +210,8 @@ open class XCTWaiter {
queue_configureExpectations(expectations)
state = .waiting(state: waitingState)
self.runLoop = runLoop

self.runLoopSource = RunLoop._Source()
self.runLoop?._add(self.runLoopSource!, forMode: .default)
queue_validateExpectationFulfillment(dueToTimeout: false)
}

Expand All @@ -219,14 +220,7 @@ open class XCTWaiter {
self.manager = manager

// Begin the core wait loop.
let timeoutTimestamp = Date.timeIntervalSinceReferenceDate + timeout
while !isFinished {
let remaining = timeoutTimestamp - Date.timeIntervalSinceReferenceDate
if remaining <= 0 {
break
}
primitiveWait(using: runLoop, duration: remaining)
}
primitiveWait(using: runLoop, duration: timeout)

manager.stopManaging(self)
self.manager = nil
Expand Down Expand Up @@ -358,22 +352,12 @@ open class XCTWaiter {

private extension XCTWaiter {
func primitiveWait(using runLoop: RunLoop, duration timeout: TimeInterval) {
// The contract for `primitiveWait(for:)` explicitly allows waiting for a shorter period than requested
// by the `timeout` argument. Only run for a short time in case `cancelPrimitiveWait()` was called and
// issued `CFRunLoopStop` just before we reach this point.
let timeIntervalToRun = min(0.1, timeout)

// RunLoop.run(mode:before:) should have @discardableResult <rdar://problem/45371901>
_ = runLoop.run(mode: .default, before: Date(timeIntervalSinceNow: timeIntervalToRun))
runLoop.run(until: .init(timeIntervalSinceNow: timeout))
}

func cancelPrimitiveWait() {
guard let runLoop = runLoop else { return }
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
CFRunLoopStop(runLoop.getCFRunLoop())
#else
runLoop._stop()
#endif
dispatchPrecondition(condition: .onQueue(XCTWaiter.subsystemQueue))
runLoopSource?.invalidate()
}
}

Expand Down