diff --git a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp index fee1a5f646dd9..844984a81dbd3 100644 --- a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp +++ b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp @@ -276,6 +276,13 @@ void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay, job->schedulerPrivate[SwiftJobDispatchQueueIndex] = DISPATCH_QUEUE_GLOBAL_EXECUTOR; + // dispatch_time takes a signed int64_t. SwiftJobDelay is unsigned, so + // extremely large values get interpreted as negative numbers, which results + // in zero delay. Clamp the value to INT64_MAX. That's about 292 years, so + // there should be no noticeable difference. + if (delay > (SwiftJobDelay)INT64_MAX) + delay = INT64_MAX; + dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delay); dispatch_after_f(when, queue, dispatchContext, dispatchFunction); } diff --git a/test/Concurrency/Runtime/async_task_sleep.swift b/test/Concurrency/Runtime/async_task_sleep.swift index 42b792d5f1b8c..ec242a1132c28 100644 --- a/test/Concurrency/Runtime/async_task_sleep.swift +++ b/test/Concurrency/Runtime/async_task_sleep.swift @@ -18,6 +18,7 @@ import Dispatch static func main() async { await testSleepDuration() await testSleepDoesNotBlock() + await testSleepHuge() } static func testSleepDuration() async { @@ -45,4 +46,31 @@ import Dispatch // CHECK: Run second await task.get() } + + static func testSleepHuge() async { + // Make sure nanoseconds values about Int64.max don't get interpreted as + // negative and fail to sleep. + let task1 = detach { + try await Task.sleep(nanoseconds: UInt64(Int64.max) + 1) + } + let task2 = detach { + try await Task.sleep(nanoseconds: UInt64.max) + } + + try! await Task.sleep(nanoseconds: UInt64(pause)) + + task1.cancel() + task2.cancel() + + // These should throw due to being canceled. If the sleeps completed then + // the cancellation will do nothing and we won't throw, which is a failure. + do { + _ = try await task1.value + fatalError("Sleep 1 completed early.") + } catch {} + do { + _ = try await task2.value + fatalError("Sleep 2 completed early.") + } catch {} + } }