//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Swift

@available(SwiftStdlib 5.1, *)
@_silgen_name("swift_continuation_logFailedCheck")
internal func logFailedCheck(_ message: UnsafeRawPointer)

/// Implementation class that holds the `UnsafeContinuation` instance for
/// a `CheckedContinuation`.
@available(SwiftStdlib 5.1, *)
internal final class CheckedContinuationCanary: @unchecked Sendable {
  // The instance state is stored in tail-allocated raw memory, so that
  // we can atomically check the continuation state.

  private init() { fatalError("must use create") }

  private static func _create(continuation: UnsafeRawPointer, function: String)
      -> CheckedContinuationCanary {
    let instance = unsafe Builtin.allocWithTailElems_1(CheckedContinuationCanary.self,
      1._builtinWordValue,
      (UnsafeRawPointer?, String).self)

    unsafe instance._continuationPtr.initialize(to: continuation)
    unsafe instance._functionPtr.initialize(to: function)
    return instance
  }

  private var _continuationPtr: UnsafeMutablePointer<UnsafeRawPointer?> {
    return unsafe UnsafeMutablePointer<UnsafeRawPointer?>(
      Builtin.projectTailElems(self, (UnsafeRawPointer?, String).self))
  }
  private var _functionPtr: UnsafeMutablePointer<String> {
    let tailPtr = unsafe UnsafeMutableRawPointer(
      Builtin.projectTailElems(self, (UnsafeRawPointer?, String).self))

    let functionPtr = unsafe tailPtr 
        + MemoryLayout<(UnsafeRawPointer?, String)>.offset(of: \(UnsafeRawPointer?, String).1)!

    return unsafe functionPtr.assumingMemoryBound(to: String.self)
  }

  internal static func create<T, E>(continuation: UnsafeContinuation<T, E>,
                                 function: String) -> CheckedContinuationCanary {
    return unsafe _create(
        continuation: unsafeBitCast(continuation, to: UnsafeRawPointer.self),
        function: function)
  }

  internal var function: String {
    return unsafe _functionPtr.pointee
  }

  // Take the continuation away from the container, or return nil if it's
  // already been taken.
  internal func takeContinuation<T, E>() -> UnsafeContinuation<T, E>? {
    // Atomically exchange the current continuation value with a null pointer.
    let rawContinuationPtr = unsafe unsafeBitCast(_continuationPtr,
      to: Builtin.RawPointer.self)
    let rawOld = Builtin.atomicrmw_xchg_seqcst_Word(rawContinuationPtr,
      0._builtinWordValue)

    return unsafe unsafeBitCast(rawOld, to: UnsafeContinuation<T, E>?.self)
  }

  deinit {
    unsafe _functionPtr.deinitialize(count: 1)
    // Log if the continuation was never consumed before the instance was
    // destructed.
    if unsafe _continuationPtr.pointee != nil {
      #if !$Embedded
      unsafe logFailedCheck("SWIFT TASK CONTINUATION MISUSE: \(function) leaked its continuation without resuming it. This may cause tasks waiting on it to remain suspended forever.\n")
      #else
      fatalError("SWIFT TASK CONTINUATION MISUSE")
      #endif
    }
  }
}

/// A mechanism to interface
/// between synchronous and asynchronous code,
/// logging correctness violations.
///
/// A *continuation* is an opaque representation of program state.
/// To create a continuation in asynchronous code,
/// call the `withCheckedContinuation(isolation:function:_:)` or
/// `withCheckedThrowingContinuation(isolation:function:_:)` function.
/// To resume the asynchronous task,
/// call the `resume(returning:)`,
/// `resume(throwing:)`,
/// `resume(with:)`,
/// or `resume()` method.
///
/// - Important: You must call a resume method exactly once
///   on every execution path throughout the program.
///
/// Resuming from a continuation more than once is undefined behavior.
/// Never resuming leaves the task in a suspended state indefinitely,
/// and leaks any associated resources.
/// `CheckedContinuation` logs a message
/// if either of these invariants is violated.
///
/// `CheckedContinuation` performs runtime checks
/// for missing or multiple resume operations.
/// `UnsafeContinuation` avoids enforcing these invariants at runtime
/// because it aims to be a low-overhead mechanism
/// for interfacing Swift tasks with
/// event loops, delegate methods, callbacks,
/// and other non-`async` scheduling mechanisms.
/// However, during development, the ability to verify that the
/// invariants are being upheld in testing is important.
/// Because both types have the same interface,
/// you can replace one with the other in most circumstances,
/// without making other changes.
@available(SwiftStdlib 5.1, *)
public struct CheckedContinuation<T, E: Error>: Sendable {
  private let canary: CheckedContinuationCanary
  
