Skip to content

Commit 95564f2

Browse files
Merge pull request #12387 from ktopley-apple/dispatch-timebase-overflow
Fixes overflow trap when creating DispatchTime objects with large upt…
2 parents a9a477a + 1da4b04 commit 95564f2

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

stdlib/public/SDK/Dispatch/Time.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,26 @@ public struct DispatchTime : Comparable {
5353
/// system sleep time), not zero nanoseconds since boot.
5454
public init(uptimeNanoseconds: UInt64) {
5555
var rawValue = uptimeNanoseconds
56-
if (DispatchTime.timebaseInfo.numer != DispatchTime.timebaseInfo.denom) {
57-
rawValue = (rawValue * UInt64(DispatchTime.timebaseInfo.denom)
58-
+ UInt64(DispatchTime.timebaseInfo.numer - 1)) / UInt64(DispatchTime.timebaseInfo.numer)
56+
57+
// UInt64.max means distantFuture. Do not try to scale it.
58+
if rawValue != UInt64.max && DispatchTime.timebaseInfo.numer != DispatchTime.timebaseInfo.denom {
59+
var (result, overflow) = rawValue.multipliedReportingOverflow(by: UInt64(DispatchTime.timebaseInfo.denom))
60+
if !overflow {
61+
(result, overflow) = result.addingReportingOverflow(UInt64(DispatchTime.timebaseInfo.numer - 1))
62+
}
63+
rawValue = overflow ? UInt64.max : result / UInt64(DispatchTime.timebaseInfo.numer)
5964
}
6065
self.rawValue = dispatch_time_t(rawValue)
6166
}
6267

6368
public var uptimeNanoseconds: UInt64 {
6469
var result = self.rawValue
65-
if (DispatchTime.timebaseInfo.numer != DispatchTime.timebaseInfo.denom) {
66-
result = result * UInt64(DispatchTime.timebaseInfo.numer) / UInt64(DispatchTime.timebaseInfo.denom)
70+
var overflow: Bool
71+
72+
// UInt64.max means distantFuture. Do not try to scale it.
73+
if rawValue != UInt64.max && DispatchTime.timebaseInfo.numer != DispatchTime.timebaseInfo.denom {
74+
(result, overflow) = result.multipliedReportingOverflow(by: UInt64(DispatchTime.timebaseInfo.numer))
75+
result = overflow ? UInt64.max : result / UInt64(DispatchTime.timebaseInfo.denom)
6776
}
6877
return result
6978
}

test/stdlib/Dispatch.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99

1010
// REQUIRES: objc_interop
1111

12-
// FIXME: rdar://34751238 DispatchTime.addSubtract test traps
13-
// XFAIL: CPU=armv7 || CPU=armv7k || CPU=armv7s || CPU=arm64
14-
15-
1612
import Dispatch
1713
import Foundation
1814
import StdlibUnittest
@@ -119,6 +115,31 @@ DispatchAPI.test("DispatchTime comparisons") {
119115
}
120116
}
121117

118+
DispatchAPI.test("DispatchTime.create") {
119+
var info = mach_timebase_info_data_t(numer: 1, denom: 1)
120+
mach_timebase_info(&info)
121+
let scales = info.numer != info.denom
122+
123+
// Simple tests for non-overflow behavior
124+
var time = DispatchTime(uptimeNanoseconds: 0)
125+
expectEqual(time.uptimeNanoseconds, 0)
126+
127+
time = DispatchTime(uptimeNanoseconds: 15 * NSEC_PER_SEC)
128+
expectEqual(time.uptimeNanoseconds, 15 * NSEC_PER_SEC)
129+
130+
// On platforms where the timebase scale is not 1, the next two cases
131+
// overflow and become DISPATCH_TIME_FOREVER (UInt64.max) instead of trapping.
132+
time = DispatchTime(uptimeNanoseconds: UInt64.max - 1)
133+
expectEqual(time.uptimeNanoseconds, scales ? UInt64.max : UInt64.max - UInt64(1))
134+
135+
time = DispatchTime(uptimeNanoseconds: UInt64.max / 2)
136+
expectEqual(time.uptimeNanoseconds, scales ? UInt64.max : UInt64.max / 2)
137+
138+
// UInt64.max must always be returned as UInt64.max.
139+
time = DispatchTime(uptimeNanoseconds: UInt64.max)
140+
expectEqual(time.uptimeNanoseconds, UInt64.max)
141+
}
142+
122143
DispatchAPI.test("DispatchTime.addSubtract") {
123144
var then = DispatchTime.now() + Double.infinity
124145
expectEqual(DispatchTime.distantFuture, then)

0 commit comments

Comments
 (0)