What's New / App Services, Foundation & Diagnostics

What's new in _Concurrency

+7 NewiOS · macOS · tvOS · watchOS · visionOS

_Concurrency is the Swift standard library module for structured concurrency: async/await, tasks, task groups, and the cancellation machinery that coordinates concurrent work.

7 new APIs, no deprecations or removals. Tasks get a readable identity via Task.name and UnsafeCurrentTask.name. A cancellation-shield mechanism adds withTaskCancellationShield, with Task.hasActiveCancellationShield and UnsafeCurrentTask.hasActiveCancellationShield to query it. The release also adds an UnownedTaskExecutor type.

New

7
extension

UnownedTaskExecutor

NewiOSmacOStvOSvisionOSwatchOS
extension UnownedTaskExecutor : Hashable
Declaration
extension UnownedTaskExecutor : Hashable {

    /// The hash value.
    ///
    /// Hash values are not guaranteed to be equal across different executions of
    /// your program. Do not save hash values to use during a future execution.
    ///
    /// - Important: `hashValue` is deprecated as a `Hashable` requirement. To
    ///   conform to `Hashable`, implement the `hash(into:)` requirement instead.
    ///   The compiler provides an implementation for `hashValue` for you.
    public var hashValue: Int { get }
}
func

withTaskCancellationShield

NewiOSmacOStvOSvisionOSwatchOS
nonisolated(nonsending) public func withTaskCancellationShield<Value, Failure>(operation: nonisolated(nonsending) () async throws(Failure) -> Value) async throws(Failure) -> Value where Failure : Error

Enters a scope in which a task cancellation shield is active.

Cancellation shields are primarily used to ensure some cleanup code will definitely run, even if the context in which the cleanup functions are called from is a cancelled task, and the functions may otherwise return early (due to observing the cancellation of the current task).

For example, a resource cleanup function might internally check for cancellation, which could cause it to skip important cleanup work:

let resource = await makeResource()
defer {
  await withTaskCancellationShield {
    await resource.finish() // runs to completion, even if task was cancelled earlier
  }
}

