Skip to content

🌸[6.0][Concurrency] Remove _unsafeInheritExecutor from public APIs, use #isolation #72830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 43 additions & 8 deletions stdlib/public/Concurrency/CheckedContinuation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -281,19 +281,37 @@ extension CheckedContinuation {
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
/// - SeeAlso: `withUnsafeContinuation(function:_:)`
/// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@inlinable
@_unavailableInEmbedded
@available(SwiftStdlib 5.1, *)
#if !$Embedded
@backDeployed(before: SwiftStdlib 6.0)
#endif
public func withCheckedContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Never>) -> Void
isolation: isolated (any Actor)? = #isolation,
function: String = #function,
_ body: (CheckedContinuation<T, Never>) -> Void
) async -> T {
return await withUnsafeContinuation {
body(CheckedContinuation(continuation: $0, function: function))
}
}

@available(SwiftStdlib 5.1, *)
@usableFromInline
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@_unavailableInEmbedded
@_silgen_name("$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalF")
internal func __abi_withCheckedContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Never>) -> Void
) async -> T {
return await withUnsafeContinuation {
body(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
Expand Down Expand Up @@ -322,13 +340,30 @@ public func withCheckedContinuation<T>(
/// - SeeAlso: `withCheckedContinuation(function:_:)`
/// - SeeAlso: `withUnsafeContinuation(function:_:)`
/// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@inlinable
@_unavailableInEmbedded
@available(SwiftStdlib 5.1, *)
#if !$Embedded
@backDeployed(before: SwiftStdlib 6.0)
#endif
public func withCheckedThrowingContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Error>) -> Void
isolation: isolated (any Actor)? = #isolation,
function: String = #function,
_ body: (CheckedContinuation<T, Error>) -> Void
) async throws -> T {
return try await withUnsafeThrowingContinuation {
body(CheckedContinuation(continuation: $0, function: function))
}
}

@available(SwiftStdlib 5.1, *)
@usableFromInline
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@_unavailableInEmbedded
@_silgen_name("$ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF")
internal func __abi_withCheckedThrowingContinuation<T>(
function: String = #function,
_ body: (CheckedContinuation<T, Error>) -> Void
) async throws -> T {
return try await withUnsafeThrowingContinuation {
body(CheckedContinuation(continuation: $0, function: function))
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/Concurrency/PartialAsyncTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -598,9 +598,9 @@ internal func _resumeUnsafeThrowingContinuationWithError<T>(
/// - SeeAlso: `withCheckedContinuation(function:_:)`
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor
@_alwaysEmitIntoClient
public func withUnsafeContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
_ fn: (UnsafeContinuation<T, Never>) -> Void
) async -> T {
return await Builtin.withUnsafeContinuation {
Expand Down Expand Up @@ -634,9 +634,9 @@ public func withUnsafeContinuation<T>(
/// - SeeAlso: `withCheckedContinuation(function:_:)`
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_unsafeInheritExecutor
@_alwaysEmitIntoClient
public func withUnsafeThrowingContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
_ fn: (UnsafeContinuation<T, Error>) -> Void
) async throws -> T {
return try await Builtin.withUnsafeThrowingContinuation {
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/Concurrency/Task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,9 @@ static SerialExecutorRef executorForEnqueuedJob(Job *job) {
return SerialExecutorRef::generic();
#else
void *jobQueue = job->SchedulerPrivate[Job::DispatchQueueIndex];
if (jobQueue == DISPATCH_QUEUE_GLOBAL_EXECUTOR)
if (jobQueue == DISPATCH_QUEUE_GLOBAL_EXECUTOR) {
return SerialExecutorRef::generic();
else
} else
return SerialExecutorRef::forOrdinary(reinterpret_cast<HeapObject*>(jobQueue),
_swift_task_getDispatchQueueSerialExecutorWitnessTable());
#endif
Expand Down
59 changes: 49 additions & 10 deletions stdlib/public/Concurrency/TaskGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public func withTaskGroup<ChildTaskResult, GroupResult>(
// Run the withTaskGroup body.
let result = await body(&group)

// TODO(concurrency): should get isolation from param from withThrowingTaskGroup
await group.awaitAllRemainingTasks()

Builtin.destroyTaskGroup(_group)
Expand Down Expand Up @@ -183,13 +184,15 @@ public func withThrowingTaskGroup<ChildTaskResult, GroupResult>(
// Run the withTaskGroup body.
let result = try await body(&group)

// TODO(concurrency): should get isolation from param from withThrowingTaskGroup
await group.awaitAllRemainingTasks()
Builtin.destroyTaskGroup(_group)

return result
} catch {
group.cancelAll()

// TODO(concurrency): should get isolation from param from withThrowingTaskGroup
await group.awaitAllRemainingTasks()
Builtin.destroyTaskGroup(_group)

Expand Down Expand Up @@ -563,22 +566,41 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
/// that method can't be called from a concurrent execution context like a child task.
///
/// - Returns: The value returned by the next child task that completes.
public mutating func next() async -> ChildTaskResult? {
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
public mutating func next(isolation: isolated (any Actor)? = #isolation) async -> ChildTaskResult? {
// try!-safe because this function only exists for Failure == Never,
// and as such, it is impossible to spawn a throwing child task.
return try! await _taskGroupWaitNext(group: _group) // !-safe cannot throw, we're a non-throwing TaskGroup
}

@usableFromInline
@available(SwiftStdlib 5.1, *)
@_silgen_name("$sScG4nextxSgyYaF")
internal mutating func __abi_next() async -> ChildTaskResult? {
// try!-safe because this function only exists for Failure == Never,
// and as such, it is impossible to spawn a throwing child task.
return try! await _taskGroupWaitNext(group: _group) // !-safe cannot throw, we're a non-throwing TaskGroup
}

/// Await all of the pending tasks added this group.
@usableFromInline
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
internal mutating func awaitAllRemainingTasks(isolation: isolated (any Actor)? = #isolation) async {
while let _ = await next(isolation: isolation) {}
}

@usableFromInline
@available(SwiftStdlib 5.1, *)
internal mutating func awaitAllRemainingTasks() async {
while let _ = await next() {}
while let _ = await next(isolation: nil) {}
}

/// Wait for all of the group's remaining tasks to complete.
@_alwaysEmitIntoClient
public mutating func waitForAll() async {
await awaitAllRemainingTasks()
public mutating func waitForAll(isolation: isolated (any Actor)? = #isolation) async {
await awaitAllRemainingTasks(isolation: isolation)
}

/// A Boolean value that indicates whether the group has any remaining tasks.
Expand Down Expand Up @@ -703,16 +725,24 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {

/// Await all the remaining tasks on this group.
@usableFromInline
internal mutating func awaitAllRemainingTasks() async {
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
internal mutating func awaitAllRemainingTasks(isolation: isolated (any Actor)? = #isolation) async {
while true {
do {
guard let _ = try await next() else {
guard let _ = try await next(isolation: isolation) else {
return
}
} catch {}
}
}

@usableFromInline
@available(SwiftStdlib 5.1, *)
internal mutating func awaitAllRemainingTasks() async {
await awaitAllRemainingTasks(isolation: nil)
}

@usableFromInline
internal mutating func _waitForAll() async throws {
await self.awaitAllRemainingTasks()
Expand Down Expand Up @@ -750,7 +780,7 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
/// - Throws: The *first* error that was thrown by a child task during draining all the tasks.
/// This first error is stored until all other tasks have completed, and is re-thrown afterwards.
@_alwaysEmitIntoClient
public mutating func waitForAll() async throws {
public mutating func waitForAll(isolation: isolated (any Actor)? = #isolation) async throws {
var firstError: Error? = nil

// Make sure we loop until all child tasks have completed
Expand Down Expand Up @@ -999,7 +1029,16 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
/// - Throws: The error thrown by the next child task that completes.
///
/// - SeeAlso: `nextResult()`
public mutating func next() async throws -> ChildTaskResult? {
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
public mutating func next(isolation: isolated (any Actor)? = #isolation) async throws -> ChildTaskResult? {
return try await _taskGroupWaitNext(group: _group)
}

@usableFromInline
@available(SwiftStdlib 5.1, *)
@_silgen_name("$sScg4nextxSgyYaKF")
internal mutating func __abi_next() async throws -> ChildTaskResult? {
return try await _taskGroupWaitNext(group: _group)
}

Expand Down Expand Up @@ -1052,7 +1091,7 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
///
/// - SeeAlso: `next()`
@_alwaysEmitIntoClient
public mutating func nextResult() async -> Result<ChildTaskResult, Failure>? {
public mutating func nextResult(isolation: isolated (any Actor)? = #isolation) async -> Result<ChildTaskResult, Failure>? {
return try! await nextResultForABI()
}

Expand Down Expand Up @@ -1332,7 +1371,7 @@ func _taskGroupIsCancelled(group: Builtin.RawPointer) -> Bool

@available(SwiftStdlib 5.1, *)
@_silgen_name("swift_taskGroup_wait_next_throwing")
func _taskGroupWaitNext<T>(group: Builtin.RawPointer) async throws -> T?
public func _taskGroupWaitNext<T>(group: Builtin.RawPointer) async throws -> T?

@available(SwiftStdlib 5.1, *)
@_silgen_name("swift_task_hasTaskGroupStatusRecord")
Expand Down
45 changes: 41 additions & 4 deletions stdlib/public/Concurrency/TaskLocal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,27 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
/// the operation closure.
@inlinable
@discardableResult
@_unsafeInheritExecutor
@backDeployed(before: SwiftStdlib 5.8)
public func withValue<R>(_ valueDuringOperation: Value, operation: () async throws -> R,
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
public func withValue<R>(_ valueDuringOperation: Value,
operation: () async throws -> R,
isolation: isolated (any Actor)? = #isolation,
file: String = #fileID, line: UInt = #line) async rethrows -> R {
return try await withValueImpl(
valueDuringOperation,
operation: operation,
isolation: isolation,
file: file, line: line)
}

@usableFromInline
@discardableResult
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
@available(SwiftStdlib 5.1, *)
@_silgen_name("$ss9TaskLocalC9withValue_9operation4file4lineqd__x_qd__yYaKXESSSutYaKlF")
internal func __abi_withValue<R>(_ valueDuringOperation: Value,
operation: () async throws -> R,
file: String = #fileID, line: UInt = #line) async rethrows -> R {
return try await withValueImpl(valueDuringOperation, operation: operation, file: file, line: line)
}

Expand All @@ -206,11 +223,30 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
/// to swift_task_de/alloc for the copy as follows:
/// - withValue contains the compiler-emitted calls swift_task_de/alloc.
/// - withValueImpl contains the calls to _taskLocalValuePush/Pop
@inlinable
@discardableResult
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 6.0)
internal func withValueImpl<R>(_ valueDuringOperation: __owned Value,
operation: () async throws -> R,
isolation: isolated (any Actor)?,
file: String = #fileID, line: UInt = #line) async rethrows -> R {
// check if we're not trying to bind a value from an illegal context; this may crash
_checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line)

_taskLocalValuePush(key: key, value: consume valueDuringOperation)
defer { _taskLocalValuePop() }

return try await operation()
}

@inlinable
@discardableResult
@_unsafeInheritExecutor
@available(SwiftStdlib 5.1, *)
@backDeployed(before: SwiftStdlib 5.9)
internal func withValueImpl<R>(_ valueDuringOperation: __owned Value, operation: () async throws -> R,
internal func withValueImpl<R>(_ valueDuringOperation: __owned Value,
operation: () async throws -> R,
file: String = #fileID, line: UInt = #line) async rethrows -> R {
// check if we're not trying to bind a value from an illegal context; this may crash
_checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line)
Expand All @@ -221,6 +257,7 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
return try await operation()
}


/// Binds the task-local to the specific value for the duration of the
/// synchronous operation.
///
Expand Down
2 changes: 1 addition & 1 deletion stdlib/toolchain/Compatibility56/Concurrency/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ExecutorTrackingInfo {

/// Unconditionally initialize a fresh tracking state on the
/// current state, shadowing any previous tracking state.
/// leave() must be called beforet the object goes out of scope.
/// leave() must be called before the object goes out of scope.
void enterAndShadow(ExecutorRef currentExecutor) {
ActiveExecutor = currentExecutor;
SavedInfo = ActiveInfoInThread.get();
Expand Down
Loading