ReactiveX / RxSwift

Reactive Programming in Swift
MIT License
24.4k stars 4.17k forks source link

Mark Operator Closures as @Sendable to Prevent Crashes in Swift 6 Isolated Contexts #2638

Open AndreiArdelean1 opened 1 month ago

AndreiArdelean1 commented 1 month ago

Short description of the issue:

In Swift 6, closures created in an isolated context automatically inherit the isolation of that context, unless they are marked as @Sendable. Because of this, creating an operation like map, flatMap, distinctUntilChanged, etc. from an isolated context, but calling it from a different thread, causes the call to crash.

Expected outcome:

No crash should happen even without manually marking the closures as @Sendable. All closures passed to operators that could be executed on another thread, should be marked as @Sendable inside the RxSwift framework.

What actually happens:

Unless manually specifying that the closure is Sendable, the code crashes

@preconcurrency import RxSwift

@MainActor
func test() -> any Disposable {
    return Observable<Int>.just(1)
        // any non main thread scheduler
        .observe(on: RxSwift.SerialDispatchQueueScheduler(qos: .background))
        .map({ $0 + 1 })
        .subscribe()
}

Workaround:

@preconcurrency import RxSwift

@MainActor
func test() -> any Disposable {
    return Observable<Int>.just(1)
        // any non main thread scheduler
        .observe(on: RxSwift.SerialDispatchQueueScheduler(qos: .background))
        .map({ @Sendable in $0 + 1 })
        .subscribe()
}

Example fix (Map.swift):

    public func map<Result>(_ transform: @escaping (Element) throws -> Result)
        -> Observable<Result> {

should be:

    public func map<Result>(_ transform: @escaping @Sendable (Element) throws -> Result)
        -> Observable<Result> {

Proposed fix:

RxSwift/RxCocoa/RxBlocking/RxTest version/commit

6.8.0

Platform/Environment

How easy is to reproduce? (chances of successful reproduce after running the self contained code)

Xcode version:

16.0

:warning: Fields below are optional for general issues or in case those questions aren't related to your issue, but filling them out will increase the chances of getting your issue resolved. :warning:

Installation method:

I have multiple versions of Xcode installed: (so we can know if this is a potential cause of your issue)

Level of RxSwift knowledge: (this is so we can understand your level of knowledge and formulate the response in an appropriate manner)

danielt1263 commented 1 month ago

@AndreiArdelean1 Can you submit a PR to that effect?

AndreiArdelean1 commented 1 month ago

@danielt1263 I've created pull request #2639 with the changes