-
Notifications
You must be signed in to change notification settings - Fork 9
Closed
Description
The custom withDeadline function crashes with a force unwrap on result! when both the timeout task and the body task complete with nil. This is possible if Task.sleep is cancelled and body() also throws or returns nil wrapped in Result?.self.
Problem line:
return result! // nil cannot occur here; it has been satisfied by either the sleep or the task
However, under certain conditions (like rapid cancellation), neither task may return a non-nil result, making the force unwrap unsafe and causing a crash.
✅ Suggested Fix:
Refactor the implementation to use withThrowingTaskGroup and avoid wrapping in Result?, eliminating the need for unsafe unwrapping and improving safety:
try await withThrowingTaskGroup(of: T.self) { group in
group.addTask {
try await Task.sleep(until: deadline, clock: clock)
throw DeadlineFailure.timedOut(clock, deadline)
}
group.addTask {
try await body()
}
let result = try await group.next()! // safe here, both tasks are added
group.cancelAll()
return result
}
This avoids crashing and ensures all edge cases are handled gracefully.
Code to test this crash:
static func getNTPTime() async -> Date? {
let servers = ["time.apple.com", "pool.ntp.org", "time.cloudflare.com", "time.google.com"]
return await withTaskGroup(of: Date?.self) { group in
for server in servers {
group.addTask {
do {
let ntpClient = NTPClient(
config: NTPClient.Config(version: .v4),
server: server
)
let response = try await ntpClient.query(timeout: .seconds(2))
let secondsComponent = Double(response.offset.components.seconds)
let attosecondsComponent = Double(response.offset.components.attoseconds) / 1_000_000_000_000_000_000
let offsetSeconds = secondsComponent + attosecondsComponent
let accurateDate = Date().addingTimeInterval(offsetSeconds)
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
formatter.timeZone = .current
print("✅ Success from \(server)")
print("System time: \(formatter.string(from: Date()))")
print("NTP offset: \(offsetSeconds) seconds")
print("Accurate time: \(formatter.string(from: accurateDate))")
return accurateDate
} catch {
print("⚠️ Failed to get time from \(server): \(error)")
return nil
}
}
}
for await result in group {
if let date = result {
group.cancelAll() // Stop other pending tasks
return date
}
}
return nil
}
}

Metadata
Metadata
Assignees
Labels
No labels
