TM-SGNL-iOS/SignalServiceKit/Concurrency/Retry.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

41 lines
1.6 KiB
Swift

//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
enum Retry {
/// Performs `block` repeatedly until `onError` throws an error (or until cancellation).
static func performRepeatedly<T>(block: () async throws -> T, onError: (any Error, _ attemptCount: Int) async throws -> Void) async throws -> T {
var attemptCount = 0
while true {
try Task.checkCancellation()
do {
attemptCount += 1
return try await block()
} catch {
try await onError(error, attemptCount)
}
}
}
/// Performs `block` repeatedly with exponential backoff.
///
/// This method will invoke `block` at most `maxAttempts` times, propagating
/// the error from the final attempt. If `block` throws a fatal error or
/// non-retryable error on an earlier attempt, that error will be propagated
/// immediately. This method supports cancellation.
static func performWithBackoff<T>(maxAttempts: Int, block: () async throws -> T) async throws -> T {
return try await performRepeatedly(
block: block,
onError: { error, attemptCount in
if error.isFatalError || !error.isRetryable || attemptCount >= maxAttempts {
throw error
}
let retryDelayNs = OWSOperation.retryIntervalForExponentialBackoffNs(failureCount: attemptCount)
try await Task.sleep(nanoseconds: retryDelayNs)
}
)
}
}