refactor(apple): share AsyncTimeout helper

This commit is contained in:
Peter Steinberger
2026-01-10 16:22:58 +00:00
parent a1533a17f7
commit 8bc9209094
4 changed files with 25 additions and 42 deletions

View File

@@ -161,18 +161,10 @@ actor BridgeClient {
purpose: String, purpose: String,
_ op: @escaping @Sendable () async throws -> T) async throws -> T _ op: @escaping @Sendable () async throws -> T) async throws -> T
{ {
try await withThrowingTaskGroup(of: T.self) { group in try await AsyncTimeout.withTimeout(
group.addTask { seconds: Double(seconds),
try await op() onTimeout: { TimeoutError(purpose: purpose, seconds: seconds) },
} operation: op)
group.addTask {
try await Task.sleep(nanoseconds: UInt64(seconds) * 1_000_000_000)
throw TimeoutError(purpose: purpose, seconds: seconds)
}
let result = try await group.next()!
group.cancelAll()
return result
}
} }
private func startAndWaitForReady(_ connection: NWConnection, queue: DispatchQueue) async throws { private func startAndWaitForReady(_ connection: NWConnection, queue: DispatchQueue) async throws {

View File

@@ -321,20 +321,10 @@ actor BridgeSession {
seconds: Double, seconds: Double,
operation: @escaping @Sendable () async throws -> T) async throws -> T operation: @escaping @Sendable () async throws -> T) async throws -> T
{ {
try await withThrowingTaskGroup(of: T.self) { group in try await AsyncTimeout.withTimeout(
group.addTask { try await operation() } seconds: seconds,
group.addTask { onTimeout: { TimeoutError(message: "UNAVAILABLE: connection timeout") },
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000)) operation: operation)
throw TimeoutError(message: "UNAVAILABLE: connection timeout")
}
guard let first = try await group.next() else {
throw TimeoutError(message: "UNAVAILABLE: connection timeout")
}
group.cancelAll()
return first
}
} }
private static func makeStateStream(for connection: NWConnection) -> AsyncStream<NWConnection.State> { private static func makeStateStream(for connection: NWConnection) -> AsyncStream<NWConnection.State> {

View File

@@ -90,20 +90,7 @@ final class LocationService: NSObject, CLLocationManagerDelegate {
timeoutMs: Int, timeoutMs: Int,
operation: @escaping @Sendable () async throws -> T) async throws -> T operation: @escaping @Sendable () async throws -> T) async throws -> T
{ {
if timeoutMs == 0 { try await AsyncTimeout.withTimeoutMs(timeoutMs: timeoutMs, onTimeout: { Error.timeout }, operation: operation)
return try await operation()
}
return try await withThrowingTaskGroup(of: T.self) { group in
group.addTask { try await operation() }
group.addTask {
try await Task.sleep(nanoseconds: UInt64(timeoutMs) * 1_000_000)
throw Error.timeout
}
let result = try await group.next()!
group.cancelAll()
return result
}
} }
private static func accuracyValue(_ accuracy: ClawdbotLocationAccuracy) -> CLLocationAccuracy { private static func accuracyValue(_ accuracy: ClawdbotLocationAccuracy) -> CLLocationAccuracy {

View File

@@ -1,12 +1,16 @@
import Foundation import Foundation
enum AsyncTimeout { public enum AsyncTimeout {
static func withTimeout<T: Sendable>( public static func withTimeout<T: Sendable>(
seconds: Double, seconds: Double,
onTimeout: @escaping @Sendable () -> Error, onTimeout: @escaping @Sendable () -> Error,
operation: @escaping @Sendable () async throws -> T) async throws -> T operation: @escaping @Sendable () async throws -> T) async throws -> T
{ {
let clamped = max(0, seconds) let clamped = max(0, seconds)
if clamped == 0 {
return try await operation()
}
return try await withThrowingTaskGroup(of: T.self) { group in return try await withThrowingTaskGroup(of: T.self) { group in
group.addTask { try await operation() } group.addTask { try await operation() }
group.addTask { group.addTask {
@@ -19,4 +23,14 @@ enum AsyncTimeout {
throw onTimeout() throw onTimeout()
} }
} }
public static func withTimeoutMs<T: Sendable>(
timeoutMs: Int,
onTimeout: @escaping @Sendable () -> Error,
operation: @escaping @Sendable () async throws -> T) async throws -> T
{
let clamped = max(0, timeoutMs)
let seconds = Double(clamped) / 1000.0
return try await self.withTimeout(seconds: seconds, onTimeout: onTimeout, operation: operation)
}
} }