Skip to content

Commit 44ff7e5

Browse files
authored
Merge pull request swiftlang#78817 from mikeash/fix-supersleep
[Concurrency] Fix Task.sleep on values greater than Int64.max.
2 parents b4640d1 + 29efab6 commit 44ff7e5

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed

stdlib/public/Concurrency/DispatchGlobalExecutor.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,13 @@ void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay,
256256
job->schedulerPrivate[SwiftJobDispatchQueueIndex] =
257257
DISPATCH_QUEUE_GLOBAL_EXECUTOR;
258258

259+
// dispatch_time takes a signed int64_t. SwiftJobDelay is unsigned, so
260+
// extremely large values get interpreted as negative numbers, which results
261+
// in zero delay. Clamp the value to INT64_MAX. That's about 292 years, so
262+
// there should be no noticeable difference.
263+
if (delay > (SwiftJobDelay)INT64_MAX)
264+
delay = INT64_MAX;
265+
259266
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delay);
260267
dispatch_after_f(when, queue, dispatchContext, dispatchFunction);
261268
}

test/Concurrency/Runtime/async_task_sleep.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Dispatch
1818
static func main() async {
1919
await testSleepDuration()
2020
await testSleepDoesNotBlock()
21+
await testSleepHuge()
2122
}
2223

2324
static func testSleepDuration() async {
@@ -45,4 +46,31 @@ import Dispatch
4546
// CHECK: Run second
4647
await task.get()
4748
}
49+
50+
static func testSleepHuge() async {
51+
// Make sure nanoseconds values about Int64.max don't get interpreted as
52+
// negative and fail to sleep.
53+
let task1 = detach {
54+
try await Task.sleep(nanoseconds: UInt64(Int64.max) + 1)
55+
}
56+
let task2 = detach {
57+
try await Task.sleep(nanoseconds: UInt64.max)
58+
}
59+
60+
try! await Task.sleep(nanoseconds: UInt64(pause))
61+
62+
task1.cancel()
63+
task2.cancel()
64+
65+
// These should throw due to being canceled. If the sleeps completed then
66+
// the cancellation will do nothing and we won't throw, which is a failure.
67+
do {
68+
_ = try await task1.value
69+
fatalError("Sleep 1 completed early.")
70+
} catch {}
71+
do {
72+
_ = try await task2.value
73+
fatalError("Sleep 2 completed early.")
74+
} catch {}
75+
}
4876
}

0 commit comments

Comments
 (0)