Skip to content

Commit f1e3002

Browse files
Added more tests for isolated actor deinit:
* Recursive deallocation with multiple locks held * (UB) Escaping self triggers assertion in stdlib
1 parent 7505cd2 commit f1e3002

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library)
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: concurrency_runtime
6+
// UNSUPPORTED: back_deployment_runtime
7+
8+
import _Concurrency
9+
import Dispatch
10+
import StdlibUnittest
11+
12+
actor EscapeLocked {
13+
var k: Int = 1
14+
15+
func increment() {
16+
k += 1
17+
}
18+
19+
deinit {
20+
let g = DispatchGroup()
21+
g.enter()
22+
Task.detached {
23+
await self.increment()
24+
g.leave()
25+
}
26+
let r = g.wait(timeout: .now() + .milliseconds(500))
27+
expectEqual(r, .timedOut)
28+
expectCrashLater(withMessage: "Assertion failed: (!oldState.getFirstUnprioritisedJob() && \"actor has queued jobs at destruction\"), function destroy")
29+
}
30+
}
31+
32+
@main struct Main {
33+
static func main() async {
34+
let tests = TestSuite("EscapingSelf")
35+
tests.test("escape while locked") {
36+
_ = EscapeLocked()
37+
}
38+
await runAllTestsAsync()
39+
}
40+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-stdlib -parse-as-library) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
6+
// REQUIRES: concurrency_runtime
7+
// UNSUPPORTED: back_deployment_runtime
8+
9+
import Swift
10+
import _Concurrency
11+
12+
#if canImport(Darwin)
13+
import Darwin
14+
typealias ThreadID = pthread_t
15+
func getCurrentThreadID() -> ThreadID { pthread_self() }
16+
func equalThreadIDs(_ a: ThreadID, _ b: ThreadID) -> Bool { pthread_equal(a, b) != 0 }
17+
#elseif canImport(Glibc)
18+
import Glibc
19+
typealias ThreadID = pthread_t
20+
func getCurrentThreadID() -> ThreadID { pthread_self() }
21+
func equalThreadIDs(_ a: ThreadID, _ b: ThreadID) -> Bool { pthread_equal(a, b) != 0 }
22+
#elseif os(Windows)
23+
import WinSDK
24+
typealias ThreadID = UInt32
25+
func getCurrentThreadID() -> ThreadID { GetCurrentThreadId() }
26+
func equalThreadIDs(_ a: ThreadID, _ b: ThreadID) -> Bool { a == b }
27+
#endif
28+
29+
var mainThread: ThreadID?
30+
func isMainThread() -> Bool {
31+
return equalThreadIDs(getCurrentThreadID(), mainThread!)
32+
}
33+
34+
@_silgen_name("swift_task_isCurrentExecutor")
35+
private func isCurrentExecutor(_ executor: Builtin.Executor) -> Bool
36+
37+
func getExecutor(_ a: AnyActor) -> Builtin.Executor {
38+
let pack = (a, UnsafeRawPointer?.none)
39+
return unsafeBitCast(pack, to: Builtin.Executor.self)
40+
}
41+
42+
func isCurrent(_ a: AnyActor) -> Bool {
43+
return isCurrentExecutor(getExecutor(a))
44+
}
45+
46+
func isMainExecutor() -> Bool {
47+
isCurrentExecutor(Builtin.buildMainActorExecutorRef())
48+
}
49+
50+
actor Foo {
51+
let name: String
52+
let child: Foo?
53+
54+
init(_ name: String, _ child: Foo?) {
55+
self.name = name
56+
self.child = child
57+
}
58+
59+
deinit {
60+
print("DEINIT: \(name) isolated:\(isCurrent(self)) mainThread:\(isMainThread())")
61+
}
62+
}
63+
64+
// CHECK: DEINIT: a isolated:true mainThread:true
65+
// CHECK: DEINIT: b isolated:true mainThread:true
66+
// CHECK: DEINIT: c isolated:true mainThread:true
67+
// CHECK: DEINIT: d isolated:true mainThread:true
68+
// CHECK: DONE
69+
70+
@main
71+
struct Main {
72+
static func main() async {
73+
mainThread = getCurrentThreadID()
74+
do {
75+
_ = Foo("a", Foo("b", Foo("c", Foo("d", nil))))
76+
}
77+
print("DONE")
78+
}
79+
}

0 commit comments

Comments
 (0)