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.
Musubi/Packages/MusubiKit/Sources/MusubiCore/ConnectionWatchdog.swift

52 lines
1.8 KiB

/// Decides when a receiver counts as stalled and when to try reconnecting.
///
/// Frames and a confirmed connection both count as signs of life: some senders (e.g. NDI Test Patterns)
/// send a still image only once, which is not an outage.
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 lastAlive: 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
connectionConfirmed(at: now)
}
/// The sender still holds a connection to us, even if no new frames arrive.
public mutating func connectionConfirmed(at now: Double) {
lastAlive = now
lastRetry = nil
}
public func state(at now: Double) -> State {
let reference = lastAlive ?? 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
}
}

Powered by TurnKey Linux.