  /// Creates a checked continuation from an unsafe continuation.
  ///
  /// Instead of calling this initializer,
  /// most code calls the `withCheckedContinuation(function:_:)` or
  /// `withCheckedThrowingContinuation(function:_:)` function instead.
  /// You only need to initialize
  /// your own `CheckedContinuation<T, E>` if you already have an
  /// `UnsafeContinuation` you want to impose checking on.
  ///
  /// - Parameters:
  ///   - continuation: An instance of `UnsafeContinuation`
  ///     that hasn't yet been resumed.
  ///     After passing the unsafe continuation to this initializer,
  ///     don't use it outside of this object.
  ///   - function: A string identifying the declaration that is the notional
  ///     source for the continuation, used to identify the continuation in
  ///     runtime diagnostics related to misuse of this continuation.
  public init(continuation: UnsafeContinuation<T, E>, function: String = #function) {
    canary = unsafe CheckedContinuationCanary.create(
      continuation: continuation,
      function: function)
  }
  
  /// Resume the task awaiting the continuation by having it return normally
  /// from its suspension point.
  ///
  /// - Parameter value: The value to return from the continuation.
  ///
  /// A continuation must be resumed exactly once. If the continuation has
  /// already been resumed through this object, then the attempt to resume
  /// the continuation will trap.
  ///
  /// After `resume` enqueues the task, control immediately returns to
  /// the caller. The task continues executing when its executor is
  /// able to reschedule it.
  public func resume(returning value: sending T) {
    if let c: UnsafeContinuation<T, E> = unsafe canary.takeContinuation() {
      unsafe c.resume(returning: value)
    } else {
      #if !$Embedded
      fatalError("SWIFT TASK CONTINUATION MISUSE: \(canary.function) tried to resume its continuation more than once, returning \(value)!\n")
      #else
      fatalError("SWIFT TASK CONTINUATION MISUSE")
      #endif
    }
  }
  
  /// Resume the task awaiting the continuation by having it throw an error
  /// from its suspension point.
  ///
  /// - Parameter error: The error to throw from the continuation.
  ///
  /// A continuation must be resumed exactly once. If the continuation has
  /// already been resumed through this object, then the attempt to resume
  /// the continuation will trap.
  ///
  /// After `resume` enqueues the task, control immediately returns to
  /// the caller. The task continues executing when its executor is
  /// able to reschedule it.
  public func resume(throwing error: __owned E) {
    if let c: UnsafeContinuation<T, E> = unsafe canary.takeContinuation() {
      unsafe c.resume(throwing: error)
    } else {
      #if !$Embedded
      fatalError("SWIFT TASK CONTINUATION MISUSE: \(canary.function) tried to resume its continuation more than once, throwing \(error)!\n")
      #else
      fatalError("SWIFT TASK CONTINUATION MISUSE")
      #endif
    }
  }
}

@available(SwiftStdlib 5.1, *)
extension CheckedContinuation {
  /// Resume the task awaiting the continuation by having it either
  /// return normally or throw an error based on the state of the given
  /// `Result` value.
  ///
  /// - Parameter result: A value to either return or throw from the
  ///   continuation.
  ///
  /// A continuation must be resumed exactly once. If the continuation has
  /// already been resumed through this object, then the attempt to resume
  /// the continuation will trap.
  ///
  /// After `resume` enqueues the task, control immediately returns to
  /// the caller. The task continues executing when its executor is
  /// able to reschedule it.
  @_alwaysEmitIntoClient
  public func resume<Er: Error>(with result: __shared sending Result<T, Er>) where E == Error {
    switch result {
      case .success(let val):
        self.resume(returning: val)
      case .failure(let err):
        self.resume(throwing: err)
    }
  }

  /// Resume the task awaiting the continuation by having it either
  /// return normally or throw an error based on the state of the given
  /// `Result` value.
  ///
  /// - Parameter result: A value to either return or throw from the
  ///   continuation.
  ///
  /// A continuation must be resumed exactly once. If the continuation has
  /// already been resumed through this object, then the attempt to resume
  /// the continuation will trap.
  ///
  /// After `resume` enqueues the task, control immediately returns to
  /// the caller. The task continues executing when its executor is
  /// able to reschedule it.
  @_alwaysEmitIntoClient
  public func resume(with result: __shared sending Result<T, E>) {
    switch result {
      case .success(let val):
        self.resume(returning: val)
      case .failure(let err):
        self.resume(throwing: err)
    }
  }

