Skip to content

[SR-14841] Concurrency: Resuming a stored continuation from an actor does not work #57188

@mickeyl

Description

@mickeyl
Previous ID SR-14841
Radar rdar://problem/79926026
Original Reporter @mickeyl
Type Bug
Status Closed
Resolution Done
Environment
  • Xcode 13 beta 4, iOS 15 simulator on macOS 11.4 on Mac6,1 (2013).

  • Xcode 13 beta 4, iOS 15 simulator on macOS 12b4 on M1-Mac (2020).

  • Xcode 13 beta 4, macOS/Catalyst on macOS 12b4 on M1-Mac (2020).

Additional Detail from JIRA
Votes 0
Component/s
Labels Bug
Assignee None
Priority Medium

md5: 42907e573028b4f88cb3e358fca70d55

relates to:

  • SR-14875 Resuming continuations from actor contexts can hang

Issue Description:

Please consider the following example program which seems to expose an incompatibility when storing a continuation somewhere and resuming it later from within an actor.

  • Changing the `actor` to a `class` makes the program work.
import Foundation
typealias Continuation = CheckedContinuation<String, Error>

public enum StreamError: Error {
    case invalidEncoding
}

public class StreamCommand {
    let continuation: Continuation

    init(continuation: Continuation) {
        self.continuation = continuation
    }

    func resumeContinuation() {
        let response = "fooBar"
        print("<triggering continuation resume> \(self.continuation)")
        self.continuation.resume(returning: response)
        print("</triggering continuation resume>")
    }
}

public actor StreamCommandQueue: NSObject {
    var activeCommand: StreamCommand?

    func send(string: String, timeout: TimeInterval) async throws -> String {
        print("awaiting...")
        let response: String = try await withCheckedThrowingContinuation { continuation in
            print("continuation: \(continuation)")
            self.activeCommand = StreamCommand(continuation: continuation)
            self.outputActiveCommand()
        }
        print("came back after awaiting")
        return response
    }

    func outputActiveCommand() {
        async {
            self.activeCommand?.resumeContinuation()
        }
    }

    func inputActiveCommand() { }
}

func doIt() {
    async {
        let streamQueue = StreamCommandQueue()
        do {
            let identification = try await streamQueue.send(string: "ATI\r", timeout: 1)
            print("identification: \(identification)")
        } catch {
            print("can't get identification: \(error)")
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions