Smuggling `self` out of a `deinit`
Asked Answered
W

1

7

What happens if I smuggle self out of my deinit, by assigning it to some external strong reference? This code below is clearly not well formed:

class C: CustomStringConvertible {
    let s = "abc"

    var description: String {
        return "C(id: \(ObjectIdentifier(self)), s: \(s))"
    }

    deinit {
        print("deinit")
        globalObject = self
    }
}

var globalObject: C!

do {
    let localObject = C()
    print("localObject: \(localObject)")
    print("end of `do`")
}

print("globalObject: \(globalObject!)")

You can't just "change your mind" about the deinitialization of an object, from the middle of a deinit. But interestingly, this code is non-deterministic, and it does occasionally sometimes complete successfully, printing:

localObject: C(id: ObjectIdentifier(0x00007f9063f00960), s: abc)
end of `do`
deinit
globalObject: C(id: ObjectIdentifier(0x00007f9063f00960), s: abc)

I'm running this using Code Runner, which is just running a single file Swift script using swiftc. So there's no unexpected Playground-owned references at play here.

Where's the non-determinism coming from?

Wriest answered 12/9, 2019 at 18:13 Comment(5)
I ran it ten times both in a Playground and on Repl.It, every time it crashed. How many did you have to run it to get it to complete successfully?Multiphase
Idk, roughly 1 in 10. swiftc --version gives Apple Swift version 5.1 (swiftlang-1100.0.212.5 clang-1100.0.28.2) Target: x86_64-apple-darwin19.0.0Wriest
I am using 5.0.1, and I ran it a few more times, still doesn't happen. Seems to be specific to 5.1 then...Multiphase
forums.swift.org/t/retain-self-in-deinit/6365 and #49118250Delphinium
@J.Doe I'm aware it's not legal. I just don't know why this doesn't always crash.Wriest
S
0

This is not an answer, but but too long for a comment:

Interestingly, I just appended:

for _ in 1...1000000 {
    print("intermediate: \(globalObject!)")
}
print("globalObject: \(globalObject!)")

and then:

swiftc test.swift
for ((i = 0; i < 10000; i++)); do
    ./test  | fgrep globalObject
done

What I get is (just once out of those 10000 runs):

Fatal error: Object was retained too many timesa(44854,0x10a92f5c0) malloc: Incorrect checksum for freed object 0x7fcc1ec02b98: probably modified after being freed.

Corrupt value: 0x7ffffffe00000000

test(44854,0x10a92f5c0) malloc: *** set a breakpoint in malloc_error_break to debug

globalObject: C(id: ObjectIdentifier(0x00007f7f452006c0), s: abc)

test(61212,0x114d7e5c0) malloc: Incorrect checksum for freed object 0x7fe063e00008: probably modified after being freed.

Corrupt value: 0x4ffffffe00000000

test(61212,0x114d7e5c0) malloc: *** set a breakpoint in malloc_error_break to debug

Next run:

test(7016,0x10682d5c0) malloc: tiny_free_list_remove_ptr: Internal invariant broken (next ptr of prev): ptr=0x7fb2e0805fd0, prev_next=0x7f92e0805fd0

It seems to be some race condition, but I don't know who is spawning threads here. Btw:

swiftc -version
Apple Swift version 5.0.1 (swiftlang-1001.0.82.4 clang-1001.0.46.5)
Target: x86_64-apple-darwin18.7.0
Slabber answered 12/9, 2019 at 21:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.