Task "factory" functions, proper implementation?

TL;DR: Checking if it's ok to use @_inheritActorContext & @_implicitSelfCapture on a Task factory method.

We have started using Swift Concurrency heavily in our SDK and when integrating it with application code, rightfully, a good number of Tasks are created to call SDK methods. The SDK surfaces errors as much as possible to allow consuming code to handle them as they see fit and provide as much information as possible to the caller.

To that end we've created some Task factory methods that allow calling throwing functions and logs any errors thrown when used in a "terminal" context (e.g. in UI code). The factory methods simply create a Task with a do/catch that calls the passed in closure and log any thrown errors.

To gain the advantages of creating a Task "in context" we copied Tasks init method and added attributes @_inheritActorContext and @_implicitSelfCapture to our passed in operation closure.

We found that without these (especially @_inheritActorContext) that we are required to await methods that should not require it; e.g. calling @MainActor isolated functions from the @MainActor context; which seemingly is exactly what this @_inheritActorContext attribute is for.

I am just looking to make sure this is a valid thing to do in current and future Swift releases and learn of any pitfalls using them might create. They are currently working as expected but Concurrency is complicated and I am looking to be sure this is fundamentally correct code/usage.

Here is one of our implementation for examination...

@discardableResult
func spawn<TaskResult>(
  name: String = "Task",
  logger: Logger = taskLogger,
  type: OSLogType = .error,
  privacy: OSLogPrivacy = .private,
  @_inheritActorContext @_implicitSelfCapture operation: @escaping @Sendable () async throws -> TaskResult
) -> Task<TaskResult?, Never> {
  return Task {
    do {
      return try await operation()
    }
    catch {
      logger.error("\(name, privacy: .public) failed: error=\(error, privacy: privacy)")
      return nil
    }
  }
}
1 Like

This is also something I would love to know, it would make these factories/Task wrappers much more convenient to use, but I've not really seen @_inheritActorContext @_implicitSelfCapture in the wild yet.

I guess one risk is that underscored attributes are not technically stable and it is actively discouraged to use them (though I've seen many repositories use them anyways, like @_spi).

Edit: I found this quote about @_implicitSelfCapture in the review of SE-0304

Not sure if there's been more discussion on this.

1 Like