Skip to content

Commit

Permalink
Perform listener invocations synchronously if we are on the target q
Browse files Browse the repository at this point in the history
  • Loading branch information
EricRabil committed Jul 25, 2022
1 parent 59c3d20 commit a7f1282
Showing 1 changed file with 38 additions and 7 deletions.
45 changes: 38 additions & 7 deletions Sources/Pwomise/Pwomise.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public protocol PromiseAbuseDelegate {

public var SharedPromiseAbuseDelegate: PromiseAbuseDelegate?

typealias dispatch_get_current_queue_t = @convention(c) () -> Unmanaged<DispatchQueue>
private let dispatch_get_current_queue = dlsym(dlopen(nil, RTLD_GLOBAL), "dispatch_get_current_queue").map {
unsafeBitCast($0, to: dispatch_get_current_queue_t.self)
}

public class Promise<Output>: CustomDebugStringConvertible {
internal typealias Pending = PendingPromise<Output, Error>
public typealias Completion = Result<Output, Error>
Expand Down Expand Up @@ -109,7 +114,9 @@ public class Promise<Output>: CustomDebugStringConvertible {
}
}

internal var listeners: [(Completion) -> ()] = [] {
typealias Listener = (Completion) -> ()

internal var listeners: [Listener] = [] {
didSet {
guard listeners.count > 0 else {
return
Expand Down Expand Up @@ -156,6 +163,31 @@ public class Promise<Output>: CustomDebugStringConvertible {
result != .pending
}

private func createExecutorFunction(_ result: Completion) -> ((@escaping Listener) -> ())? {
switch resolveQueue {
case is OS_dispatch_queue_concurrent:
// this should be scheduled concurrently, it must have an executor
break
case let queue as DispatchQueue:
if dispatch_get_current_queue?().takeUnretainedValue() == queue {
// same queue, passthrough
return nil
}
case let runLoop as RunLoop:
if RunLoop.current == runLoop {
// same runloop, passthrough
return nil
}
default:
break
}
return { listener in
self.resolveQueue.schedule {
listener(result)
}
}
}

private func emit() {
guard case .resolved(let result) = result, listeners.count > 0 else {
// Not resolved or no listeners
Expand All @@ -166,13 +198,12 @@ public class Promise<Output>: CustomDebugStringConvertible {
let listeners = listeners
self.listeners = []

for listener in listeners {
resolveQueue.schedule {
listener(result)
}
if let executor = createExecutorFunction(result) {
listeners.forEach(executor)
resolveQueue.wake()
} else {
listeners.forEach { $0(result) }
}

resolveQueue.wake()
}

// Changes the RunLoop downstream listeners are invoked on
Expand Down

0 comments on commit a7f1282

Please sign in to comment.