Skip to content

Commit 66871b9

Browse files
Deterministically crash in DefaultActorImpl::destroy() when self escapes in deinit
1 parent 6151731 commit 66871b9

File tree

2 files changed

+21
-2
lines changed

2 files changed

+21
-2
lines changed

stdlib/public/Concurrency/Actor.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,22 @@ void ProcessOutOfLineJob::process(Job *job) {
16531653
#endif /* !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS */
16541654

16551655
void DefaultActorImpl::destroy() {
1656+
HeapObject *object = asAbstract(this);
1657+
size_t retainCount = swift_retainCount(object);
1658+
if (SWIFT_UNLIKELY(retainCount > 1)) {
1659+
auto descriptor = object->metadata->getTypeContextDescriptor();
1660+
1661+
swift_Concurrency_fatalError(0,
1662+
"Object %p of class %s deallocated with non-zero retain "
1663+
"count %zd. This object's deinit, or something called "
1664+
"from it, may have created a strong reference to self "
1665+
"which outlived deinit, resulting in a dangling "
1666+
"reference.\n",
1667+
object,
1668+
descriptor ? descriptor->Name.get() : "<unknown>",
1669+
retainCount);
1670+
}
1671+
16561672
#if SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
16571673
// TODO (rokhinip): Do something to assert that the lock is unowned
16581674
#else

test/Concurrency/Runtime/actor_deinit_escaping_self.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
// REQUIRES: libdispatch
55
// REQUIRES: concurrency
66
// REQUIRES: concurrency_runtime
7-
// REQUIRES: swift_stdlib_asserts
87
// UNSUPPORTED: back_deployment_runtime
98

109
import _Concurrency
1110
import Dispatch
1211
import StdlibUnittest
1312

13+
func formatAddr(_ obj: AnyObject) -> String {
14+
"0x" + String(unsafeBitCast(obj, to: UInt.self), radix: 16)
15+
}
16+
1417
actor EscapeLocked {
1518
var k: Int = 1
1619

@@ -27,7 +30,7 @@ actor EscapeLocked {
2730
}
2831
let r = g.wait(timeout: .now() + .milliseconds(500))
2932
expectEqual(r, .timedOut)
30-
expectCrashLater(withMessage: "Assertion failed: (!oldState.getFirstUnprioritisedJob() && \"actor has queued jobs at destruction\"), function destroy")
33+
expectCrashLater(withMessage: "Object \(formatAddr(self)) of class EscapeLocked deallocated with non-zero retain count 2. This object's deinit, or something called from it, may have created a strong reference to self which outlived deinit, resulting in a dangling reference.")
3134
}
3235
}
3336

0 commit comments

Comments
 (0)