Skip to content
Merged
Show file tree
Hide file tree
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
8 changes: 4 additions & 4 deletions RxSwift/Observable+Concurrency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ public extension ObservableConvertibleType {
/// ```
var values: AsyncThrowingStream<Element, Error> {
AsyncThrowingStream<Element, Error> { continuation in
var isFinished = false
let didFinish = AtomicInt(0)
let disposable = asObservable().subscribe(
onNext: { value in continuation.yield(value) },
onError: { error in
isFinished = true
guard fetchOr(didFinish, 1) == 0 else { return }
continuation.finish(throwing: error)
},
onCompleted: {
isFinished = true
guard fetchOr(didFinish, 1) == 0 else { return }
continuation.finish()
},
onDisposed: {
guard !isFinished else { return }
guard fetchOr(didFinish, 1) == 0 else { return }
continuation.finish(throwing: CancellationError())
}
)
Expand Down
30 changes: 30 additions & 0 deletions Tests/RxSwiftTests/Observable+ConcurrencyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,36 @@ extension ObservableConcurrencyTests {
task.cancel()
}

func testValuesCompleteWhenCompletionRacesCancellation() {
let expectation = XCTestExpectation(description: "Observable values tasks finish")
let iterations = 10000
expectation.expectedFulfillmentCount = iterations

for _ in 0 ..< iterations {
let subject = PublishSubject<Void>()
let stream = subject.values
let task = Task {
defer { expectation.fulfill() }

do {
for try await _ in stream {
}
} catch {
// Cancellation is an expected outcome of the race.
}
}

DispatchQueue.global(qos: .userInitiated).async {
subject.onCompleted()
}
DispatchQueue.global(qos: .userInitiated).async {
task.cancel()
}
}

wait(for: [expectation], timeout: 10)
}

// MARK: - AsyncSequence.asObservable() Tests

func testAsyncSequenceToObservable() async {
Expand Down
Loading