in Sources/OSS/Internal/ExpiringValue.swift [49:112]
func getValue(getExpiringValue: @escaping @Sendable () async throws -> (T, Date)) async throws -> T {
let task: Task<T, Error>
switch state {
case .noValue:
task = try getValueTask(getExpiringValue)
state = .waitingOnValue(task)
case let .initialWaitingOnValue(task):
return try await withTaskCancellationHandler {
switch await task.result {
case let .success(result):
self.state = .withValue(result.0, result.1)
return result.0
case let .failure(error):
self.state = .error(error)
throw error
}
} onCancel: {
task.cancel()
}
case let .waitingOnValue(waitingOnTask):
task = waitingOnTask
case let .withValue(value, expires):
if expires.timeIntervalSinceNow < 0 {
// value has expired, create new task to update value and
// return the result of that task
task = try getValueTask(getExpiringValue)
state = .waitingOnValue(task)
} else if expires.timeIntervalSinceNow < threshold {
// value is about to expire, create new task to update value and
// return current value
let task = try getValueTask(getExpiringValue)
state = .withValueAndWaiting(value, expires, task)
return value
} else {
return value
}
case let .withValueAndWaiting(value, expires, waitingOnTask):
if expires.timeIntervalSinceNow < 0 {
// as value has expired wait for task to finish and return result
task = waitingOnTask
} else {
// value hasn't expired so return current value
return value
}
case let .error(error):
throw error
}
return try await withTaskCancellationHandler {
switch await task.result {
case let .success(value):
return value
case let .failure(error):
self.state = .error(error)
throw error
}
} onCancel: {
task.cancel()
}
}