struct Resource {
  func finish() {
    guard !Task.isCancelled() else { return } // returns early if task was cancelled!
    // real work happens here
  }

While inside a cancellation shield, Task.isCancelled returns false and Task.checkCancellation() does not throw, even if the surrounding task has been cancelled. Similarly task cancellation handlers do not trigger while executing in a shielded block of code.

Once the shield scope exits, the task's actual cancellation status becomes observable again. Cancellation shields to not prevent the task from becoming cancelled, but only prevent observing the cancellation while executing inside a shielded scope.

Cancellation shields also prevent cancellation from propagating to child tasks created within the shielded scope:

let task = Task {
  withUnsafeCurrentTask { $0?.cancel() } // cancel the task

  await withTaskCancellationShield {
    // Child tasks created here do NOT observe the parent's cancellation
    // and therefore start as not cancelled. They can be individually cancelled though.
    await withTaskGroup(of: Void.self) { group in
      group.addTask {
        print(Task.isCancelled) // false
      }
      for await _ in group {}

      group.cancelAll() // explicitly cancelling the group does cancel child tasks of the group
      group.addTask {
        print(Task.isCancelled) // true
      }
    }
  }
}

Note that shielding the addTask call itself does not shield the child task:

await withTaskGroup(of: Void.self) { group in
  group.cancelAll()
  withTaskCancellationShield {
    group.addTask { print(Task.isCancelled) } // true - child IS cancelled
  }
  group.addTask {
    withTaskCancellationShield { print(Task.isCancelled) } // false - shielded inside child
  }
}
func

withTaskCancellationShield

NewiOSmacOStvOSvisionOSwatchOS
public func withTaskCancellationShield<Value, Failure>(operation: () throws(Failure) -> Value) throws(Failure) -> Value where Failure : Error

Enters a scope in which a task cancellation shield is active.

Cancellation shields are primarily used to ensure some cleanup code will definitely run, even if the context in which the cleanup functions are called from is a cancelled task, and the functions may otherwise return early (due to observing the cancellation of the current task).

For example, a resource cleanup function might internally check for cancellation, which could cause it to skip important cleanup work:

let resource = await makeResource()
defer {
  await withTaskCancellationShield {
    await resource.finish() // runs to completion, even if task was cancelled earlier
  }
}

struct Resource {
  func finish() {
    guard !Task.isCancelled() else { return } // returns early if task was cancelled!
    // real work happens here
  }

While inside a cancellation shield, Task.isCancelled returns false and Task.checkCancellation() does not throw, even if the surrounding task has been cancelled. Similarly task cancellation handlers do not trigger while executing in a shielded block of code.

Once the shield scope exits, the task's actual cancellation status becomes observable again. Cancellation shields to not prevent the task from becoming cancelled, but only prevent observing the cancellation while executing inside a shielded scope.

Cancellation shields also prevent cancellation from propagating to child tasks created within the shielded scope:

let task = Task {
  withUnsafeCurrentTask { $0?.cancel() } // cancel the task

  await withTaskCancellationShield {
    // Child tasks created here do NOT observe the parent's cancellation
    // and therefore start as not cancelled. They can be individually cancelled though.
    await withTaskGroup(of: Void.self) { group in
      group.addTask {
        print(Task.isCancelled) // false
      }
      for await _ in group {}

      group.cancelAll() // explicitly cancelling the group does cancel child tasks of the group
      group.addTask {
        print(Task.isCancelled) // true
      }
    }
  }
}

Note that shielding the addTask call itself does not shield the child task:

await withTaskGroup(of: Void.self) { group in
  group.cancelAll()
  withTaskCancellationShield {
    group.addTask { print(Task.isCancelled) } // true - child IS cancelled
  }
  group.addTask {
    withTaskCancellationShield { print(Task.isCancelled) } // false - shielded inside child
  }
}
var

Task.hasActiveCancellationShield

NewiOSmacOStvOSvisionOSwatchOS
public static var hasActiveCancellationShield: Bool { get }

Checks if the current task is executing in a scope with a task cancellation shield activated by the withTaskCancellationShield(operation:) function.

An active task cancellation shield prevents a task's ability to observe if it was cancelled, i.e. the isCancelled property will always return false when the task is executing with an active shield.

This property is primarily aimed at debugging and understanding cancellation behavior in complex call hierarchies, and should not be used in regular control flow.

Returns true when executing within a task that has an active cancellation shield.

Cancellation shields are not automatically inherited by child tasks; each child task must install its own shield if needed if it, independently, wanted to ignore cancellation during a specific scope.

  • SeeAlso: withTaskCancellationShield(operation:)
  • SeeAlso: hasActiveCancellationShield
var

Task.name

NewiOSmacOStvOSvisionOSwatchOS
public var name: String? { get }

Return the task's name, if it was set during its creation.

var

UnsafeCurrentTask.hasActiveCancellationShield

NewiOSmacOStvOSvisionOSwatchOS
public var hasActiveCancellationShield: Bool { get }

Checks if this task is executing in a scope with a task cancellation shield activated by the withTaskCancellationShield(operation:) function.

An active task cancellation shield prevents a task's ability to observe if it was cancelled, i.e. the isCancelled property will always return false when the task is executing with an active shield.

This property is primarily aimed at debugging and understanding cancellation behavior in complex call hierarchies, and should not be used in regular control flow.

Returns true when executing within a task that has an active cancellation shield.

Cancellation shields are not automatically inherited by child tasks; each child task must install its own shield if needed if it, independently, wanted to ignore cancellation during a specific scope.

  • SeeAlso: withTaskCancellationShield(operation:)
  • SeeAlso: hasActiveCancellationShield
var

UnsafeCurrentTask.name

NewiOSmacOStvOSvisionOSwatchOS
public var name: String? { get }

Return the task's name, if it was set during its creation.

No APIs match your filter.

← More in App Services, Foundation & Diagnostics