Skip to content

[6.2][stdlib] SE-0472: Rename Task and*TaskGroup APIs to match the proposal #81488

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 5 commits into from
May 14, 2025
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
4 changes: 2 additions & 2 deletions Runtimes/Core/Concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
add_subdirectory(InternalShims)

gyb_expand(TaskGroup+addTask.swift.gyb TaskGroup+addTask.swift)
gyb_expand(Task+startSynchronously.swift.gyb Task+startSynchronously.swift)
gyb_expand(Task+immediate.swift.gyb Task+immediate.swift)

add_library(swift_Concurrency
Actor.cpp
Expand Down Expand Up @@ -97,7 +97,7 @@ add_library(swift_Concurrency
TaskSleep.swift
TaskSleepDuration.swift
"${CMAKE_CURRENT_BINARY_DIR}/TaskGroup+addTask.swift"
"${CMAKE_CURRENT_BINARY_DIR}/Task+startSynchronously.swift")
"${CMAKE_CURRENT_BINARY_DIR}/Task+immediate.swift")

include(${SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR}.cmake)
target_compile_definitions(swift_Concurrency PRIVATE
Expand Down
8 changes: 4 additions & 4 deletions include/swift/ABI/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ class SerialExecutorRef {
/// Executor that may need to participate in complex "same context" checks,
/// by invoking `isSameExclusiveExecutionContext` when comparing execution contexts.
ComplexEquality = 0b01,
/// Mark this executor as the one used by `Task.startSynchronously`,
/// Mark this executor as the one used by `Task.immediate`,
/// It cannot participate in switching.
// TODO: Perhaps make this a generic "cannot switch" rather than start synchronously specific.
StartSynchronously = 0b10,
Immediate = 0b10,
};

static_assert(static_cast<uintptr_t>(ExecutorKind::Ordinary) == 0);
Expand All @@ -107,12 +107,12 @@ class SerialExecutorRef {

static SerialExecutorRef forSynchronousStart() {
auto wtable = reinterpret_cast<uintptr_t>(nullptr) |
static_cast<uintptr_t>(ExecutorKind::StartSynchronously);
static_cast<uintptr_t>(ExecutorKind::Immediate);
return SerialExecutorRef(nullptr, wtable);
}
bool isForSynchronousStart() const {
return getIdentity() == nullptr &&
getExecutorKind() == ExecutorKind::StartSynchronously;
getExecutorKind() == ExecutorKind::Immediate;
}

/// Given a pointer to a serial executor and its SerialExecutor
Expand Down
8 changes: 4 additions & 4 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -2746,7 +2746,7 @@ class TaskCreateFlags : public FlagSet<size_t> {
///
/// Supported starting in Swift 6.1.
Task_IsTaskFunctionConsumed = 15,
Task_IsStartSynchronouslyTask = 16,
Task_IsImmediateTask = 16,
};

explicit constexpr TaskCreateFlags(size_t bits) : FlagSet(bits) {}
Expand Down Expand Up @@ -2779,9 +2779,9 @@ class TaskCreateFlags : public FlagSet<size_t> {
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsTaskFunctionConsumed,
isTaskFunctionConsumed,
setIsTaskFunctionConsumed)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsStartSynchronouslyTask,
isSynchronousStartTask,
setIsSYnchronousStartTask)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsImmediateTask,
isImmediateTask,
setIsImmediateTask)
};

/// Flags for schedulable jobs.
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_startOnMainActor(AsyncTask* job);

SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_startSynchronously(AsyncTask* job);
void swift_task_immediate(AsyncTask* job, SerialExecutorRef targetExecutor);

/// Donate this thread to the global executor until either the
/// given condition returns true or we've run out of cooperative
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,9 +439,10 @@ OVERRIDE_TASK(task_startOnMainActor, void,
swift::, (AsyncTask *task), (task))

// In ACTOR since we need ExecutorTracking info
OVERRIDE_ACTOR(task_startSynchronously, void,
OVERRIDE_ACTOR(task_immediate, void,
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
swift::, (AsyncTask *task), (task))
swift::, (AsyncTask *task, SerialExecutorRef targetExecutor),
(task, targetExecutor))

#undef OVERRIDE
#undef OVERRIDE_ACTOR
Expand Down
35 changes: 22 additions & 13 deletions stdlib/public/Concurrency/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2510,22 +2510,31 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
}

SWIFT_CC(swift)
static void swift_task_startSynchronouslyImpl(AsyncTask* task) {
static void
swift_task_immediateImpl(AsyncTask *task,
SerialExecutorRef targetExecutor) {
swift_retain(task);

auto currentTracking = ExecutorTrackingInfo::current();
if (currentTracking) {
auto currentExecutor = currentTracking->getActiveExecutor();
AsyncTask * originalTask = _swift_task_clearCurrent();

swift_job_run(task, currentExecutor);
_swift_task_setCurrent(originalTask);
if (targetExecutor.isGeneric()) {
// If the target is generic, it means that the closure did not specify
// an isolation explicitly. According to the "start synchronously" rules,
// we should therefore ignore the global and just start running on the
// caller immediately.
SerialExecutorRef executor = SerialExecutorRef::forSynchronousStart();

auto originalTask = ActiveTask::swap(task);
swift_job_run(task, executor);
_swift_task_setCurrent(originalTask);
} else {
auto originalTask = ActiveTask::swap(task);
assert(!originalTask);
assert(swift_task_isCurrentExecutor(targetExecutor) &&
"'immediate' must only be invoked when it is correctly in "
"the same isolation already, but wasn't!");

// We can run synchronously, we're on the expected executor so running in
// the caller context is going to be in the same context as the requested
// "caller" context.
AsyncTask *originalTask = _swift_task_clearCurrent();

SerialExecutorRef executor = SerialExecutorRef::forSynchronousStart();
swift_job_run(task, executor);
swift_job_run(task, targetExecutor);
_swift_task_setCurrent(originalTask);
}
}
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/Concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I

GYB_SOURCES
TaskGroup+addTask.swift.gyb
Task+startSynchronously.swift.gyb
Task+immediate.swift.gyb

SWIFT_MODULE_DEPENDS_ANDROID Android
SWIFT_MODULE_DEPENDS_LINUX Glibc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import Swift
@_implementationOnly import SwiftConcurrencyInternalShims

// ==== Task.startSynchronously ------------------------------------------------
// ==== Task.immediate ---------------------------------------------------------

% METHOD_VARIANTS = [
% 'THROWING',
Expand All @@ -32,6 +32,17 @@ import Swift
@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 _ operation: __owned @isolated(any) @escaping () async throws -> Success
) -> Task<Success, ${FAILURE_TYPE}> {
immediate(name: name, priority: priority, 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.
Expand All @@ -56,17 +67,33 @@ extension Task where Failure == ${FAILURE_TYPE} {
/// - Returns: A reference to the unstructured task which may be awaited on.
@available(SwiftStdlib 6.2, *)
@discardableResult
public static func startSynchronously(
public static func immediate(
name: String? = nil,
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture _ operation: __owned sending @escaping () async throws -> Success
% # 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 _ operation: __owned @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: false, // don't enqueue, we'll run it manually
enqueueJob: !canRunSynchronously,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false,
isSynchronousStart: true
Expand All @@ -79,6 +106,7 @@ extension Task where Failure == ${FAILURE_TYPE} {
unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in
Builtin.createTask(
flags: flags,
initialSerialExecutor: builtinSerialExecutor,
taskName: nameBytes.baseAddress!._rawValue,
operation: operation).0
}
Expand All @@ -91,7 +119,9 @@ extension Task where Failure == ${FAILURE_TYPE} {
operation: operation).0
}

_startTaskSynchronously(task!)
if canRunSynchronously {
_startTaskImmediately(task!, targetExecutor: builtinSerialExecutor)
}
return Task<Success, ${FAILURE_TYPE}>(task!)
}
}
Expand All @@ -102,35 +132,35 @@ GROUP_AND_OP_INFO = [
(
'TaskGroup',
[
'startTaskSynchronously',
'startTaskSynchronouslyUnlessCancelled'
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'',
'ChildTaskResult'
),
(
'ThrowingTaskGroup',
[
'startTaskSynchronously',
'startTaskSynchronouslyUnlessCancelled'
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'throws ',
'ChildTaskResult'
),
(
'DiscardingTaskGroup',
[
'startTaskSynchronously',
'startTaskSynchronouslyUnlessCancelled'
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'',
'Void'
),
(
'ThrowingDiscardingTaskGroup',
[
'startTaskSynchronously',
'startTaskSynchronouslyUnlessCancelled'
'addImmediateTask',
'addImmediateTaskUnlessCancelled'
],
'throws ',
'Void'
Expand Down Expand Up @@ -161,7 +191,7 @@ extension ${GROUP_TYPE} {
public func ${METHOD_NAME}( // in ${GROUP_TYPE}
name: String? = nil,
priority: TaskPriority? = nil,
operation: sending @escaping () async ${THROWS}-> ${RESULT_TYPE}
@_inheritActorContext @_implicitSelfCapture operation: sending @isolated(any) @escaping () async ${THROWS}-> ${RESULT_TYPE}
) {
let flags = taskCreateFlags(
priority: priority,
Expand All @@ -174,13 +204,18 @@ extension ${GROUP_TYPE} {
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
)
_startTaskSynchronously(task)
_startTaskImmediately(task, targetExecutor: builtinSerialExecutor)
}
}
% end # METHOD_NAMES
Expand Down Expand Up @@ -240,5 +275,5 @@ extension Task where Failure == ${FAILURE_TYPE} {
@_silgen_name("swift_task_startOnMainActor")
internal func _startTaskOnMainActor(_ task: Builtin.NativeObject)

@_silgen_name("swift_task_startSynchronously")
internal func _startTaskSynchronously(_ task: Builtin.NativeObject)
@_silgen_name("swift_task_immediate")
internal func _startTaskImmediately(_ task: Builtin.NativeObject, targetExecutor: Builtin.Executor?)
3 changes: 1 addition & 2 deletions stdlib/public/Concurrency/Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -807,8 +807,7 @@ extension Task where Failure == Error {
unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor

let (task, _) = Builtin.createTask(flags: flags,
initialSerialExecutor:
builtinSerialExecutor,
initialSerialExecutor: builtinSerialExecutor,
operation: operation)

self._task = task
Expand Down
Loading