Files
swift-mirror/test/Concurrency/Runtime/async_taskgroup_discarding_neverConsumingTasks.swift
2025-08-21 12:54:00 -07:00

119 lines
3.5 KiB
Swift

// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s --dump-input=always
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library -swift-version 5 -strict-concurrency=complete -enable-upcoming-feature NonisolatedNonsendingByDefault) | %FileCheck %s --dump-input=always
// REQUIRES: swift_feature_NonisolatedNonsendingByDefault
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime
// UNSUPPORTED: freestanding
// FIXME: enable discarding taskgroup on windows; rdar://104762037
// UNSUPPORTED: OS=windows-msvc
actor Waiter {
let until: Int
var count: Int
var cc: CheckedContinuation<Int, Never>?
init(until: Int) {
self.until = until
self.count = 0
}
func increment() {
self.count += 1
if self.until <= self.count {
if let cc = self.cc {
cc.resume(returning: self.count)
}
}
}
func wait() async -> Int {
if self.until <= self.count {
return self.count
}
return await withCheckedContinuation { cc in
self.cc = cc
}
}
}
func test_discardingTaskGroup_neverConsume() async {
print(">>> \(#function)")
let until = 100
let waiter = Waiter(until: until)
print("Start tasks: \(until)")
let allTasks = await withDiscardingTaskGroup() { group in
for n in 1...until {
group.addTask {
try? await Task.sleep(until: .now + .milliseconds(100), clock: .continuous)
await waiter.increment()
}
}
return until
}
// CHECK: all tasks: 100
print("all tasks: \(allTasks)")
}
func test_discardingTaskGroup_neverConsume(sleepBeforeGroupWaitAll: Duration) async {
print(">>> \(#function)")
let until = 100
let waiter = Waiter(until: until)
print("Start tasks: \(until)")
let allTasks = await withDiscardingTaskGroup() { group in
for n in 1...until {
group.addTask {
try? await Task.sleep(until: .now + .milliseconds(100), clock: .continuous)
await waiter.increment()
}
}
// wait a little bit, so some tasks complete before we hit the implicit "wait at end of task group scope"
try? await Task.sleep(until: .now + sleepBeforeGroupWaitAll, clock: .continuous)
return until
}
// CHECK: all tasks: 100
print("all tasks: \(allTasks)")
}
func test_discardingTaskGroup_bigReturn() async {
print(">>> \(#function)")
// Test returning a very large value to ensure we don't overflow memory.
let array = await withDiscardingTaskGroup { group in
group.addTask {}
try? await Task.sleep(until: .now + .milliseconds(100), clock: .continuous)
// InlineArray.init(repeating:) uses a lot of stack space with optimizations
// disabled, so set one up in a less friendly but less stack-consuming way.
let ptr = UnsafeMutablePointer<InlineArray<32768, Int>>.allocate(capacity: 1)
ptr.withMemoryRebound(to: Int.self, capacity: 32768) {
$0.initialize(repeating: 12345, count: 32768)
}
// Deliberately leak `ptr` to avoid needing to save any temporaries.
return ptr.pointee
}
// CHECK: Huge return value produced: 12345 12345
print("Huge return value produced:", array[0], array[32767])
}
@main struct Main {
static func main() async {
await test_discardingTaskGroup_neverConsume()
await test_discardingTaskGroup_neverConsume(sleepBeforeGroupWaitAll: .milliseconds(500))
await test_discardingTaskGroup_bigReturn()
}
}