  /// Resume the task awaiting the continuation by having it return normally
  /// from its suspension point.
  ///
  /// A continuation must be resumed exactly once. If the continuation has
  /// already been resumed through this object, then the attempt to resume
  /// the continuation will trap.
  ///
  /// After `resume` enqueues the task, control immediately returns to
  /// the caller. The task continues executing when its executor is
  /// able to reschedule it.
  @_alwaysEmitIntoClient
  public func resume() where T == Void {
    self.resume(returning: ())
  }
}

/// Invokes the passed in closure with a checked continuation for the current task.
///
/// The body of the closure executes synchronously on the calling task, and once it returns
/// the calling task is suspended. It is possible to immediately resume the task, or escape the
/// continuation in order to complete it afterwards, which will then resume the suspended task.
///
/// You must invoke the continuation's `resume` method exactly once.
///
/// Missing to invoke it (eventually) will cause the calling task to remain suspended
/// indefinitely which will result in the task "hanging" as well as being leaked with
/// no possibility to destroy it.
///
/// The checked continuation offers detection of misuse, and dropping the last reference
/// to it, without having resumed it will trigger a warning. Resuming a continuation twice
/// is also diagnosed and will cause a crash.
///
/// - Parameters:
///   - function: A string identifying the declaration that is the notional
///     source for the continuation, used to identify the continuation in
///     runtime diagnostics related to misuse of this continuation.
///   - body: A closure that takes a `CheckedContinuation` parameter.
/// - Returns: The value continuation is resumed with.
///
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
/// - SeeAlso: `withUnsafeContinuation(function:_:)`
/// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)`
@inlinable
@_alwaysEmitIntoClient
@available(SwiftStdlib 5.1, *)
// ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy
// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit actor parameter
@abi(
  nonisolated(nonsending) func withCheckedContinuationNonisolatedNonsending<T>(
    function: String,
    _ body: (CheckedContinuation<T, Never>) -> Void
  ) async -> sending T
)
public nonisolated(nonsending) func withCheckedContinuation<T>(
  function: String = #function,
  _ body: (CheckedContinuation<T, Never>) -> Void
) async -> sending T {
  return await Builtin.withUnsafeContinuation {
    let unsafeContinuation = unsafe UnsafeContinuation<T, Never>($0)
    return body(unsafe CheckedContinuation(continuation: unsafeContinuation,
                                           function: function))
  }
}

/// Source-compatibility overload; replaced by
/// ``withCheckedContinuation(function:_:)``.
@inlinable
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
@_disfavoredOverload
@available(*, deprecated, message: "Replaced by nonisolated(nonsending) overload")
public func withCheckedContinuation<T>( // source-compatibility overload
  isolation: isolated (any Actor)?,
  function: String = #function,
  _ body: (CheckedContinuation<T, Never>) -> Void
) async -> sending T {
  return await Builtin.withUnsafeContinuation {
    let unsafeContinuation = unsafe UnsafeContinuation<T, Never>($0)
    return body(unsafe CheckedContinuation(continuation: unsafeContinuation,
                                           function: function))
  }
}

// Note: hack to stage out @_unsafeInheritExecutor forms of various functions
// in favor of #isolation. The _unsafeInheritExecutor_ prefix is meaningful
// to the type checker.
//
// This function also doubles as an ABI-compatibility shim predating the
// introduction of #isolation.
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@_silgen_name("$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalF")
public func _unsafeInheritExecutor_withCheckedContinuation<T>(
  function: String = #function,
  _ body: (CheckedContinuation<T, Never>) -> Void
) async -> T {
  return await unsafe withUnsafeContinuation {
    body(unsafe CheckedContinuation(continuation: $0, function: function))
  }
}


