Skip to content

Commit 5788a2c

Browse files
authored
Merge pull request #59959 from lorentey/duration-components-crash
[stdlib] Duration: Fix rare overflow trap in Int128.multipliedFullWidth
2 parents 5e9470a + 95a45db commit 5788a2c

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

stdlib/public/core/Int128.swift.gyb

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,24 @@ extension _${U}Int128: FixedWidthInteger {
350350
) -> (high: Self, low: Magnitude) {
351351
let isNegative = Self.isSigned && (self._isNegative != other._isNegative)
352352

353+
func sum(_ x: Low, _ y: Low) -> (high: Low, low: Low) {
354+
let (sum, overflow) = x.addingReportingOverflow(y)
355+
return (overflow ? 1 : 0, sum)
356+
}
357+
353358
func sum(_ x: Low, _ y: Low, _ z: Low) -> (high: Low, low: Low) {
354-
let (sum1, overflow1) = x.addingReportingOverflow(y)
355-
let (sum2, overflow2) = sum1.addingReportingOverflow(z)
356-
let carry: Low = (overflow1 ? 1 : 0) + (overflow2 ? 1 : 0)
357-
return (carry, sum2)
359+
let s1 = sum(x, y)
360+
let s2 = sum(s1.low, z)
361+
return (s1.high &+ s2.high, s2.low)
362+
}
363+
364+
func sum(
365+
_ x0: Low, _ x1: Low, _ x2: Low, _ x3: Low
366+
) -> (high: Low, low: Low) {
367+
let s1 = sum(x0, x1)
368+
let s2 = sum(x2, x3)
369+
let s = sum(s1.low, s2.low)
370+
return (s1.high &+ s2.high &+ s.high, s.low)
358371
}
359372

360373
let lhs = self.magnitude
@@ -366,12 +379,14 @@ extension _${U}Int128: FixedWidthInteger {
366379
let d = rhs.high.multipliedFullWidth(by: lhs.high)
367380

368381
let mid1 = sum(a.high, b.low, c.low)
369-
let mid2 = sum(b.high, c.high, d.low)
382+
let mid2 = sum(b.high, c.high, mid1.high, d.low)
370383

371-
let low = _UInt128(high: mid1.low, low: a.low)
372384
let high = _${U}Int128(
373-
high: High(mid2.high + d.high),
374-
low: mid1.high + mid2.low)
385+
high: High(d.high &+ mid2.high), // Note: this addition will never wrap
386+
low: mid2.low)
387+
let low = _UInt128(
388+
high: mid1.low,
389+
low: a.low)
375390

376391
if isNegative {
377392
let (lowComplement, overflow) = (~low).addingReportingOverflow(.one)

test/Concurrency/Runtime/clock.swift

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,16 @@
99
// UNSUPPORTED: back_deployment_runtime
1010
// UNSUPPORTED: back_deploy_concurrency
1111

12-
// This test is disabled because it seems to require dedicated CPU access:
13-
// REQUIRES: rdar94451729
14-
1512
import _Concurrency
1613
import StdlibUnittest
1714

1815
var tests = TestSuite("Time")
1916

2017
@main struct Main {
2118
static func main() async {
22-
tests.test("ContinuousClock sleep") {
19+
tests.test("ContinuousClock sleep")
20+
.skip(.always("failed despite increased timeout (rdar://94451729)"))
21+
.code {
2322
let clock = ContinuousClock()
2423
let elapsed = await clock.measure {
2524
try! await clock.sleep(until: .now + .milliseconds(100))
@@ -29,7 +28,9 @@ var tests = TestSuite("Time")
2928
expectLT(elapsed, .milliseconds(1000))
3029
}
3130

32-
tests.test("ContinuousClock sleep with tolerance") {
31+
tests.test("ContinuousClock sleep with tolerance")
32+
.skip(.always("failed despite increased timeout (rdar://94451729)"))
33+
.code {
3334
let clock = ContinuousClock()
3435
let elapsed = await clock.measure {
3536
try! await clock.sleep(until: .now + .milliseconds(100), tolerance: .milliseconds(100))
@@ -39,15 +40,19 @@ var tests = TestSuite("Time")
3940
expectLT(elapsed, .milliseconds(2000))
4041
}
4142

42-
tests.test("ContinuousClock sleep longer") {
43+
tests.test("ContinuousClock sleep longer")
44+
.skip(.always("failed despite increased timeout (rdar://94451729)"))
45+
.code {
4346
let elapsed = await ContinuousClock().measure {
4447
try! await Task.sleep(until: .now + .seconds(1), clock: .continuous)
4548
}
4649
expectGT(elapsed, .seconds(1) - .milliseconds(90))
4750
expectLT(elapsed, .seconds(1) + .milliseconds(1000))
4851
}
4952

50-
tests.test("SuspendingClock sleep") {
53+
tests.test("SuspendingClock sleep")
54+
.skip(.always("failed despite increased timeout (rdar://94451729)"))
55+
.code {
5156
let clock = SuspendingClock()
5257
let elapsed = await clock.measure {
5358
try! await clock.sleep(until: .now + .milliseconds(100))
@@ -57,7 +62,9 @@ var tests = TestSuite("Time")
5762
expectLT(elapsed, .milliseconds(1000))
5863
}
5964

60-
tests.test("SuspendingClock sleep with tolerance") {
65+
tests.test("SuspendingClock sleep with tolerance")
66+
.skip(.always("failed despite increased timeout (rdar://94451729)"))
67+
.code {
6168
let clock = SuspendingClock()
6269
let elapsed = await clock.measure {
6370
try! await clock.sleep(until: .now + .milliseconds(100), tolerance: .milliseconds(100))
@@ -67,7 +74,9 @@ var tests = TestSuite("Time")
6774
expectLT(elapsed, .milliseconds(2000))
6875
}
6976

70-
tests.test("SuspendingClock sleep longer") {
77+
tests.test("SuspendingClock sleep longer")
78+
.skip(.always("failed despite increased timeout (rdar://94451729)"))
79+
.code {
7180
let elapsed = await SuspendingClock().measure {
7281
try! await Task.sleep(until: .now + .seconds(1), clock: .suspending)
7382
}
@@ -110,6 +119,44 @@ var tests = TestSuite("Time")
110119
expectEqual(twoSeconds, .seconds(2))
111120
}
112121

122+
tests.test("Duration components/whole second increments") {
123+
for i in 0 ..< 1_000_000 {
124+
let d = Duration.seconds(i)
125+
let comps = d.components
126+
expectEqual(comps.seconds, Int64(i))
127+
expectEqual(comps.attoseconds, 0)
128+
}
129+
}
130+
131+
tests.test("Duration components/1ms increments") {
132+
for i in 0 ..< 1_000_000 {
133+
let d = Duration.milliseconds(i)
134+
let comps = d.components
135+
expectEqual(comps.seconds, Int64(i / 1000))
136+
expectEqual(comps.attoseconds, Int64(i % 1000) * 1_000_000_000_000_000)
137+
}
138+
}
139+
140+
tests.test("Duration components/100µs increments") {
141+
for i in 0 ..< 1_000_000 {
142+
let ms = 100 * i
143+
let d = Duration.microseconds(ms)
144+
let comps = d.components
145+
expectEqual(comps.seconds, Int64(ms / 1_000_000))
146+
expectEqual(comps.attoseconds, Int64(ms % 1_000_000) * 1_000_000_000_000)
147+
}
148+
}
149+
150+
tests.test("Duration components/200ns increments") {
151+
for i in 0 ..< 1_000_000 {
152+
let ns = 200 * i
153+
let d = Duration.nanoseconds(ns)
154+
let comps = d.components
155+
expectEqual(comps.seconds, Int64(ns / 1_000_000_000))
156+
expectEqual(comps.attoseconds, Int64(ns % 1_000_000_000) * 1_000_000_000)
157+
}
158+
}
159+
113160
await runAllTestsAsync()
114161
}
115162
}

0 commit comments

Comments
 (0)