CouchDeveloper
CouchDeveloper

Reputation: 19124

Behavior for receive(on:) for DispatchQueue.main

Given the code below from a class:

cancellable = input
    .receive(on: scheduler)
    .map { ... }
    .sink(receiveValue: { value in
        self.state = value
    })

where input is a PassthroughSubject.

Now, when scheduler is the main queue or the RunLoop.main AND input will be called from the main thread, does receive(on: scheduler) programmatically optimise away an explicit dispatch to the main queue?

So, basically something like this:

 if Thread.isMainThread { 
    /* execute closure */
 } else { 
    /* dispatch closure async to main */
 }

The documentation for receive(on:) gives a vague hint, that it might perform some optimisations:

"Prefer receive(on:options:) over explicit use of dispatch queues"

pub.sink {
    DispatchQueue.main.async {
        // Do something.
    }
}

Upvotes: 6

Views: 1367

Answers (2)

rob mayoff
rob mayoff

Reputation: 385890

No, receive(on:) does not optimize away the dispatch. Doing so could lead to a deadlock. Example:

let l = Lock()

let cancellable = input
    .receive(on: scheduler)
    .map { ... }
    .sink(receiveValue: { value in
        l.lock()
        self.state = value
        l.unlock()
    })

l.lock()
input.send(1)
l.unlock()

If the dispatch were eliminated, this example would try to lock the already-locked lock l, and hang (or crash if it can detect the deadlock).

Upvotes: 3

Rengers
Rengers

Reputation: 15238

Looking at the sources for RunLoop and DispatchQueue, it doesn't look like there is such an optimisation in their conformance to the Scheduler protocol.

But to be fair, there might be lower level optimisations at play.

Upvotes: 2

Related Questions