Skip to content

Fix concurrent subscriptions to share without replay#2719

Open
thliu21 wants to merge 1 commit into
ReactiveX:mainfrom
thliu21:agent/rxswift-2718
Open

Fix concurrent subscriptions to share without replay#2719
thliu21 wants to merge 1 commit into
ReactiveX:mainfrom
thliu21:agent/rxswift-2718

Conversation

@thliu21

@thliu21 thliu21 commented Jul 22, 2026

Copy link
Copy Markdown

Summary

  • make first-subscriber detection and observer insertion atomic for share(replay: 0, scope: .whileConnected)
  • add a gated concurrent regression test that verifies a cold source is connected exactly once
  • register the regression test in the generated SwiftPM test entry point

Root cause and impact

The optimized no-replay sharing path released its recursive lock before inserting the observer. Concurrent first subscribers could therefore both observe an empty observer bag and both call connect() on the same connection. The second call assigned the connection's SingleAssignmentDisposable again and crashed with Fatal error: oldState.disposable != nil.

This change inserts the observer before releasing the lock, keeping the observer count and insertion in one critical section. Connecting to the source still happens after unlocking, so source subscription and user code remain outside the lock and the previous replay deadlock is not reintroduced.

Validation

  • focused test2718ShareWithoutReplayConnectsOnceForConcurrentFirstSubscriptions macOS test
  • complete AnomaliesTest and ObservableShareReplayScopeTests macOS suites (18 tests), including the four existing take(1) + share(replay: 1) + zip = deadlock #2653 deadlock regressions
  • swift build --product RxSwift
  • ./scripts/package-spm.swift
  • git diff --check

Fixes #2718

@thliu21
thliu21 marked this pull request as ready for review July 22, 2026 17:48
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.

share(replay: 0) crashes with "Fatal error: oldState.disposable != nil" on concurrent first subscriptions (regression in 6.10.0)

1 participant