Step 0:
- Simple observable
let sharedSubscription = Observable.create { observer in
observer.on(.next(3))
return Disposables.create()
}
.share(replay: 1)
let sources = 0...50
let oneSource = Observable.zip(sources.map { _ in
self.getSomeInfoFromNetwork()
.flatMap { _ in
self.sharedSubscription
}
.take(1)
})
Step 1:
- TakeCount operator tries to emit completion
Step 2:
- Zip catches completion and starts disposing of the source sequence
Step 3:
- This disposal propagates up to
ShareReplay1WhileConnected
- And it tries to acquire a lock to unsubscribe
Step 4:
- Meanwhile, on a separate thread
ShareReplay1WhileConnected
is replaying an event, while being under its own lock
Step 5:
- It propagates down to
zip
operator, trying to acquire a lock
Step 6:
- It's a data race, so occasionally the processes can overlap, which can cause two threads to wait on each other.
- More concurrent operations = exponentially higher chances of a deadlock.
And that's how RxSwift deadlocks.