Skip to content

Commit 921d6d8

Browse files
authored
Merge pull request swiftlang#81332 from al45tair/eng/PR-148899609
[Concurrency] Don't pass negative times to the Dispatch code.
2 parents 88de665 + 08f4d2f commit 921d6d8

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

stdlib/public/Concurrency/DispatchExecutor.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,20 +161,29 @@ enum DispatchClockID: CInt {
161161
@available(SwiftStdlib 6.2, *)
162162
extension DispatchExecutorProtocol {
163163

164+
func clamp(_ components: (seconds: Int64, attoseconds: Int64))
165+
-> (seconds: Int64, attoseconds: Int64) {
166+
if components.seconds < 0
167+
|| components.seconds == 0 && components.attoseconds < 0 {
168+
return (seconds: 0, attoseconds: 0)
169+
}
170+
return (seconds: components.seconds, attoseconds: components.attoseconds)
171+
}
172+
164173
func timestamp<C: Clock>(for instant: C.Instant, clock: C)
165174
-> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) {
166175
if clock.traits.contains(.continuous) {
167176
let dispatchClock: ContinuousClock = .continuous
168177
let instant = dispatchClock.convert(instant: instant, from: clock)!
169-
let (seconds, attoseconds) = instant._value.components
178+
let (seconds, attoseconds) = clamp(instant._value.components)
170179
let nanoseconds = attoseconds / 1_000_000_000
171180
return (clockID: .continuous,
172181
seconds: Int64(seconds),
173182
nanoseconds: Int64(nanoseconds))
174183
} else {
175184
let dispatchClock: SuspendingClock = .suspending
176185
let instant = dispatchClock.convert(instant: instant, from: clock)!
177-
let (seconds, attoseconds) = instant._value.components
186+
let (seconds, attoseconds) = clamp(instant._value.components)
178187
let nanoseconds = attoseconds / 1_000_000_000
179188
return (clockID: .suspending,
180189
seconds: Int64(seconds),
@@ -185,7 +194,7 @@ extension DispatchExecutorProtocol {
185194
func delay<C: Clock>(from duration: C.Duration, clock: C)
186195
-> (seconds: Int64, nanoseconds: Int64) {
187196
let swiftDuration = clock.convert(from: duration)!
188-
let (seconds, attoseconds) = swiftDuration.components
197+
let (seconds, attoseconds) = clamp(swiftDuration.components)
189198
let nanoseconds = attoseconds / 1_000_000_000
190199
return (seconds: seconds, nanoseconds: nanoseconds)
191200
}

test/Concurrency/Runtime/async_task_sleep.swift

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ import Dispatch
1414
@available(SwiftStdlib 5.1, *)
1515
@main struct Main {
1616
static let pause = 500_000_000 // 500ms
17-
17+
1818
static func main() async {
1919
await testSleepDuration()
2020
await testSleepDoesNotBlock()
2121
await testSleepHuge()
22+
if #available(SwiftStdlib 5.7, *) {
23+
await testSleepNegative()
24+
}
2225
}
2326

2427
static func testSleepDuration() async {
@@ -34,7 +37,7 @@ import Dispatch
3437

3538
static func testSleepDoesNotBlock() async {
3639
// FIXME: Should run on main executor
37-
let task = detach {
40+
let task = Task.detached {
3841
print("Run first")
3942
}
4043

@@ -50,10 +53,10 @@ import Dispatch
5053
static func testSleepHuge() async {
5154
// Make sure nanoseconds values about Int64.max don't get interpreted as
5255
// negative and fail to sleep.
53-
let task1 = detach {
56+
let task1 = Task.detached {
5457
try await Task.sleep(nanoseconds: UInt64(Int64.max) + 1)
5558
}
56-
let task2 = detach {
59+
let task2 = Task.detached {
5760
try await Task.sleep(nanoseconds: UInt64.max)
5861
}
5962

@@ -73,4 +76,28 @@ import Dispatch
7376
fatalError("Sleep 2 completed early.")
7477
} catch {}
7578
}
79+
80+
@available(SwiftStdlib 5.7, *)
81+
static func testSleepNegative() async {
82+
// Make sure that "negative" times don't cause us to sleep forever
83+
let negativeDuration = Duration(secondsComponent: -60,
84+
attosecondsComponent: 0)
85+
let negativeTime = unsafe unsafeBitCast(negativeDuration,
86+
to: ContinuousClock.Instant.self)
87+
88+
let task = Task.detached {
89+
try await Task.sleep(until: negativeTime)
90+
}
91+
92+
try! await Task.sleep(nanoseconds: UInt64(pause))
93+
94+
task.cancel()
95+
96+
// The sleep should complete; if they throw, this is a failure.
97+
do {
98+
_ = try await task.value
99+
} catch {
100+
fatalError("Sleep tried to wait for a negative time")
101+
}
102+
}
76103
}

0 commit comments

Comments
 (0)