Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-8206] Block capturing unowned self crash when using it #50738

Closed
ainopara opened this issue Jul 8, 2018 · 7 comments
Closed

[SR-8206] Block capturing unowned self crash when using it #50738

ainopara opened this issue Jul 8, 2018 · 7 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software run-time crash Bug → crash: Swift code crashed during execution

Comments

@ainopara
Copy link
Contributor

ainopara commented Jul 8, 2018

Previous ID SR-8206
Radar None
Original Reporter @ainopara
Type Bug
Status Closed
Resolution Invalid
Environment

Xcode Version 10.0 beta 3 (10L201y)

`swift --version`:

Apple Swift version 4.2 (swiftlang-1000.0.25.1 clang-1000.10.28.1)
Target: x86_64-apple-darwin18.0.0
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, RunTimeCrash
Assignee None
Priority Medium

md5: 22dd0632ca3ec2b23ddb3f869cc791c0

Issue Description:

testCrash.swift

import Foundation

public typealias RenderFunction = (_ info: String) throws -> String
class MustacheBox: NSObject {
    var render: RenderFunction
    
    init(message: String) {
        self.render = { (_) in return "" }
        super.init()
        self.render = { [unowned self] (info: String) in
            print(message)
            print(self)
            return info
        }
    }
}

let box = MustacheBox(message: "1")
let result1 = try box.render("2")
print("This works as expected.")

let result2 = try MustacheBox(message: "1").render("2")
print("This will crash on swift 4.2.")

Output of `swift testCrash.swift`:

1
<testCrash.MustacheBox: 0x7f7f2aacadd0>
This works as expected.
1
Fatal error: Attempted to read an unowned reference but the object was already deallocated0 swift 0x000000010fd5c4aa PrintStackTraceSignalHandler(void*) + 42
1 swift 0x000000010fd5b8b6 SignalHandler(int) + 966
2 libsystem_platform.dylib 0x00007fff5dc24d3a _sigtramp + 26
3 libsystem_platform.dylib 0x00000001209886d7 _sigtramp + 3268819383
4 libsystem_c.dylib 0x00007fff5dae6151 abort + 127
5 libswiftCore.dylib 0x0000000115d03c65 swift::fatalError(unsigned int, char const*, ...) + 149
6 libswiftCore.dylib 0x0000000115d03e1f swift::swift_abortRetainUnowned(void const*) + 31
7 libswiftCore.dylib 0x0000000115d43b86 swift_unknownUnownedLoadStrong + 70
8 libswiftCore.dylib 0x00000001136d1928 swift_unknownUnownedLoadStrong + 4254653928
9 libswiftCore.dylib 0x00000001136d1c3c swift_unknownUnownedLoadStrong + 4254654716
10 libswiftCore.dylib 0x00000001136d13c1 swift_unknownUnownedLoadStrong + 4254652545
11 swift 0x000000010cdcd76d llvm::MCJIT::runFunction(llvm::Function*, llvm::ArrayRef<llvm::GenericValue>) + 365
12 swift 0x000000010cdd416c llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, char const* const*) + 1004
13 swift 0x000000010c039c33 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 52659
14 swift 0x000000010c029985 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 7733
15 swift 0x000000010bfd3028 main + 13144
16 libdyld.dylib 0x00007fff5da2a3ed start + 1
Stack dump:
0.  Program arguments: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -interpret testCrash.swift -enable-objc-interop -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -color-diagnostics -module-name testCrash
[1] 25834 abort swift testCrash.swift
@belkadan
Copy link
Contributor

belkadan commented Jul 9, 2018

@mikeash, is this familiar?

@mikeash
Copy link
Contributor

mikeash commented Jul 9, 2018

My understanding is that this is expected behavior. ObjC ARC will not release strong references until the end of the full statement, but Swift ARC can (intentionally) release strong references right after the last use. I suspect the first example is working because of a quirk of top-level declarations (maybe it's being treated like a global?) or just ARC optimization not happening.

You can use withExtendedLifetime to force the object to remain live for longer. In this example you might write something like:

let result2 = withExtendedLifetime(try MustacheBox(message: "1"), { $0.render("2") })

@belkadan
Copy link
Contributor

belkadan commented Jul 9, 2018

Oops, that's what I get for not reading the example carefully enough. Mike is correct.

@ainopara
Copy link
Contributor Author

Thank you for explanation. I agreed that this should be expected behavior.

The cause of misunderstanding this behavior is

MustacheBox(message: "1").render("2")

looks like "method calling on a newly created object". However it is actually "get the closure property of a newly created object and execute it". Calling a method instand works as expected.

I also tried to wrap test code in a function, It seems this behavior has nothing to do with top-level declaration. My understanding is that swift does not guarantee that it release unretained objet as soon as possible, so there is chance that swift will let an object live longer. But developers should not depending on this behavior because it may change over time.

import Foundation

public typealias RenderFunction = (_ info: String) throws -> String
class MustacheBox: NSObject {
    var render: RenderFunction
    
    init(message: String) {
        self.render = { (_) in return "" }
        super.init()
        self.render = { [unowned self] (info: String) in
            print(message)
            print(self)
            return info
        }
    }

    func render2(_ info: String) throws -> String {
        return try self.render(info)
    }
}

func run() throws {
    let box = MustacheBox(message: "1")
    let result0 = try box.render("2")
    print("This works as expected.")
    
    let result1 = try MustacheBox(message: "1").render2("2")
    print("This works as expected.")

    let result2 = try MustacheBox(message: "1").render("2")
    print("This will crash on swift 4.2.")
}

try run()

@mikeash
Copy link
Contributor

mikeash commented Jul 10, 2018

Yes, I don't think it's guaranteed that the release happens as soon as possible, it's just an option. If you want to experiment, you might also try enabling optimization to see if that changes the behavior.

@ainopara
Copy link
Contributor Author

Release build with `-O` gives the same result. Anyway, thank you for your kindly and timely reply.

(Reopen this issue in case others want to talk on it. Feel free to close this issue at any time. )

p.s. this reply is delayed because I don't know emoji is not supported by JIRA.

@ainopara
Copy link
Contributor Author

I thought closed JIRA issue could not be commented so I reopened the issue to enable further conversaion. Actually it could, so there is no reason to keep this open.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software run-time crash Bug → crash: Swift code crashed during execution
Projects
None yet
Development

No branches or pull requests

4 participants