mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This isn't a nice fix, because I can't see a good way to fix this problem robustly. The issue is that the Concurrency runtime may mutate the AsyncTask structure from a thread other than the one that is reflecting the current task, so looking through at the child tasks is inherently dangerous and may cause the test to crash at random, even though it will *often* work. To make this a lot less likely, add a .25s delay before reflecting the parent task. rdar://150478597
130 lines
4.1 KiB
Swift
130 lines
4.1 KiB
Swift
// RUN: %empty-directory(%t)
|
|
// RUN: %target-clang -x c %S/Inputs/reflect_task.c -o %t/reflect_task.c.o -c
|
|
// RUN: %target-build-swift -c -target %target-swift-5.1-abi-triple -parse-stdlib -parse-as-library -lswiftSwiftReflectionTest %s -o %t/reflect_task.swift.o -module-name reflect_task
|
|
// RUN: %target-build-swift %t/reflect_task.swift.o %t/reflect_task.c.o -o %t/reflect_task
|
|
// RUN: %target-codesign %t/reflect_task
|
|
|
|
// RUN: %target-run %target-swift-reflection-test %t/reflect_task | %FileCheck %s --dump-input=always
|
|
|
|
// REQUIRES: reflection_test_support
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: concurrency
|
|
// UNSUPPORTED: use_os_stdlib
|
|
// UNSUPPORTED: back_deployment_runtime
|
|
// UNSUPPORTED: asan
|
|
|
|
import Swift
|
|
import _Concurrency
|
|
|
|
import SwiftReflectionTest
|
|
|
|
// We do not use swift_task_getCurrent directly since we also can get a
|
|
// declaration (if we get unlucky) from _Concurrency with a differing type. So
|
|
// we instead just compile a shim in a .c file that actually calls
|
|
// swift_task_getCurrent and avoid the collision.
|
|
@_silgen_name("getCurrentTaskShim")
|
|
func _getCurrentTaskShim() -> UInt
|
|
|
|
func add(_ a: UInt, _ b: UInt) async -> UInt {
|
|
if b == 0 {
|
|
reflect(asyncTask: _getCurrentTaskShim())
|
|
return a
|
|
} else {
|
|
return await add(a, b - 1) + 1
|
|
}
|
|
}
|
|
|
|
func sleepForever() async -> Int {
|
|
if #available(SwiftStdlib 5.7, *) {
|
|
try? await Task.sleep(for: .seconds(1_000_000_000))
|
|
return 42
|
|
} else {
|
|
fatalError("This test shouldn't be running against old stdlibs.")
|
|
}
|
|
}
|
|
|
|
func testNestedCallsTask() async {
|
|
reflectionLog(str: "testNestedCallsTask")
|
|
// CHECK: testNestedCallsTask
|
|
|
|
let n = await add(100, 100)
|
|
reflect(any: n)
|
|
// CHECK: Reflecting an async task.
|
|
// CHECK: Async task {{0x[0-9a-fA-F]*}}
|
|
|
|
// The actual number of chunks we'll get depends on internal implementation
|
|
// details that we don't want this test to depend on. We'll just make sure
|
|
// we get at least two, and ignore the details.
|
|
// CHECK: Slab pointer {{0x[0-9a-fA-F]*}}
|
|
// CHECK: Slab size {{[0-9]{2,}()}}
|
|
// CHECK: Chunk at {{0x[0-9a-fA-F]*}} length {{[1-9][0-9]*}} kind {{[0-9]*}}
|
|
// CHECK: Slab pointer {{0x[0-9a-fA-F]*}}
|
|
// CHECK: Slab size {{[0-9]{2,}()}}
|
|
// CHECK: Chunk at {{0x[0-9a-fA-F]*}} length {{[1-9[[0-9]*}} kind {{[0-9]*}}
|
|
}
|
|
|
|
// ###HACK: Avoid a race condition by waiting a bit.
|
|
//
|
|
// If we reflect the current task immediately, there is a chance that the
|
|
// runtime is busy manipulating the task structure (e.g. allocating using the
|
|
// task allocator on a child task, or adding a child task) in parallel with
|
|
// the reflection. This can cause this test to crash when the
|
|
// swift-reflection-test binary asks to read from an uninitialized address.
|
|
//
|
|
// While we could stop the crashing by changing the pipe memory reader to
|
|
// return error codes when it is unable to read memory, that would still
|
|
// result in this test failing randomly. It's not obvious what the "proper"
|
|
// fix for this should be; the Concurrency runtime isn't going to export
|
|
// anything that would help here.
|
|
func dodgeRaceCondition() async {
|
|
if #available(SwiftStdlib 5.7, *) {
|
|
try! await Task.sleep(for: .milliseconds(250))
|
|
} else {
|
|
fatalError("This test shouldn't be running against old stdlibs.")
|
|
}
|
|
}
|
|
|
|
func testOneAsyncLet() async {
|
|
reflectionLog(str: "testOneAsyncLet")
|
|
// CHECK: testOneAsyncLet
|
|
|
|
async let alet = sleepForever()
|
|
|
|
await dodgeRaceCondition()
|
|
|
|
reflect(asyncTask: _getCurrentTaskShim())
|
|
// CHECK: Async task {{0x[0-9a-fA-F]*}}
|
|
// CHECK: children = {
|
|
// CHECK: Async task {{0x[0-9a-fA-F]*}}
|
|
// CHECK: }
|
|
}
|
|
|
|
func testMultipleAsyncLet() async {
|
|
reflectionLog(str: "testMultipleAsyncLet")
|
|
// CHECK: testMultipleAsyncLet
|
|
|
|
async let alet1 = sleepForever()
|
|
async let alet2 = sleepForever()
|
|
|
|
await dodgeRaceCondition()
|
|
|
|
reflect(asyncTask: _getCurrentTaskShim())
|
|
// CHECK: Async task {{0x[0-9a-fA-F]*}}
|
|
// CHECK: children = {
|
|
// CHECK: Async task {{0x[0-9a-fA-F]*}}
|
|
// CHECK: Async task {{0x[0-9a-fA-F]*}}
|
|
// CHECK: }
|
|
}
|
|
|
|
@main struct Main {
|
|
static func main() async {
|
|
await testNestedCallsTask()
|
|
await testOneAsyncLet()
|
|
await testMultipleAsyncLet()
|
|
|
|
doneReflecting()
|
|
}
|
|
}
|
|
|
|
// CHECK: Done.
|