Skip to content

Make Observable.values terminal handling atomic - #2728

Merged
freak4pc merged 1 commit into
ReactiveX:mainfrom
victorpimentel:victorpimentel/observable-concurrency-atomic-latch
Sep 4, 2026
Merged

freak4pc merged 1 commit into
ReactiveX:mainfrom
victorpimentel:victorpimentel/observable-concurrency-atomic-latch

Conversation

@victorpimentel

Copy link
Copy Markdown
Contributor

While following up on #2727, I found the same terminal/disposal race in ObservableConvertibleType.values. onError and onCompleted write a captured isFinished flag while onDisposed reads it, and those callbacks can run on different threads. AsyncThrowingStream.finish is idempotent, so this does not cause the double-resume crash from #2727, but the flag is still a data race and cancellation can win over the source terminal event.

This replaces the flag with the existing AtomicInt latch. Each terminal callback claims the latch with fetchOr, and only the winner calls finish. The test exercises 10,000 completion/cancellation races and waits for every task to finish.

Tests: xcodebuild -workspace Rx.xcworkspace -scheme AllTests-macOS -destination platform=macOS -only-testing:AllTests-macOS/ObservableConcurrencyTests test (the scheme enables Thread Sanitizer and Undefined Behavior Sanitizer).

@victorpimentel

Copy link
Copy Markdown
Contributor Author

@freak4pc this is the fix related to the Observable.values property. The added tests do fail pretty much every time with the previous code, while the new code always passes the tests.

@victorpimentel

Copy link
Copy Markdown
Contributor Author

BTW I did audit other types and didn't find a similar issue so this should be all.

@freak4pc
freak4pc merged commit 3e33f90 into ReactiveX:main Sep 4, 2026
18 checks passed
freak4pc added a commit to victorpimentel/RxSwift that referenced this pull request Sep 4, 2026
* origin/main:
  Fix Observable values terminal race (ReactiveX#2728)
  Bump SPM watchOS deployment target to 5.0 (ReactiveX#2732)
  Bump faraday from 1.4.1 to 1.10.6 (ReactiveX#2720)
  Select Xcode 26.6 for CI on the macOS 26 runner image (ReactiveX#2726)
  Add regression test for the merge lock-inversion deadlock
  Run merge teardown outside forwardLock, keeping the disposed flag inside it
  Original fix of merge deadlock
  Add regression tests for the ReplaySubject, Connection and Zip lock-inversion deadlocks
  Apply review cleanups to Multicast and Zip
  Use performLocked and strip trailing whitespace
  Make ZipCollectionTypeSink not call out under lock
  Connect only outside of lock
  Make sure to never call dispose and subscribe underlock in Connection, ConnectionObservableAdapter and RefCountSink
  ReplayBufferBase propagate outiside locks
  Fix: missing CFBundleShortVersionString and update RX_VERSION *issue no ReactiveX#2687 (ReactiveX#2696)
  Fix concurrent share subscriptions (ReactiveX#2719)
  Bump addressable from 2.8.0 to 2.9.0 (ReactiveX#2702)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants