You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
42 lines
1.4 KiB
42 lines
1.4 KiB
/// Decides when a receiver counts as stalled and when to try reconnecting.
|
|
public struct ConnectionWatchdog: Sendable, Equatable {
|
|
public enum State: Equatable, Sendable {
|
|
case connecting
|
|
case live
|
|
/// No frames for a while. `retryIn` is the time until the next reconnect attempt.
|
|
case stalled(retryIn: Double)
|
|
}
|
|
|
|
public var stallAfter: Double
|
|
public var retryInterval: Double
|
|
|
|
private var lastFrame: Double?
|
|
private var lastRetry: Double?
|
|
private let startedAt: Double
|
|
|
|
public init(now: Double, stallAfter: Double = 2, retryInterval: Double = 3) {
|
|
self.startedAt = now
|
|
self.stallAfter = stallAfter
|
|
self.retryInterval = retryInterval
|
|
}
|
|
|
|
public mutating func frameArrived(at now: Double) {
|
|
lastFrame = now
|
|
lastRetry = nil
|
|
}
|
|
|
|
public func state(at now: Double) -> State {
|
|
let reference = lastFrame ?? startedAt
|
|
guard now - reference >= stallAfter else { return lastFrame == nil ? .connecting : .live }
|
|
let since = lastRetry ?? reference + stallAfter
|
|
return .stalled(retryIn: max(0, retryInterval - (now - since)))
|
|
}
|
|
|
|
/// Returns true when a reconnect should be attempted now, and records the attempt.
|
|
public mutating func shouldRetry(at now: Double) -> Bool {
|
|
guard case .stalled(let retryIn) = state(at: now), retryIn == 0 else { return false }
|
|
lastRetry = now
|
|
return true
|
|
}
|
|
}
|