swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.6k stars 10.37k forks source link

[SR-14810] Concurrency Interoperability with Objective-C: Swift runtime crashes after calling Objective-C async function which calls completion handler immediately #57158

Closed swift-ci closed 2 months ago

swift-ci commented 3 years ago
Previous ID SR-14810
Radar rdar://problem/79632420
Original Reporter SvyatoslavScherbina (JIRA User)
Type Bug
Environment Xcode 13.0 beta (13A5154h)
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, Concurrency | |Assignee | None | |Priority | Medium | md5: 014d4f80d630d5722a90259e25948a24

Issue Description:

Steps to reproduce:

1. Create new "iOS App" project in Xcode (set "Interface" to "Storyboard", "Language" to "Swift")

2. Add Objective-C files:

Select main group in the project structure. Go to menu File -> New -> File... -> "Cocoa Touch Class". Set "Language" to "Objective-C", name the class "TestAsync", press Next, agree to create bridging header.

3. Add #import "TestAsync.h" to the bridging header.

4. Add the following method to TestAsync implementation:

- (void)testWithCompletionHandler:(void(^)(void))completionHandler {
    completionHandler();
}

Add the corresponding method declaration to TestAsync.h as well.

5. Add the following code to the end of ViewController.viewDidLoad:

async {
    await TestAsync().test()
    print("OK")
}

6. Run the app on the simulator.

The app prints "OK" and crashes with:

* thread #​1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #&#8203;0: 0x00007fff3070c1be libswiftCore.dylib`bool swift::HeapObjectSideTableEntry::decrementStrong<(swift::PerformDeinit)1>(unsigned int) + 14
    frame #&#8203;1: 0x000000010a9b598c TestAsyncIos3`closure #&#8203;1 in ViewController.viewDidLoad() at ViewController.swift:19:9
  * frame #&#8203;2: 0x000000010a9b5c60 TestAsyncIos3`partial apply for thunk for @escaping @callee_guaranteed @Sendable @async () -> () at <compiler-generated>:0
    frame #&#8203;3: 0x00007fff6a5284a0 libswift_Concurrency.dylib`(1) await resume partial function for reabstraction thunk helper <τ_0_0> from @escaping @callee_guaranteed @Sendable @async () -> (@out τ_0_0) to @escaping @callee_guaranteed @async () -> (@out τ_0_0, @error @owned Swift.Error)
    frame #&#8203;4: 0x00007fff6a534d50 libswift_Concurrency.dylib`reabstraction thunk helper <τ_0_0> from @escaping @callee_guaranteed @Sendable @async () -> (@out τ_0_0) to @escaping @callee_guaranteed @async () -> (@out τ_0_0, @error @owned Swift.Error)partial apply forwarder with unmangled suffix ".127TQ0_"

If I define testWithCompletionHandler as

- (void)testWithCompletionHandler:(void(^)(void))completionHandler {
    dispatch_async(dispatch_get_main_queue(), ^{
        completionHandler();
    });
}

then the code works without crashes and prints "OK".

swift-ci commented 3 years ago

Comment by Svyatoslav Scherbina (JIRA)

(Just in case) more "real-world" reproducer, that relies on "standard" frameworks instead of specific Objective-C code in the project:

import Contacts

// ...

async {
    try await CNContactStore().requestAccess(for: .contacts)
    print("OK")
}

Add NSContactsUsageDescription to Info.plist and run this code twice.

For the first time, it will request the access (doesn't matter if you grant it or not).

For the second time it seems to immediately call the completion handler, causing the crash.

typesanitizer commented 3 years ago

@swift-ci create

alobaili commented 3 months ago

By using Task instead of async in the OP steps to reproduce code and in Svyatoslav Scherbina's comment, I am unable to reproduce the issue in Xcode 15.4 (Swift 5.10). I think this issue has long been fixed and it should be closed.

hborla commented 2 months ago

Thank you @alobaili! I also cannot reproduce the issue with Swift 6.0.