/// Invokes the passed in closure with a checked continuation for the current task.
///
/// The body of the closure executes synchronously on the calling task, and once it returns
/// the calling task is suspended. It is possible to immediately resume the task, or escape the
/// continuation in order to complete it afterwards, which will then resume the suspended task.
///
/// If `resume(throwing:)` is called on the continuation, this function throws that error.
///
/// You must invoke the continuation's `resume` method exactly once.
///
/// Missing to invoke it (eventually) will cause the calling task to remain suspended
/// indefinitely which will result in the task "hanging" as well as being leaked with
/// no possibility to destroy it.
///
/// The checked continuation offers detection of misuse, and dropping the last reference
/// to it, without having resumed it will trigger a warning. Resuming a continuation twice
/// is also diagnosed and will cause a crash.
///
/// - Parameters:
///   - function: A string identifying the declaration that is the notional
///     source for the continuation, used to identify the continuation in
///     runtime diagnostics related to misuse of this continuation.
///   - body: A closure that takes a `CheckedContinuation` parameter.
/// - Returns: The value continuation is resumed with.
///
/// - SeeAlso: `withCheckedContinuation(function:_:)`
/// - SeeAlso: `withUnsafeContinuation(function:_:)`
/// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)`
@inlinable
@_alwaysEmitIntoClient
@available(SwiftStdlib 5.1, *)
// ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy
// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit actor parameter
@abi(
  nonisolated(nonsending) func withCheckedThrowingContinuationNonisolatedNonsending<T, E>(
    function: String,
    _ body: (CheckedContinuation<T, E>) -> Void
  ) async throws(E) -> sending T
)
public nonisolated(nonsending) func withCheckedThrowingContinuation<T, E>(
  function: String = #function,
  _ body: (CheckedContinuation<T, E>) -> Void
) async throws(E) -> sending T {
  do {
    return try await Builtin.withUnsafeThrowingContinuation {
      let unsafeContinuation = unsafe UnsafeContinuation<T, E>($0)
      return body(unsafe CheckedContinuation(continuation: unsafeContinuation,
                                             function: function))
    }
  } catch {
    throw error as! E
  }
}

@inlinable
@_alwaysEmitIntoClient
@available(SwiftStdlib 5.1, *)
// ABI Note: We need to use @abi here because the ABI of this function otherwise conflicts with the legacy
// @_unsafeInheritExecutor declaration, as none of them have (or mangle) the implicit actor parameter
@abi(
  nonisolated(nonsending) func withCheckedThrowingContinuationNonisolatedNonsending<T>(
    function: String,
    _ body: (CheckedContinuation<T, Error>) -> Void
  ) async throws(Error) -> sending T
)
public nonisolated(nonsending) func withCheckedThrowingContinuation<T>( // nonsending + untyped throws
  function: String = #function,
  _ body: (CheckedContinuation<T, Error>) -> Void
) async throws(Error) -> sending T {
  return try await Builtin.withUnsafeThrowingContinuation {
    let unsafeContinuation = unsafe UnsafeContinuation<T, Error>($0)
    return body(unsafe CheckedContinuation(continuation: unsafeContinuation,
                                           function: function))
  }
}

/// Source-compatibility overload; replaced by
/// ``withCheckedThrowingContinuation(function:_:)``.
@inlinable
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
@_disfavoredOverload
@available(*, deprecated, message: "Replaced by nonisolated(nonsending) overload")
public func withCheckedThrowingContinuation<T>(
  isolation: isolated (any Actor)?,
  function: String = #function,
  _ body: (CheckedContinuation<T, Error>) -> Void
) async throws -> sending T {
  return try await Builtin.withUnsafeThrowingContinuation {
    let unsafeContinuation = unsafe UnsafeContinuation<T, Error>($0)
    return body(unsafe CheckedContinuation(continuation: unsafeContinuation,
                                           function: function))
  }
}

// Note: hack to stage out @_unsafeInheritExecutor forms of various functions
// in favor of #isolation. The _unsafeInheritExecutor_ prefix is meaningful
// to the type checker.
//
// This function also doubles as an ABI-compatibility shim predating the
// introduction of #isolation.
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@_silgen_name("$ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF")
public func _unsafeInheritExecutor_withCheckedThrowingContinuation<T>(
  function: String = #function,
  _ body: (CheckedContinuation<T, Error>) -> Void
) async throws -> T {
  return try await unsafe withUnsafeThrowingContinuation {
    body(unsafe CheckedContinuation(continuation: $0, function: function))
  }
}

#if _runtime(_ObjC)

// Intrinsics used by SILGen to create, resume, or fail checked continuations.
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _createCheckedContinuation<T>(
  _ continuation: __owned UnsafeContinuation<T, Never>
) -> CheckedContinuation<T, Never> {
  return unsafe CheckedContinuation(continuation: continuation)
}

@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _createCheckedThrowingContinuation<T>(
  _ continuation: __owned UnsafeContinuation<T, Error>
) -> CheckedContinuation<T, Error> {
  return unsafe CheckedContinuation(continuation: continuation)
}

@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _resumeCheckedContinuation<T>(
  _ continuation: CheckedContinuation<T, Never>,
  _ value: sending T
) {
  continuation.resume(returning: value)
}

@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _resumeCheckedThrowingContinuation<T>(
  _ continuation: CheckedContinuation<T, Error>,
  _ value: sending T
) {
  continuation.resume(returning: value)
}

@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _resumeCheckedThrowingContinuationWithError<T>(
  _ continuation: CheckedContinuation<T, Error>,
  _ error: consuming Error
) {
  continuation.resume(throwing: error)
}

#endif
