Skip to content

[Runtime] Resume to the generic executor in the absence of an override. #38600

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

Merged

Conversation

varungandhi-apple
Copy link
Contributor

Without this fix, we saw a crash involving URLSession where we'd hit
assertions or corruption in the runtime in various forms:

  • Assertion on trying to enqueue work onto a zombie actor.
  • job->SchedulerPrivate being overwritten (crash in getNextJobInQueue).
  • *(job->SchedulerPrivate) being overwritten (crash in getAsPreprocessedJob
    right after call to getNextJobInQueue).

I haven't been able to create a minimal crashing example yet, but manual
testing shows this does fix the issue.

Fixes rdar://79859254.

Without this fix, we saw a crash involving URLSession where we'd hit
assertions or corruption in the runtime in various forms:
- Assertion on trying to enqueue work onto a zombie actor.
- job->SchedulerPrivate being overwritten (crash in getNextJobInQueue).
- *(job->SchedulerPrivate) being overwritten (crash in getAsPreprocessedJob
  right after call to getNextJobInQueue).

I haven't been able to create a minimal crashing example yet, but manual
testing shows this does fix the issue.

Fixes rdar://79859254.
@varungandhi-apple varungandhi-apple changed the title Resume to the generic executor in the absence of an override. [Runtime] Resume to the generic executor in the absence of an override. Jul 23, 2021
@varungandhi-apple
Copy link
Contributor Author

@swift-ci smoke test

@varungandhi-apple
Copy link
Contributor Author

Code snippet which I tried to use:

// RUN: %target-swift-frontend -target x86_64-apple-macos12.0 -emit-sil -enable-experimental-concurrency -parse-as-library %s > ~/tmp/tmp.sil
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xfrontend -parse-as-library -Xfrontend -disable-availability-checking %s -o %t_binary
// RUN: %{python} %S/../Inputs/timeout.py 2.0 %target-run %t_binary | tee ~/tmp/tmp.stdout | %FileCheck %s
// RUN: DYLD_PRINT_LIBRARIES=1 %target-run %t_binary | tee ~/tmp/tmp.stdout

// REQUIRES: concurrency
// REQUIRES: executable_test
// REQUIRES: OS=macosx || OS=ios

// rdar://76038845
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime

// CHECK: Returning from withCheckedContinuation.
// CHECK: Actor deinit.
// CHECK: Returned from A.f().

typealias Cont = CheckedContinuation<Void, Never>

actor A {
    public var cont: Cont? = nil
    init() {}
    func f() async {
        await withCheckedContinuation { (c: Cont) -> Void in
          print("Returning from withCheckedContinuation.")
          self.cont = c
        }
    }
    deinit {
        print("Actor deinit.")
    }
}

@main
struct M {
    static func main() async {
        let cont: Cont?
        do {
          let a = A()
          Task {
            await a.f()
            print("Returned from A.f().")
          }
          let t: Task<Cont?, Never> = Task.detached { 
              try? await Task.sleep(nanoseconds: 10_000_000)
              return await a.cont
          }
          cont = await t.value
        }
        cont!.resume()
        try? await Task.sleep(nanoseconds: 10_000_000)
    }
}

I was hoping that the actor would be de-inited before the continuation is resumed, leading to the continuation trying to resume on the zombie/deinited actor, leading to a crash in the absence of this fix. However, there is a retain/release pair in func f(), and the release only happens after withCheckedContinuation is done. So the actor ends up being de-inited last, and everything works fine, instead of crashing before this change.

As an alternative approach, I considered generating the SIL and tweaking it to do what I wanted. To first establish a baseline, I tried compiling the SIL for the example above to executable code and then running it. However, that segfaults for me instead of printing various strings (i.e. Swift -> executable works OK, SIL -> executable doesn't). I have not had time to look at why that's the case (since landing this patch is somewhat pressing), but that reduced my confidence in being able to write some SIL code and having that compile correctly all the way to executable code.

@DougGregor DougGregor merged commit 8180c26 into swiftlang:main Jul 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants