diff --git a/src/logic/BackgroundWork.swift b/src/logic/BackgroundWork.swift index dd2e9e5f..e736f5db 100644 --- a/src/logic/BackgroundWork.swift +++ b/src/logic/BackgroundWork.swift @@ -10,8 +10,6 @@ class BackgroundWork { // we cap concurrent tasks to .processorCount to avoid thread explosion on the .global queue static let globalSemaphore = DispatchSemaphore(value: ProcessInfo.processInfo.processorCount) - // AX calls may block for a long time. We use a big semaphore to avoid blocking the main thread in case the window server is busy - static let axCallsGlobalSemaphore = DispatchSemaphore(value: 100) // swift static variables are lazy; we artificially force the threads to init static func start() { @@ -25,12 +23,12 @@ extension DispatchQueue { return DispatchQueue(label: label, target: .global(qos: qos.qosClass)) } - func asyncWithCap(_ deadline: DispatchTime? = nil, semaphore: DispatchSemaphore = BackgroundWork.globalSemaphore, _ fn: @escaping () -> Void) { + func asyncWithCap(_ deadline: DispatchTime? = nil, _ fn: @escaping () -> Void) { let block = { fn() - semaphore.signal() + BackgroundWork.globalSemaphore.signal() } - semaphore.wait() + BackgroundWork.globalSemaphore.wait() if let deadline = deadline { asyncAfter(deadline: deadline, execute: block) } else { diff --git a/src/logic/events/AccessibilityEvents.swift b/src/logic/events/AccessibilityEvents.swift index ed225102..914ee448 100644 --- a/src/logic/events/AccessibilityEvents.swift +++ b/src/logic/events/AccessibilityEvents.swift @@ -7,15 +7,19 @@ func axObserverCallback(observer: AXObserver, element: AXUIElement, notification // if the window server is busy, it may not reply to AX calls. We retry right before the call times-out and returns a bogus value func retryAxCallUntilTimeout(_ fn: @escaping () throws -> Void, _ startTime: DispatchTime = DispatchTime.now()) { - BackgroundWork.axCallsQueue.asyncWithCap(semaphore: BackgroundWork.axCallsGlobalSemaphore) { + BackgroundWork.axCallsQueue.async { + retryAxCallUntilTimeout_(fn, startTime) + } +} + +func retryAxCallUntilTimeout_(_ fn: @escaping () throws -> Void, _ startTime: DispatchTime = DispatchTime.now()) { do { try fn() } catch { let timePassedInSeconds = Double(DispatchTime.now().uptimeNanoseconds - startTime.uptimeNanoseconds) / 1_000_000_000 if timePassedInSeconds < Double(AXUIElement.globalTimeoutInSeconds) { - BackgroundWork.axCallsQueue.asyncWithCap(.now() + .milliseconds(10), semaphore: BackgroundWork.axCallsGlobalSemaphore) { - retryAxCallUntilTimeout(fn, startTime) - } + BackgroundWork.axCallsQueue.asyncAfter(deadline: .now() + .milliseconds(10)) { + retryAxCallUntilTimeout_(fn, startTime) } } }