Repository: apple/swift
File: stdlib/public/Concurrency/AsyncStream.swift (Swift)

Code before:

    /// Resume the task awaiting the next iteration point by having it return
    /// nil, which signifies the end of the iteration.
    ///
    /// Calling this function more than once has no effect. After calling
    /// finish, the stream enters a terminal state and doesn't produce any
    /// additional elements.
    public func finish() {
      storage.finish()
    }

    /// A callback to invoke when canceling iteration of an asynchronous
    /// stream.
    ///
    /// If an `onTermination` callback is set, using task cancellation to
    /// terminate iteration of an `AsyncStream` results in a call to this
    /// callback.
    ///
    /// Canceling an active iteration invokes the `onTermination` callback
    /// first, then resumes by yielding `nil`. This means that you can perform
    /// needed cleanup in the cancellation handler. After reaching a terminal
    /// state as a result of cancellation, the `AsyncStream` sets the callback
    /// to `nil`.
    ///
    ///  - Note: The `onTermination` callback may be invoked synchronously as part
    ///  of task cancellation. As such, it is subject to similar considerations for
    ///  avoiding deadlock as outlined in the documentation for
    ///  ``withTaskCancellationHandler(operation:onCancel:)``.
    public var onTermination: (@Sendable (Termination) -> Void)? {
      get {
        return storage.getOnTermination()
      }
      nonmutating set {
        storage.setOnTermination(newValue)
      }
    }
  }

  final class _Context {
    let storage: _Storage?
    let produce: () async -> Element?

    init(storage: _Storage? = nil, produce: @escaping () async -> Element?) {
      self.storage = storage
      self.produce = produce
    }

    deinit {
      storage?.cancel()
    }
  }

Review comment (security):
Let's avoid passive voice here, to clarify who calls the callback.  I think we mean:

```suggestion
    ///  - Note: Because the system might call the `onTermination` callback synchronously as part
    ///  of task cancellation, it's subject to similar considerations for
```

If the considerations are "similar" but not exactly the same, we should state those considerations.

Avoiding "may" because it can be ambiguous between possibility (something that may or may not happen) and permission (something that you may do).

If this is meant to be a "note" in the sense that some developers can ignore it, `- Note` is correct.  If it's meant to be a callout of important information, please use `- Important` instead.

Code after:

    /// Resume the task awaiting the next iteration point by having it return
    /// nil, which signifies the end of the iteration.
    ///
    /// Calling this function more than once has no effect. After calling
    /// finish, the stream enters a terminal state and doesn't produce any
    /// additional elements.
    public func finish() {
      storage.finish()
    }

    /// A callback to invoke when canceling iteration of an asynchronous
    /// stream.
    ///
    /// If an `onTermination` callback is set, using task cancellation to
    /// terminate iteration of an `AsyncStream` results in a call to this
    /// callback.
    ///
    /// Canceling an active iteration invokes the `onTermination` callback
    /// first, then resumes by yielding `nil`. This means that you can perform
    /// needed cleanup in the cancellation handler. After reaching a terminal
    /// state as a result of cancellation, the `AsyncStream` sets the callback
    /// to `nil`.
    ///
    /// - Note: Because the system might call the `onTermination` callback as
    /// part of task cancellation, it's subject to the same considerations for
    /// avoiding deadlock as outlined in the documentation for
    /// ``withTaskCancellationHandler(operation:onCancel:)``.
    public var onTermination: (@Sendable (Termination) -> Void)? {
      get {
        return storage.getOnTermination()
      }
      nonmutating set {
        storage.setOnTermination(newValue)
      }
    }
  }

  final class _Context {
    let storage: _Storage?
    let produce: () async -> Element?

    init(storage: _Storage? = nil, produce: @escaping () async -> Element?) {
      self.storage = storage
      self.produce = produce
    }

    deinit {
      storage?.cancel()
    }
  }