Uploaded image for project: 'Swift'
  1. Swift
  2. SR-14841

Concurrency: Resuming a stored continuation from an actor does not work

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Reopened
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: None
    • Labels:
      None
    • 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).

      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)")
              }
          }
      }
      

       

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              Unassigned Unassigned
              Reporter:
              DrMickeyLauer Dr. Michael 'Mickey' Lauer
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

                Dates

                Created:
                Updated: