refactor on timer and polling threading

This commit is contained in:
ryanzhao 2022-02-22 14:01:48 +11:00
parent eec3d31109
commit b2ab984586
4 changed files with 44 additions and 53 deletions

View File

@ -61,13 +61,11 @@ public final class ClosedGroupPoller : NSObject {
// MARK: Private API // MARK: Private API
private func setUpPolling(for groupPublicKey: String) { private func setUpPolling(for groupPublicKey: String) {
poll(groupPublicKey).done2 { [weak self] _ in Threading.closedGroupPollerQueue.async {
DispatchQueue.main.async { // Timers don't do well on background queues self.poll(groupPublicKey).done(on: Threading.closedGroupPollerQueue) { [weak self] _ in
self?.pollRecursively(groupPublicKey) self?.pollRecursively(groupPublicKey)
} }.catch(on: Threading.closedGroupPollerQueue) { [weak self] error in
}.catch2 { [weak self] error in // The error is logged in poll(_:)
// The error is logged in poll(_:)
DispatchQueue.main.async { // Timers don't do well on background queues
self?.pollRecursively(groupPublicKey) self?.pollRecursively(groupPublicKey)
} }
} }
@ -87,18 +85,14 @@ public final class ClosedGroupPoller : NSObject {
let a = (ClosedGroupPoller.maxPollInterval - minPollInterval) / limit let a = (ClosedGroupPoller.maxPollInterval - minPollInterval) / limit
let nextPollInterval = a * min(timeSinceLastMessage, limit) + minPollInterval let nextPollInterval = a * min(timeSinceLastMessage, limit) + minPollInterval
SNLog("Next poll interval for closed group with public key: \(groupPublicKey) is \(nextPollInterval) s.") SNLog("Next poll interval for closed group with public key: \(groupPublicKey) is \(nextPollInterval) s.")
timers[groupPublicKey] = Timer.scheduledTimer(withTimeInterval: nextPollInterval, repeats: false) { [weak self] timer in timers[groupPublicKey] = Timer.scheduledTimerOnMainThread(withTimeInterval: nextPollInterval, repeats: false) { [weak self] timer in
timer.invalidate() timer.invalidate()
Threading.closedGroupPollerQueue.async { Threading.closedGroupPollerQueue.async {
self?.poll(groupPublicKey).done2 { _ in self?.poll(groupPublicKey).done(on: Threading.closedGroupPollerQueue) { _ in
DispatchQueue.main.async { // Timers don't do well on background queues self?.pollRecursively(groupPublicKey)
self?.pollRecursively(groupPublicKey) }.catch(on: Threading.closedGroupPollerQueue) { error in
}
}.catch2 { error in
// The error is logged in poll(_:) // The error is logged in poll(_:)
DispatchQueue.main.async { // Timers don't do well on background queues self?.pollRecursively(groupPublicKey)
self?.pollRecursively(groupPublicKey)
}
} }
} }
} }

View File

@ -19,18 +19,11 @@ public final class OpenGroupPollerV2 : NSObject {
@objc public func startIfNeeded() { @objc public func startIfNeeded() {
guard !hasStarted else { return } guard !hasStarted else { return }
DispatchQueue.main.async { [weak self] in // Timers don't do well on background queues hasStarted = true
guard let strongSelf = self else { return } timer = Timer.scheduledTimerOnMainThread(withTimeInterval: pollInterval, repeats: true) { _ in
strongSelf.hasStarted = true self.poll().retainUntilComplete()
strongSelf.timer = Timer.scheduledTimer(withTimeInterval: strongSelf.pollInterval, repeats: true) { _ in
Threading.openGroupPollerQueue.async {
self?.poll().retainUntilComplete()
}
}
Threading.openGroupPollerQueue.async {
strongSelf.poll().retainUntilComplete()
}
} }
poll().retainUntilComplete()
} }
@objc public func stop() { @objc public func stop() {
@ -50,15 +43,17 @@ public final class OpenGroupPollerV2 : NSObject {
self.isPolling = true self.isPolling = true
let (promise, seal) = Promise<Void>.pending() let (promise, seal) = Promise<Void>.pending()
promise.retainUntilComplete() promise.retainUntilComplete()
OpenGroupAPIV2.compactPoll(server).done(on: OpenGroupAPIV2.workQueue) { [weak self] bodies in Threading.openGroupPollerQueue.async {
guard let self = self else { return } OpenGroupAPIV2.compactPoll(self.server).done(on: OpenGroupAPIV2.workQueue) { [weak self] bodies in
self.isPolling = false guard let self = self else { return }
bodies.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) } self.isPolling = false
seal.fulfill(()) bodies.forEach { self.handleCompactPollBody($0, isBackgroundPoll: isBackgroundPoll) }
}.catch(on: OpenGroupAPIV2.workQueue) { error in seal.fulfill(())
SNLog("Open group polling failed due to error: \(error).") }.catch(on: OpenGroupAPIV2.workQueue) { error in
self.isPolling = false SNLog("Open group polling failed due to error: \(error).")
seal.fulfill(()) // The promise is just used to keep track of when we're done self.isPolling = false
seal.fulfill(()) // The promise is just used to keep track of when we're done
}
} }
return promise return promise
} }

View File

@ -45,21 +45,22 @@ public final class Poller : NSObject {
// MARK: Private API // MARK: Private API
private func setUpPolling() { private func setUpPolling() {
guard isPolling else { return } guard isPolling else { return }
let _ = SnodeAPI.getSwarm(for: getUserHexEncodedPublicKey()).then2 { [weak self] _ -> Promise<Void> in Threading.pollerQueue.async {
guard let strongSelf = self else { return Promise { $0.fulfill(()) } } let _ = SnodeAPI.getSwarm(for: getUserHexEncodedPublicKey()).then(on: Threading.pollerQueue) { [weak self] _ -> Promise<Void> in
strongSelf.usedSnodes.removeAll() guard let strongSelf = self else { return Promise { $0.fulfill(()) } }
let (promise, seal) = Promise<Void>.pending() strongSelf.usedSnodes.removeAll()
strongSelf.pollNextSnode(seal: seal) let (promise, seal) = Promise<Void>.pending()
return promise strongSelf.pollNextSnode(seal: seal)
}.ensure(on: DispatchQueue.main) { [weak self] in // Timers don't do well on background queues return promise
guard let strongSelf = self, strongSelf.isPolling else { return } }.ensure(on: Threading.pollerQueue) { [weak self] in // Timers don't do well on background queues
Timer.scheduledTimer(withTimeInterval: Poller.retryInterval, repeats: false) { _ in guard let strongSelf = self, strongSelf.isPolling else { return }
guard let strongSelf = self else { return } Timer.scheduledTimerOnMainThread(withTimeInterval: Poller.retryInterval, repeats: false) { _ in
Threading.pollerQueue.async { guard let strongSelf = self else { return }
strongSelf.setUpPolling() strongSelf.setUpPolling()
} }
} }
} }
} }
private func pollNextSnode(seal: Resolver<Void>) { private func pollNextSnode(seal: Resolver<Void>) {
@ -70,9 +71,9 @@ public final class Poller : NSObject {
// randomElement() uses the system's default random generator, which is cryptographically secure // randomElement() uses the system's default random generator, which is cryptographically secure
let nextSnode = unusedSnodes.randomElement()! let nextSnode = unusedSnodes.randomElement()!
usedSnodes.insert(nextSnode) usedSnodes.insert(nextSnode)
poll(nextSnode, seal: seal).done2 { poll(nextSnode, seal: seal).done(on: Threading.pollerQueue) {
seal.fulfill(()) seal.fulfill(())
}.catch2 { [weak self] error in }.catch(on: Threading.pollerQueue) { [weak self] error in
if let error = error as? Error, error == .pollLimitReached { if let error = error as? Error, error == .pollLimitReached {
self?.pollCount = 0 self?.pollCount = 0
} else { } else {

View File

@ -2,12 +2,13 @@ import PromiseKit
/// Delay the execution of the promise constructed in `body` by `delay` seconds. /// Delay the execution of the promise constructed in `body` by `delay` seconds.
public func withDelay<T>(_ delay: TimeInterval, completionQueue: DispatchQueue, body: @escaping () -> Promise<T>) -> Promise<T> { public func withDelay<T>(_ delay: TimeInterval, completionQueue: DispatchQueue, body: @escaping () -> Promise<T>) -> Promise<T> {
#if DEBUG
assert(Thread.current.isMainThread) // Timers don't do well on background queues
#endif
let (promise, seal) = Promise<T>.pending() let (promise, seal) = Promise<T>.pending()
Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { _ in Timer.scheduledTimerOnMainThread(withTimeInterval: delay, repeats: false) { _ in
body().done(on: completionQueue) { seal.fulfill($0) }.catch(on: completionQueue) { seal.reject($0) } body().done(on: completionQueue) {
seal.fulfill($0)
}.catch(on: completionQueue) {
seal.reject($0)
}
} }
return promise return promise
} }