Files
swift-mirror/stdlib/public/Concurrency/Task+immediate.swift.gyb
Pavel Yaskevich 0598b8c9f4 [stdlib] Task.immediate annotate operation parameter with @_implicitSelfCapture
Since the "operation" inherits the context of the actor it should
also be possible to reference its members without explicit use of
`self.`.
2025-05-19 18:01:23 -07:00

284 lines
9.4 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020-2025 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
@_implementationOnly import SwiftConcurrencyInternalShims
// ==== Task.immediate ---------------------------------------------------------
% METHOD_VARIANTS = [
% 'THROWING',
% 'NON_THROWING',
% ]
% OPERATION_PARAM = '@_inheritActorContext @_implicitSelfCapture _ operation: __owned @Sendable @escaping () async throws -> Success'
% for METHOD_VARIANT in METHOD_VARIANTS:
% IS_THROWING = METHOD_VARIANT == 'THROWING'
% if IS_THROWING:
% FAILURE_TYPE = 'Error'
% else:
% FAILURE_TYPE = 'Never'
% end
@available(SwiftStdlib 6.2, *)
extension Task where Failure == ${FAILURE_TYPE} {
@available(SwiftStdlib 6.2, *)
@available(*, deprecated, renamed: "immediate")
@discardableResult
public static func startSynchronously(
name: String? = nil,
priority: TaskPriority? = nil,
@_implicitSelfCapture @_inheritActorContext(always) _ operation: sending @isolated(any) @escaping () async throws -> Success
) -> Task<Success, ${FAILURE_TYPE}> {
immediate(name: name, priority: priority, operation: operation)
}
/// Create and immediately start running a new task in the context of the calling thread/task.
///
/// This function _starts_ the created task on the calling context.
/// The task will continue executing on the caller's context until it suspends,
/// and after suspension will resume on the adequate executor. For a nonisolated
/// operation this means running on the global concurrent pool, and on an isolated
/// operation it means the appropriate executor of that isolation context.
///
/// As indicated by the lack of `async` on this method, this method does _not_
/// suspend, and instead takes over the calling task's (thread's) execution in
/// a synchronous manner.
///
/// Other than the execution semantics discussed above, the created task
/// is semantically equivalent to its basic version which can be
/// created using ``Task/init``.
///
/// - Parameters:
/// - name: The high-level human-readable name given for this task
/// - priority: The priority of the task.
/// Pass `nil` to use the ``Task/basePriority`` of the current task (if there is one).
/// - operation: the operation to be run immediately upon entering the task.
/// - Returns: A reference to the unstructured task which may be awaited on.
@available(SwiftStdlib 6.2, *)
@_alwaysEmitIntoClient
@discardableResult
public static func immediate(
name: String? = nil,
priority: TaskPriority? = nil,
% # NOTE: This closure cannot be 'sending' because we'll trigger ' pattern that the region based isolation checker does not understand how to check'
% # In this case: `func syncOnMyGlobalActor() { Task.immediate { @MyGlobalActor in } }`
@_implicitSelfCapture @_inheritActorContext(always) operation: sending @isolated(any) @escaping () async throws -> Success
) -> Task<Success, ${FAILURE_TYPE}> {
let builtinSerialExecutor =
unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
// Determine if we're switching isolation dynamically.
// If not, we can run the task synchronously and therefore MUST NOT "enqueue" it.
let flagsMustNotCrash: UInt64 = 0
let canRunSynchronously: Bool =
if let builtinSerialExecutor {
_taskIsCurrentExecutor(executor: builtinSerialExecutor, flags: flagsMustNotCrash)
} else {
true // if there is not target executor, we can run synchronously
}
let flags = taskCreateFlags(
priority: priority,
isChildTask: false,
copyTaskLocals: true,
inheritContext: true,
enqueueJob: !canRunSynchronously,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false,
isSynchronousStart: true
)
var task: Builtin.NativeObject?
#if $BuiltinCreateAsyncTaskName
if let name {
task =
unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in
Builtin.createTask(
flags: flags,
initialSerialExecutor: builtinSerialExecutor,
taskName: nameBytes.baseAddress!._rawValue,
operation: operation).0
}
}
#endif
if task == nil {
// either no task name was set, or names are unsupported
task = Builtin.createTask(
flags: flags,
operation: operation).0
}
if canRunSynchronously {
_startTaskImmediately(task!, targetExecutor: builtinSerialExecutor)
}
return Task<Success, ${FAILURE_TYPE}>(task!)
}
}
%end
%{
GROUP_AND_OP_INFO = [
(
'TaskGroup',
[
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'',
'ChildTaskResult'
),
(
'ThrowingTaskGroup',
[
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'throws ',
'ChildTaskResult'
),
(
'DiscardingTaskGroup',
[
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'',
'Void'
),
(
'ThrowingDiscardingTaskGroup',
[
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'throws ',
'Void'
),
]
}%
% for (GROUP_TYPE, METHOD_NAMES, THROWS, RESULT_TYPE) in GROUP_AND_OP_INFO:
% for METHOD_NAME in METHOD_NAMES:
@available(SwiftStdlib 6.2, *)
extension ${GROUP_TYPE} {
/// Create and immediately start running a new child task in the context of the calling thread/task.
///
/// This function _starts_ the created task on the calling context.
/// The task will continue executing on the caller's context until it suspends,
/// and after suspension will resume on the adequate executor. For a nonisolated
/// operation this means running on the global concurrent pool, and on an isolated
/// operation it means the appropriate executor of that isolation context.
///
/// As indicated by the lack of `async` on this method, this method does _not_
/// suspend, and instead takes over the calling task's (thread's) execution in
/// a synchronous manner.
///
/// Other than the execution semantics discussed above, the created task
/// is semantically equivalent to its basic version which can be
/// created using ``${GROUP_TYPE}/addTask``.
@available(SwiftStdlib 6.2, *)
@_alwaysEmitIntoClient
public func ${METHOD_NAME}( // in ${GROUP_TYPE}
name: String? = nil,
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: sending @isolated(any) @escaping () async ${THROWS}-> ${RESULT_TYPE}
) {
let flags = taskCreateFlags(
priority: priority,
isChildTask: true,
copyTaskLocals: false,
inheritContext: false,
enqueueJob: false, // don't enqueue, we'll run it manually
addPendingGroupTaskUnconditionally: true,
isDiscardingTask: ${'true' if 'Discarding' in GROUP_TYPE else 'false'},
isSynchronousStart: true
)
// Create the asynchronous task.
let builtinSerialExecutor =
unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
// Create the task in this group.
let (task, _) = Builtin.createTask(
flags: flags,
initialSerialExecutor: builtinSerialExecutor,
taskGroup: self._group,
operation: operation
)
_startTaskImmediately(task, targetExecutor: builtinSerialExecutor)
}
}
% end # METHOD_NAMES
%end # GROUP_TYPES
// ==== Legacy SPI -------------------------------------------------------------
% METHOD_VARIANTS = [
% 'THROWING',
% 'NON_THROWING',
% ]
% OPERATION_PARAM = '@_inheritActorContext @_implicitSelfCapture _ operation: __owned @Sendable @escaping @MainActor () async throws -> Success'
% for METHOD_VARIANT in METHOD_VARIANTS:
% IS_THROWING = METHOD_VARIANT == 'THROWING'
% if IS_THROWING:
% FAILURE_TYPE = 'Error'
% else:
% FAILURE_TYPE = 'Never'
% OPERATION_PARAM = OPERATION_PARAM.replace('throws', '')
% end
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY && !SWIFT_CONCURRENCY_EMBEDDED
@available(SwiftStdlib 5.9, *)
extension Task where Failure == ${FAILURE_TYPE} {
@_spi(MainActorUtilities)
@MainActor
@available(SwiftStdlib 5.9, *)
@discardableResult
public static func startOnMainActor(
priority: TaskPriority? = nil,
${OPERATION_PARAM}
) -> Task<Success, ${FAILURE_TYPE}> {
let flags = taskCreateFlags(
priority: priority,
isChildTask: false,
copyTaskLocals: true,
inheritContext: true,
enqueueJob: false,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false,
isSynchronousStart: false
)
let (task, _) = Builtin.createAsyncTask(flags, operation)
_startTaskOnMainActor(task)
return Task<Success, ${FAILURE_TYPE}>(task)
}
}
#endif
% end
// Internal Runtime Functions --------------------------------------------------
@_silgen_name("swift_task_startOnMainActor")
internal func _startTaskOnMainActor(_ task: Builtin.NativeObject)
@available(SwiftStdlib 6.2, *)
@_silgen_name("swift_task_immediate")
@usableFromInline
internal func _startTaskImmediately(_ task: Builtin.NativeObject, targetExecutor: Builtin.Executor?)