From 910bc1a1a3c27a053c1e3dd453876b812a2558a9 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Tue, 15 Apr 2025 16:20:31 +0900 Subject: [PATCH 1/5] [Concurrency] Improve in order synchronous enqueue of startSynchronously Previously there was still a sneaky hop which caused ordering issues. This introduced a specific test startSynchronously_order which checks that the task enqueues indeed are "immediate" and cleans up how we handle this. This also prepares for the being discussed in SE review direction of this API that it SHOULD be ALLOWED to actually hop and NOT be synchronous at all IF the isolation is specified on the closure and is DIFFERENT than the callers dynamic isolation. This effectively implements "synchronously run right now if dynamically on the exact isolation as requested by the closure; otherwise enqueue the task as usual". resolves rdar://149284186 cc @drexin (cherry picked from commit a24a28c217ea1b5a6a99cca2790133413be558c2) --- include/swift/Runtime/Concurrency.h | 2 +- .../CompatibilityOverrideConcurrency.def | 3 +- stdlib/public/Concurrency/Actor.cpp | 35 ++++--- .../Task+startSynchronously.swift.gyb | 36 +++++-- stdlib/public/Concurrency/Task.swift | 3 +- .../Runtime/startSynchronously.swift | 94 ++++++++++++++++++- .../Runtime/startSynchronously_order.swift | 64 +++++++++++++ .../CompatibilityOverrideConcurrency.cpp | 4 +- 8 files changed, 213 insertions(+), 28 deletions(-) create mode 100644 test/Concurrency/Runtime/startSynchronously_order.swift diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index 7135c2ae13185..f509c2ab36fbf 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -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_startSynchronously(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 diff --git a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def index 40cd713e4f629..270349ffc01a2 100644 --- a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def +++ b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def @@ -441,7 +441,8 @@ OVERRIDE_TASK(task_startOnMainActor, void, // In ACTOR since we need ExecutorTracking info OVERRIDE_ACTOR(task_startSynchronously, 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 diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 47da9f404dc75..32ed9e6225518 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -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_startSynchronouslyImpl(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) && + "startSynchronously 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); } } diff --git a/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb b/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb index 30c55da989f40..c2331fdf6c8c7 100644 --- a/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb +++ b/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb @@ -59,14 +59,30 @@ extension Task where Failure == ${FAILURE_TYPE} { public static func startSynchronously( 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.startSynchronously { @MyGlobalActor in } }` + @_implicitSelfCapture _ operation: __owned @isolated(any) @escaping () async throws -> Success ) -> Task { + + 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 @@ -79,6 +95,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 } @@ -91,7 +108,9 @@ extension Task where Failure == ${FAILURE_TYPE} { operation: operation).0 } - _startTaskSynchronously(task!) + if canRunSynchronously { + _startTaskSynchronously(task!, targetExecutor: builtinSerialExecutor) + } return Task(task!) } } @@ -161,7 +180,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, @@ -174,13 +193,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) + _startTaskSynchronously(task, targetExecutor: builtinSerialExecutor) } } % end # METHOD_NAMES @@ -241,4 +265,4 @@ extension Task where Failure == ${FAILURE_TYPE} { internal func _startTaskOnMainActor(_ task: Builtin.NativeObject) @_silgen_name("swift_task_startSynchronously") -internal func _startTaskSynchronously(_ task: Builtin.NativeObject) +internal func _startTaskSynchronously(_ task: Builtin.NativeObject, targetExecutor: Builtin.Executor?) diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index dfb31ffaba6cc..8db587e5863f8 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -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 diff --git a/test/Concurrency/Runtime/startSynchronously.swift b/test/Concurrency/Runtime/startSynchronously.swift index bbbb9418f9be8..a03b1095fbd3f 100644 --- a/test/Concurrency/Runtime/startSynchronously.swift +++ b/test/Concurrency/Runtime/startSynchronously.swift @@ -1,4 +1,3 @@ -// REQUIRES: rdar145735542 // RUN: %empty-directory(%t) // RUN: %target-build-swift -Xfrontend -disable-availability-checking %s %import-libdispatch -swift-version 6 -o %t/a.out // RUN: %target-codesign %t/a.out @@ -62,6 +61,9 @@ extension ThreadID: @unchecked Sendable {} @globalActor actor MyGlobalActor { static let shared: MyGlobalActor = MyGlobalActor() + + @MyGlobalActor + static func test() {} } final class NaiveQueueExecutor: SerialExecutor { @@ -73,14 +75,30 @@ final class NaiveQueueExecutor: SerialExecutor { public func enqueue(_ job: consuming ExecutorJob) { let unowned = UnownedJob(job) - print("NaiveQueueExecutor(\(self.queue.label)) enqueue... [thread:\(getCurrentThreadID())]") + print("NaiveQueueExecutor(\(self.queue.label)) enqueue [thread:\(getCurrentThreadID())]") queue.async { - print("NaiveQueueExecutor(\(self.queue.label)) enqueue: run [thread:\(getCurrentThreadID())]") unowned.runSynchronously(on: self.asUnownedSerialExecutor()) } } } +@globalActor +actor DifferentGlobalActor { + static let queue = DispatchQueue(label: "DifferentGlobalActor-queue") + let executor: NaiveQueueExecutor + nonisolated let unownedExecutor: UnownedSerialExecutor + + init() { + self.executor = NaiveQueueExecutor(queue: DifferentGlobalActor.queue) + self.unownedExecutor = executor.asUnownedSerialExecutor() + } + + static let shared: DifferentGlobalActor = DifferentGlobalActor() + + @DifferentGlobalActor + static func test() {} +} + // Test on all platforms func syncOnMyGlobalActor() -> [Task] { MyGlobalActor.shared.preconditionIsolated("Should be executing on the global actor here") @@ -111,6 +129,49 @@ func syncOnMyGlobalActor() -> [Task] { return [t1, tt] } +func syncOnMyGlobalActorHopToDifferentActor() -> [Task] { + MyGlobalActor.shared.preconditionIsolated("Should be executing on the global actor here") + print("Confirmed to be on @MyGlobalActor") + + // This task must be guaranteed to happen AFTER 'tt' because we are already on this actor + // so this enqueue must happen after we give up the actor. + print("schedule Task { @DifferentGlobalActor }, before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + let t1 = Task { @DifferentGlobalActor in + print("inside Task { @DifferentGlobalActor } [thread:\(getCurrentThreadID())] @ :\(#line)") + DifferentGlobalActor.shared.preconditionIsolated("Expected Task{} to be on DifferentGlobalActor") + } + + print("before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + let outerTID = getCurrentThreadID() + let tt = Task.startSynchronously { @DifferentGlobalActor in + let innerTID = getCurrentThreadID() + print("inside startSynchronously, outer thread = \(outerTID)") + print("inside startSynchronously, inner thread = \(innerTID)") + if (compareThreadIDs(outerTID, .equal, innerTID)) { + // This case specifically is NOT synchronously run because we specified a different isolation for the closure + // and FORCED a hop to the DifferentGlobalActor executor. + print("ERROR! Outer Thread ID must NOT equal Thread ID inside runSynchronously synchronous part!") + } + // We crucially need to see this task be enqueued on the different global actor, + // so it did not execute "synchronously" after all - it had to hop to the other actor. + dispatchPrecondition(condition: .onQueue(DifferentGlobalActor.queue)) + DifferentGlobalActor.shared.preconditionIsolated("Expected Task.startSynchronously { @DifferentGlobalActor in } to be on DifferentGlobalActor") + + print("inside startSynchronously, sleep now [thread:\(getCurrentThreadID())] @ :\(#line)") + _ = try? await Task.sleep(for: .milliseconds(100)) + + print("inside startSynchronously, after sleep [thread:\(getCurrentThreadID())] @ :\(#line)") + dispatchPrecondition(condition: .onQueue(DifferentGlobalActor.queue)) + DifferentGlobalActor.shared.preconditionIsolated("Expected Task.startSynchronously { @DifferentGlobalActor in } to be on DifferentGlobalActor") + + // do something here + await MyGlobalActor.test() + DifferentGlobalActor.test() + } + + return [t1, tt] +} + func syncOnNonTaskThread(synchronousTask behavior: SynchronousTaskBehavior) { let sem1 = DispatchSemaphore(value: 0) let sem2 = DispatchSemaphore(value: 0) @@ -179,6 +240,33 @@ await Task { @MyGlobalActor in // resume on some other thread // CHECK: after sleep, inside startSynchronously +print("\n\n==== ------------------------------------------------------------------") +print("syncOnMyGlobalActorHopToDifferentActor()") + +await Task { @MyGlobalActor in + MyGlobalActor.shared.preconditionIsolated("Should be executing on the global actor here") + for t in syncOnMyGlobalActorHopToDifferentActor() { + await t.value + } +}.value + +// Assertion Notes: We expect the task to be on the specified queue as we force the Task.startSynchronously +// task to enqueue on the DifferentGlobalActor, however we CANNOT use threads to verify this behavior, +// because dispatch may still pull tricks and reuse threads. We can only verify that we're on the right +// queue, and that the `enqueue` calls on the target executor happen when we expect them to. +// +// CHECK: syncOnMyGlobalActorHopToDifferentActor() +// CHECK: Confirmed to be on @MyGlobalActor +// CHECK: before startSynchronously + +// This IS actually enqueueing on the target actor (not synchronous), as expected: +// CHECK: NaiveQueueExecutor(DifferentGlobalActor-queue) enqueue +// CHECK: inside startSynchronously, sleep now + +// After the sleep we get back onto the specified executor as expected +// CHECK: NaiveQueueExecutor(DifferentGlobalActor-queue) enqueue +// CHECK: inside startSynchronously, after sleep + print("\n\n==== ------------------------------------------------------------------") var behavior: SynchronousTaskBehavior = .suspend print("syncOnNonTaskThread(synchronousTask: \(behavior))") diff --git a/test/Concurrency/Runtime/startSynchronously_order.swift b/test/Concurrency/Runtime/startSynchronously_order.swift new file mode 100644 index 0000000000000..03927dfaa7e5e --- /dev/null +++ b/test/Concurrency/Runtime/startSynchronously_order.swift @@ -0,0 +1,64 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s %import-libdispatch -swift-version 6 -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: concurrency_runtime + +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: back_deploy_concurrency +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: freestanding + +import _Concurrency + +let max = 1000 + +func bar(x: Int, cc: CheckedContinuation) { + Task.startSynchronously { + print("Task \(x) started") + try! await Task.sleep(nanoseconds: 10000) + if (x == max) { + cc.resume() + } + } +} + +await withCheckedContinuation { (cc: CheckedContinuation) in + for i in 1...max { + bar(x: i, cc: cc) + } +} + +// CHECK: Task 1 started +// CHECK: Task 2 started +// CHECK: Task 3 started +// CHECK: Task 4 started +// CHECK: Task 5 started +// CHECK: Task 6 started +// CHECK: Task 7 started +// CHECK: Task 8 started +// CHECK: Task 9 started +// CHECK: Task 10 started +// CHECK: Task 11 started +// CHECK: Task 12 started +// CHECK: Task 13 started +// CHECK: Task 14 started +// CHECK: Task 15 started +// CHECK: Task 16 started +// CHECK: Task 17 started +// CHECK: Task 18 started +// CHECK: Task 19 started +// CHECK: Task 20 started +// CHECK: Task 21 started +// CHECK: Task 22 started +// CHECK: Task 23 started +// CHECK: Task 24 started +// CHECK: Task 25 started +// CHECK: Task 26 started +// CHECK: Task 27 started +// CHECK: Task 28 started +// CHECK: Task 29 started +// CHECK: Task 30 started \ No newline at end of file diff --git a/unittests/runtime/CompatibilityOverrideConcurrency.cpp b/unittests/runtime/CompatibilityOverrideConcurrency.cpp index 353c62011328d..2f220bcf177ed 100644 --- a/unittests/runtime/CompatibilityOverrideConcurrency.cpp +++ b/unittests/runtime/CompatibilityOverrideConcurrency.cpp @@ -118,7 +118,7 @@ static void swift_task_startOnMainActor_override(AsyncTask* task) { } SWIFT_CC(swift) -static void swift_task_startSynchronously_override(AsyncTask* task) { +static void swift_task_startSynchronously_override(AsyncTask* task, SerialExecutorRef targetExecutor) { Ran = true; } @@ -351,7 +351,7 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startOnMainActorImpl) { } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startSynchronously) { - swift_task_startSynchronously(nullptr); + swift_task_startSynchronously(nullptr, SerialExecutorRef::generic()); } TEST_F(CompatibilityOverrideConcurrencyTest, From 413373480eef7de35975a9f8b35fdbb3e4aa4bb3 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 18 Apr 2025 08:28:01 +0900 Subject: [PATCH 2/5] Disable startSynchronously while we rework semantics Reworking semantics of this API so disabling the flaky test, it'll have different sematntics and then the test will be adjusted (cherry picked from commit a3762d4e199a75520cd8da475f6cd6ba6dea1abb) --- test/Concurrency/Runtime/startSynchronously.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Concurrency/Runtime/startSynchronously.swift b/test/Concurrency/Runtime/startSynchronously.swift index a03b1095fbd3f..6ea63196a5753 100644 --- a/test/Concurrency/Runtime/startSynchronously.swift +++ b/test/Concurrency/Runtime/startSynchronously.swift @@ -1,3 +1,6 @@ +// FIXME: Marking this disabled since we're reworking the semantics and the test is a bit racy until we do +// REQUIRES: rdar149506152 + // RUN: %empty-directory(%t) // RUN: %target-build-swift -Xfrontend -disable-availability-checking %s %import-libdispatch -swift-version 6 -o %t/a.out // RUN: %target-codesign %t/a.out From 05ddcac51bc2ffea32eb9dd2abb9bd18f2270c69 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Tue, 15 Apr 2025 20:33:19 +0900 Subject: [PATCH 3/5] [Concurrency] Update ABI for startSynchronously (cherry picked from commit b76abb4a9993cd245e6c8539bd7fa31a01475ff8) --- test/abi/macOS/arm64/concurrency.swift | 23 +++++++++++++---------- test/abi/macOS/x86_64/concurrency.swift | 23 +++++++++++++---------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/test/abi/macOS/arm64/concurrency.swift b/test/abi/macOS/arm64/concurrency.swift index 634e96492dde8..b75ed24239519 100644 --- a/test/abi/macOS/arm64/concurrency.swift +++ b/test/abi/macOS/arm64/concurrency.swift @@ -388,18 +388,21 @@ Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolatio Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV Added: _swift_task_getCurrentTaskName -Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKcntFZ -Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKcntFZ -Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKcntF +// startSynchronously Added: _swift_task_startSynchronously +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF // isIsolatingCurrentContext Added: _swift_task_invokeSwiftIsIsolatingCurrentContext diff --git a/test/abi/macOS/x86_64/concurrency.swift b/test/abi/macOS/x86_64/concurrency.swift index 07ec5e21e67ee..cfb5cac453634 100644 --- a/test/abi/macOS/x86_64/concurrency.swift +++ b/test/abi/macOS/x86_64/concurrency.swift @@ -388,18 +388,21 @@ Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolatio Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV Added: _swift_task_getCurrentTaskName -Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYacntF -Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKcntFZ -Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKcntFZ -Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKcntF -Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYacntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKcntF +// startSynchronously Added: _swift_task_startSynchronously +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF // isIsolatingCurrentContext Added: _swift_task_invokeSwiftIsIsolatingCurrentContext From 2bf00077e4f14f7b1fc7b707c089fd40339f8002 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 9 May 2025 17:43:50 -0700 Subject: [PATCH 4/5] [stdlib] SE-0472: Rename `Task` and`*TaskGroup` APIs to match the proposal `Task.startSynchronously` -> `Task.immediate` `*TaskGroup.startTaskSynchronously{UnlessCancelled}` -> `*TaskGroup.addImmediateTask{UnlessCancelled}` (cherry picked from commit 001eab867dddfc1ea9cdf68c37262f493a2eb1e3) --- Runtimes/Core/Concurrency/CMakeLists.txt | 4 +- include/swift/ABI/Executor.h | 8 +- include/swift/ABI/MetadataValues.h | 8 +- include/swift/Runtime/Concurrency.h | 2 +- .../CompatibilityOverrideConcurrency.def | 2 +- stdlib/public/Concurrency/Actor.cpp | 6 +- stdlib/public/Concurrency/CMakeLists.txt | 2 +- ...sly.swift.gyb => Task+immediate.swift.gyb} | 30 ++-- ...hronously.swift => startImmediately.swift} | 138 +++++++++--------- ...der.swift => startImmediately_order.swift} | 4 +- ....swift => startImmediatelyIsolation.swift} | 22 +-- test/abi/macOS/arm64/concurrency.swift | 24 +-- test/abi/macOS/x86_64/concurrency.swift | 24 +-- .../CompatibilityOverrideConcurrency.cpp | 6 +- 14 files changed, 140 insertions(+), 140 deletions(-) rename stdlib/public/Concurrency/{Task+startSynchronously.swift.gyb => Task+immediate.swift.gyb} (90%) rename test/Concurrency/Runtime/{startSynchronously.swift => startImmediately.swift} (73%) rename test/Concurrency/Runtime/{startSynchronously_order.swift => startImmediately_order.swift} (96%) rename test/Concurrency/{startSynchronouslyIsolation.swift => startImmediatelyIsolation.swift} (50%) diff --git a/Runtimes/Core/Concurrency/CMakeLists.txt b/Runtimes/Core/Concurrency/CMakeLists.txt index 65f9ca7ea92ed..5fd2ec17e4138 100644 --- a/Runtimes/Core/Concurrency/CMakeLists.txt +++ b/Runtimes/Core/Concurrency/CMakeLists.txt @@ -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 @@ -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 diff --git a/include/swift/ABI/Executor.h b/include/swift/ABI/Executor.h index 5523255c05076..4f4fcf8927253 100644 --- a/include/swift/ABI/Executor.h +++ b/include/swift/ABI/Executor.h @@ -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(ExecutorKind::Ordinary) == 0); @@ -107,12 +107,12 @@ class SerialExecutorRef { static SerialExecutorRef forSynchronousStart() { auto wtable = reinterpret_cast(nullptr) | - static_cast(ExecutorKind::StartSynchronously); + static_cast(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 diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index e2e1c45a9b43b..c34633649e5d0 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -2746,7 +2746,7 @@ class TaskCreateFlags : public FlagSet { /// /// Supported starting in Swift 6.1. Task_IsTaskFunctionConsumed = 15, - Task_IsStartSynchronouslyTask = 16, + Task_IsImmediateTask = 16, }; explicit constexpr TaskCreateFlags(size_t bits) : FlagSet(bits) {} @@ -2779,9 +2779,9 @@ class TaskCreateFlags : public FlagSet { 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. diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index f509c2ab36fbf..02b932976aa88 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -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, SerialExecutorRef targetExecutor); +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 diff --git a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def index 270349ffc01a2..7873272df2663 100644 --- a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def +++ b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def @@ -439,7 +439,7 @@ 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, SerialExecutorRef targetExecutor), (task, targetExecutor)) diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 32ed9e6225518..00a0443175048 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -2511,8 +2511,8 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex SWIFT_CC(swift) static void -swift_task_startSynchronouslyImpl(AsyncTask *task, - SerialExecutorRef targetExecutor) { +swift_task_immediateImpl(AsyncTask *task, + SerialExecutorRef targetExecutor) { swift_retain(task); if (targetExecutor.isGeneric()) { // If the target is generic, it means that the closure did not specify @@ -2526,7 +2526,7 @@ swift_task_startSynchronouslyImpl(AsyncTask *task, _swift_task_setCurrent(originalTask); } else { assert(swift_task_isCurrentExecutor(targetExecutor) && - "startSynchronously must only be invoked when it is correctly in " + "'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 diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 611c525e6e666..3a85929288672 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -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 diff --git a/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb b/stdlib/public/Concurrency/Task+immediate.swift.gyb similarity index 90% rename from stdlib/public/Concurrency/Task+startSynchronously.swift.gyb rename to stdlib/public/Concurrency/Task+immediate.swift.gyb index c2331fdf6c8c7..2e3bf94574fcc 100644 --- a/stdlib/public/Concurrency/Task+startSynchronously.swift.gyb +++ b/stdlib/public/Concurrency/Task+immediate.swift.gyb @@ -13,7 +13,7 @@ import Swift @_implementationOnly import SwiftConcurrencyInternalShims -// ==== Task.startSynchronously ------------------------------------------------ +// ==== Task.immediate --------------------------------------------------------- % METHOD_VARIANTS = [ % 'THROWING', @@ -56,11 +56,11 @@ 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, % # 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.startSynchronously { @MyGlobalActor in } }` + % # In this case: `func syncOnMyGlobalActor() { Task.immediate { @MyGlobalActor in } }` @_implicitSelfCapture _ operation: __owned @isolated(any) @escaping () async throws -> Success ) -> Task { @@ -109,7 +109,7 @@ extension Task where Failure == ${FAILURE_TYPE} { } if canRunSynchronously { - _startTaskSynchronously(task!, targetExecutor: builtinSerialExecutor) + _startTaskImmediately(task!, targetExecutor: builtinSerialExecutor) } return Task(task!) } @@ -121,8 +121,8 @@ GROUP_AND_OP_INFO = [ ( 'TaskGroup', [ - 'startTaskSynchronously', - 'startTaskSynchronouslyUnlessCancelled' + 'addImmediateTask', + 'addImmediateTaskUnlessCancelled' ], '', 'ChildTaskResult' @@ -130,8 +130,8 @@ GROUP_AND_OP_INFO = [ ( 'ThrowingTaskGroup', [ - 'startTaskSynchronously', - 'startTaskSynchronouslyUnlessCancelled' + 'addImmediateTask', + 'addImmediateTaskUnlessCancelled' ], 'throws ', 'ChildTaskResult' @@ -139,8 +139,8 @@ GROUP_AND_OP_INFO = [ ( 'DiscardingTaskGroup', [ - 'startTaskSynchronously', - 'startTaskSynchronouslyUnlessCancelled' + 'addImmediateTask', + 'addImmediateTaskUnlessCancelled' ], '', 'Void' @@ -148,8 +148,8 @@ GROUP_AND_OP_INFO = [ ( 'ThrowingDiscardingTaskGroup', [ - 'startTaskSynchronously', - 'startTaskSynchronouslyUnlessCancelled' + 'addImmediateTask', + 'addImmediateTaskUnlessCancelled' ], 'throws ', 'Void' @@ -204,7 +204,7 @@ extension ${GROUP_TYPE} { taskGroup: self._group, operation: operation ) - _startTaskSynchronously(task, targetExecutor: builtinSerialExecutor) + _startTaskImmediately(task, targetExecutor: builtinSerialExecutor) } } % end # METHOD_NAMES @@ -264,5 +264,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, targetExecutor: Builtin.Executor?) +@_silgen_name("swift_task_immediate") +internal func _startTaskImmediately(_ task: Builtin.NativeObject, targetExecutor: Builtin.Executor?) diff --git a/test/Concurrency/Runtime/startSynchronously.swift b/test/Concurrency/Runtime/startImmediately.swift similarity index 73% rename from test/Concurrency/Runtime/startSynchronously.swift rename to test/Concurrency/Runtime/startImmediately.swift index 6ea63196a5753..629fa9f20a640 100644 --- a/test/Concurrency/Runtime/startSynchronously.swift +++ b/test/Concurrency/Runtime/startImmediately.swift @@ -109,24 +109,24 @@ func syncOnMyGlobalActor() -> [Task] { // This task must be guaranteed to happen AFTER 'tt' because we are already on this actor // so this enqueue must happen after we give up the actor. - print("schedule Task { @MyGlobalActor }, before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("schedule Task { @MyGlobalActor }, before immediate [thread:\(getCurrentThreadID())] @ :\(#line)") let t1 = Task { @MyGlobalActor in print("inside Task { @MyGlobalActor }, after sleep [thread:\(getCurrentThreadID())] @ :\(#line)") } - print("before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("before immediate [thread:\(getCurrentThreadID())] @ :\(#line)") let outerTID = getCurrentThreadID() - let tt = Task.startSynchronously { @MyGlobalActor in + let tt = Task.immediate { @MyGlobalActor in let innerTID = getCurrentThreadID() - print("inside startSynchronously, outer thread = \(outerTID)") - print("inside startSynchronously, inner thread = \(innerTID)") + print("inside immediate, outer thread = \(outerTID)") + print("inside immediate, inner thread = \(innerTID)") if (compareThreadIDs(outerTID, .notEqual, innerTID)) { print("ERROR! Outer Thread ID must be equal Thread ID inside runSynchronously synchronous part!") } - print("inside startSynchronously, sleep now [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, sleep now [thread:\(getCurrentThreadID())] @ :\(#line)") _ = try? await Task.sleep(for: .seconds(1)) - print("after sleep, inside startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("after sleep, inside immediate [thread:\(getCurrentThreadID())] @ :\(#line)") } return [t1, tt] @@ -138,18 +138,18 @@ func syncOnMyGlobalActorHopToDifferentActor() -> [Task] { // This task must be guaranteed to happen AFTER 'tt' because we are already on this actor // so this enqueue must happen after we give up the actor. - print("schedule Task { @DifferentGlobalActor }, before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("schedule Task { @DifferentGlobalActor }, before immediate [thread:\(getCurrentThreadID())] @ :\(#line)") let t1 = Task { @DifferentGlobalActor in print("inside Task { @DifferentGlobalActor } [thread:\(getCurrentThreadID())] @ :\(#line)") DifferentGlobalActor.shared.preconditionIsolated("Expected Task{} to be on DifferentGlobalActor") } - print("before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("before immediate [thread:\(getCurrentThreadID())] @ :\(#line)") let outerTID = getCurrentThreadID() - let tt = Task.startSynchronously { @DifferentGlobalActor in + let tt = Task.immediate { @DifferentGlobalActor in let innerTID = getCurrentThreadID() - print("inside startSynchronously, outer thread = \(outerTID)") - print("inside startSynchronously, inner thread = \(innerTID)") + print("inside immediate, outer thread = \(outerTID)") + print("inside immediate, inner thread = \(innerTID)") if (compareThreadIDs(outerTID, .equal, innerTID)) { // This case specifically is NOT synchronously run because we specified a different isolation for the closure // and FORCED a hop to the DifferentGlobalActor executor. @@ -158,14 +158,14 @@ func syncOnMyGlobalActorHopToDifferentActor() -> [Task] { // We crucially need to see this task be enqueued on the different global actor, // so it did not execute "synchronously" after all - it had to hop to the other actor. dispatchPrecondition(condition: .onQueue(DifferentGlobalActor.queue)) - DifferentGlobalActor.shared.preconditionIsolated("Expected Task.startSynchronously { @DifferentGlobalActor in } to be on DifferentGlobalActor") + DifferentGlobalActor.shared.preconditionIsolated("Expected Task.immediate { @DifferentGlobalActor in } to be on DifferentGlobalActor") - print("inside startSynchronously, sleep now [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, sleep now [thread:\(getCurrentThreadID())] @ :\(#line)") _ = try? await Task.sleep(for: .milliseconds(100)) - print("inside startSynchronously, after sleep [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, after sleep [thread:\(getCurrentThreadID())] @ :\(#line)") dispatchPrecondition(condition: .onQueue(DifferentGlobalActor.queue)) - DifferentGlobalActor.shared.preconditionIsolated("Expected Task.startSynchronously { @DifferentGlobalActor in } to be on DifferentGlobalActor") + DifferentGlobalActor.shared.preconditionIsolated("Expected Task.immediate { @DifferentGlobalActor in } to be on DifferentGlobalActor") // do something here await MyGlobalActor.test() @@ -182,33 +182,33 @@ func syncOnNonTaskThread(synchronousTask behavior: SynchronousTaskBehavior) { queue.async { // This is in order so we don't have a "current task" nor any "current executor" - print("before startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("before immediate [thread:\(getCurrentThreadID())] @ :\(#line)") let outerTID = getCurrentThreadID() - let tt = Task.startSynchronously { + let tt = Task.immediate { dispatchPrecondition(condition: .onQueue(queue)) let innerTID = getCurrentThreadID() if compareThreadIDs(outerTID, .notEqual, innerTID) { - print("inside startSynchronously, outer thread = \(outerTID)") - print("inside startSynchronously, inner thread = \(innerTID)") + print("inside immediate, outer thread = \(outerTID)") + print("inside immediate, inner thread = \(innerTID)") print("ERROR! Outer Thread ID must be equal Thread ID inside runSynchronously synchronous part!") } - print("inside startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate [thread:\(getCurrentThreadID())] @ :\(#line)") switch behavior { case .suspend: // sleep until woken up by outer task; i.e. actually suspend - print("inside startSynchronously, before sleep [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, before sleep [thread:\(getCurrentThreadID())] @ :\(#line)") _ = try? await Task.sleep(for: .seconds(10)) - print("inside startSynchronously, after sleep [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, after sleep [thread:\(getCurrentThreadID())] @ :\(#line)") case .dontSuspend: - print("inside startSynchronously, done [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, done [thread:\(getCurrentThreadID())] @ :\(#line)") () } sem1.signal() } - print("after startSynchronously, outside; cancel (wakeup) the synchronous task! [thread:\(getCurrentThreadID())] @ :\(#line)") + print("after immediate, outside; cancel (wakeup) the synchronous task! [thread:\(getCurrentThreadID())] @ :\(#line)") tt.cancel() // wake up the sleep sem1.wait() @@ -235,13 +235,13 @@ await Task { @MyGlobalActor in // CHECK-LABEL: syncOnMyGlobalActor() // CHECK: Confirmed to be on @MyGlobalActor -// CHECK: schedule Task { @MyGlobalActor }, before startSynchronously [thread:[[CALLING_THREAD:.*]]] -// CHECK: before startSynchronously [thread:[[CALLING_THREAD]]] +// CHECK: schedule Task { @MyGlobalActor }, before immediate [thread:[[CALLING_THREAD:.*]]] +// CHECK: before immediate [thread:[[CALLING_THREAD]]] // CHECK-NOT: ERROR! -// CHECK: inside startSynchronously, sleep now +// CHECK: inside immediate, sleep now // CHECK: inside Task { @MyGlobalActor }, after sleep // resume on some other thread -// CHECK: after sleep, inside startSynchronously +// CHECK: after sleep, inside immediate print("\n\n==== ------------------------------------------------------------------") print("syncOnMyGlobalActorHopToDifferentActor()") @@ -253,22 +253,22 @@ await Task { @MyGlobalActor in } }.value -// Assertion Notes: We expect the task to be on the specified queue as we force the Task.startSynchronously +// Assertion Notes: We expect the task to be on the specified queue as we force the Task.immediate // task to enqueue on the DifferentGlobalActor, however we CANNOT use threads to verify this behavior, // because dispatch may still pull tricks and reuse threads. We can only verify that we're on the right // queue, and that the `enqueue` calls on the target executor happen when we expect them to. // // CHECK: syncOnMyGlobalActorHopToDifferentActor() // CHECK: Confirmed to be on @MyGlobalActor -// CHECK: before startSynchronously +// CHECK: before immediate // This IS actually enqueueing on the target actor (not synchronous), as expected: // CHECK: NaiveQueueExecutor(DifferentGlobalActor-queue) enqueue -// CHECK: inside startSynchronously, sleep now +// CHECK: inside immediate, sleep now // After the sleep we get back onto the specified executor as expected // CHECK: NaiveQueueExecutor(DifferentGlobalActor-queue) enqueue -// CHECK: inside startSynchronously, after sleep +// CHECK: inside immediate, after sleep print("\n\n==== ------------------------------------------------------------------") var behavior: SynchronousTaskBehavior = .suspend @@ -277,12 +277,12 @@ syncOnNonTaskThread(synchronousTask: behavior) // CHECK-LABEL: syncOnNonTaskThread(synchronousTask: suspend) // No interleaving allowed between "before" and "inside": -// CHECK-NEXT: before startSynchronously [thread:[[CALLING_THREAD2:.*]]] +// CHECK-NEXT: before immediate [thread:[[CALLING_THREAD2:.*]]] // CHECK-NOT: ERROR! -// CHECK-NEXT: inside startSynchronously [thread:[[CALLING_THREAD2]]] -// CHECK-NEXT: inside startSynchronously, before sleep [thread:[[CALLING_THREAD2]]] -// CHECK-NEXT: after startSynchronously, outside; cancel (wakeup) the synchronous task! [thread:[[CALLING_THREAD2]]] -// CHECK-NEXT: inside startSynchronously, after sleep +// CHECK-NEXT: inside immediate [thread:[[CALLING_THREAD2]]] +// CHECK-NEXT: inside immediate, before sleep [thread:[[CALLING_THREAD2]]] +// CHECK-NEXT: after immediate, outside; cancel (wakeup) the synchronous task! [thread:[[CALLING_THREAD2]]] +// CHECK-NEXT: inside immediate, after sleep print("\n\n==== ------------------------------------------------------------------") behavior = .dontSuspend @@ -290,11 +290,11 @@ print("syncOnNonTaskThread(synchronousTask: \(behavior))") syncOnNonTaskThread(synchronousTask: behavior) // CHECK-LABEL: syncOnNonTaskThread(synchronousTask: dontSuspend) -// CHECK-NEXT: before startSynchronously [thread:[[CALLING_THREAD3:.*]]] +// CHECK-NEXT: before immediate [thread:[[CALLING_THREAD3:.*]]] // CHECK-NOT: ERROR! -// CHECK-NEXT: inside startSynchronously [thread:[[CALLING_THREAD3]]] -// CHECK: inside startSynchronously, done [thread:[[CALLING_THREAD3]]] -// CHECK: after startSynchronously, outside; cancel (wakeup) the synchronous task! [thread:[[CALLING_THREAD3]]] +// CHECK-NEXT: inside immediate [thread:[[CALLING_THREAD3]]] +// CHECK: inside immediate, done [thread:[[CALLING_THREAD3]]] +// CHECK: after immediate, outside; cancel (wakeup) the synchronous task! [thread:[[CALLING_THREAD3]]] print("\n\n==== ------------------------------------------------------------------") print("callActorFromStartSynchronousTask() - not on specific queue") @@ -302,17 +302,17 @@ callActorFromStartSynchronousTask(recipient: .recipient(Recipient())) // CHECK: callActorFromStartSynchronousTask() // No interleaving allowed between "before" and "inside": -// CHECK: before startSynchronously [thread:[[CALLING_THREAD4:.*]]] -// CHECK-NEXT: inside startSynchronously [thread:[[CALLING_THREAD4]]] +// CHECK: before immediate [thread:[[CALLING_THREAD4:.*]]] +// CHECK-NEXT: inside immediate [thread:[[CALLING_THREAD4]]] -// It is important that as we suspend on the actor call, the 'after' startSynchronously gets to run -// CHECK-NEXT: inside startSynchronously, call rec.sync() [thread:[[CALLING_THREAD4]]] -// CHECK: after startSynchronously +// It is important that as we suspend on the actor call, the 'after' immediate gets to run +// CHECK-NEXT: inside immediate, call rec.sync() [thread:[[CALLING_THREAD4]]] +// CHECK: after immediate // CHECK-NOT: ERROR! -// CHECK: inside startSynchronously, call rec.sync() done +// CHECK: inside immediate, call rec.sync() done // CHECK-NOT: ERROR! -// CHECK: inside startSynchronously, done +// CHECK: inside immediate, done /// Don't want to involve protocol calls to not confuse the test with additional details, /// so we use concrete types here. @@ -346,13 +346,13 @@ func callActorFromStartSynchronousTask(recipient rec: TargetActorToCall) { queue.async { let outerTID = getCurrentThreadID() - print("before startSynchronously [thread:\(outerTID)] @ :\(#line)") - let tt = Task.startSynchronously { + print("before immediate [thread:\(outerTID)] @ :\(#line)") + let tt = Task.immediate { dispatchPrecondition(condition: .onQueue(queue)) let innerTID = getCurrentThreadID() precondition(compareThreadIDs(outerTID, .equal, innerTID), "Outer Thread ID must be equal Thread ID inside runSynchronously synchronous part!") - print("inside startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate [thread:\(getCurrentThreadID())] @ :\(#line)") for i in 1..<10 { queue.async { @@ -360,12 +360,12 @@ func callActorFromStartSynchronousTask(recipient rec: TargetActorToCall) { } } - print("inside startSynchronously, call rec.sync() [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, call rec.sync() [thread:\(getCurrentThreadID())] @ :\(#line)") switch rec { case .recipient(let recipient): await recipient.callAndSuspend(syncTaskThreadID: innerTID) case .recipientOnQueue(let recipient): await recipient.callAndSuspend(syncTaskThreadID: innerTID) } - print("inside startSynchronously, call rec.sync() done [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, call rec.sync() done [thread:\(getCurrentThreadID())] @ :\(#line)") // after suspension we are supposed to hop off to the global pool, // thus the thread IDs cannot be the same anymore @@ -378,11 +378,11 @@ func callActorFromStartSynchronousTask(recipient rec: TargetActorToCall) { print("NOTICE: Task resumed on same thread as it entered the synchronous task!") } - print("inside startSynchronously, done [thread:\(getCurrentThreadID())] @ :\(#line)") + print("inside immediate, done [thread:\(getCurrentThreadID())] @ :\(#line)") sem1.signal() } - print("after startSynchronously [thread:\(getCurrentThreadID())] @ :\(#line)") + print("after immediate [thread:\(getCurrentThreadID())] @ :\(#line)") sem2.signal() } @@ -397,14 +397,14 @@ callActorFromStartSynchronousTask(recipient: .recipientOnQueue(RecipientOnQueue( // 50: callActorFromStartSynchronousTask() -// 51: before startSynchronously [thread:0x00007000054f5000] @ :366 -// 52: inside startSynchronously [thread:0x00007000054f5000] @ :372 -// 53: inside startSynchronously, call rec.sync() [thread:0x00007000054f5000] @ :380 +// 51: before immediate [thread:0x00007000054f5000] @ :366 +// 52: inside immediate [thread:0x00007000054f5000] @ :372 +// 53: inside immediate, call rec.sync() [thread:0x00007000054f5000] @ :380 // 54: Recipient/sync(syncTaskThreadID:) Current actor thread id = 0x000070000567e000 @ :336 -// 55: inside startSynchronously, call rec.sync() done [thread:0x000070000567e000] @ :385 +// 55: inside immediate, call rec.sync() done [thread:0x000070000567e000] @ :385 // 56: Inner thread id = 0x00007000054f5000 // 57: Current thread id = 0x000070000567e000 -// 60: after startSynchronously [thread:0x00007000054f5000] @ :418 +// 60: after immediate [thread:0x00007000054f5000] @ :418 // 61: - async work on queue // 62: - async work on queue // 63: - async work on queue @@ -415,24 +415,24 @@ callActorFromStartSynchronousTask(recipient: .recipientOnQueue(RecipientOnQueue( // 69: - async work on queue // 71: Inner thread id = 0x00007000054f5000 // 72: Current thread id = 0x000070000567e000 -// 73: inside startSynchronously, done [thread:0x000070000567e000] @ :414 +// 73: inside immediate, done [thread:0x000070000567e000] @ :414 // CHECK-LABEL: callActorFromStartSynchronousTask() - actor in custom executor with its own queue // No interleaving allowed between "before" and "inside": -// CHECK: before startSynchronously [thread:[[CALLING_THREAD4:.*]]] -// CHECK-NEXT: inside startSynchronously [thread:[[CALLING_THREAD4]]] +// CHECK: before immediate [thread:[[CALLING_THREAD4:.*]]] +// CHECK-NEXT: inside immediate [thread:[[CALLING_THREAD4]]] // As we call into an actor, we must enqueue to its custom executor; // Make sure the enqueue happens as expected and only then do we give up the calling thread -// allowing the 'after startSynchronously' to run. +// allowing the 'after immediate' to run. // -// CHECK-NEXT: inside startSynchronously, call rec.sync() [thread:[[CALLING_THREAD4]]] -// CHECK: after startSynchronously +// CHECK-NEXT: inside immediate, call rec.sync() [thread:[[CALLING_THREAD4]]] +// CHECK: after immediate // CHECK-NOT: ERROR! -// CHECK: inside startSynchronously, call rec.sync() done +// CHECK: inside immediate, call rec.sync() done // CHECK-NOT: ERROR! -// CHECK: inside startSynchronously, done +// CHECK: inside immediate, done actor RecipientOnQueue: RecipientProtocol { let executor: NaiveQueueExecutor diff --git a/test/Concurrency/Runtime/startSynchronously_order.swift b/test/Concurrency/Runtime/startImmediately_order.swift similarity index 96% rename from test/Concurrency/Runtime/startSynchronously_order.swift rename to test/Concurrency/Runtime/startImmediately_order.swift index 03927dfaa7e5e..7dbd579e37afa 100644 --- a/test/Concurrency/Runtime/startSynchronously_order.swift +++ b/test/Concurrency/Runtime/startImmediately_order.swift @@ -17,7 +17,7 @@ import _Concurrency let max = 1000 func bar(x: Int, cc: CheckedContinuation) { - Task.startSynchronously { + Task.immediate { print("Task \(x) started") try! await Task.sleep(nanoseconds: 10000) if (x == max) { @@ -61,4 +61,4 @@ await withCheckedContinuation { (cc: CheckedContinuation) in // CHECK: Task 27 started // CHECK: Task 28 started // CHECK: Task 29 started -// CHECK: Task 30 started \ No newline at end of file +// CHECK: Task 30 started diff --git a/test/Concurrency/startSynchronouslyIsolation.swift b/test/Concurrency/startImmediatelyIsolation.swift similarity index 50% rename from test/Concurrency/startSynchronouslyIsolation.swift rename to test/Concurrency/startImmediatelyIsolation.swift index 8837fecdba3f1..d864285757a85 100644 --- a/test/Concurrency/startSynchronouslyIsolation.swift +++ b/test/Concurrency/startImmediatelyIsolation.swift @@ -4,37 +4,37 @@ @available(SwiftStdlib 6.2, *) func sync() -> Task { - Task.startSynchronously { + Task.immediate { return "" } } @available(SwiftStdlib 6.2, *) func async() async throws { - let t1 = Task.startSynchronously { + let t1 = Task.immediate { return "" } let _: String = await t1.value - let t2: Task = Task.startSynchronously { + let t2: Task = Task.immediate { throw CancellationError() } let _: String = try await t2.value await withTaskGroup(of: Int.self) { group in - group.startTaskSynchronously { 1 } - group.startTaskSynchronouslyUnlessCancelled { 2 } + group.addImmediateTask { 1 } + group.addImmediateTaskUnlessCancelled { 2 } } await withThrowingTaskGroup(of: Int.self) { group in - group.startTaskSynchronously { () async throws -> Int in 1 } - group.startTaskSynchronouslyUnlessCancelled { () async throws -> Int in 2 } + group.addImmediateTask { () async throws -> Int in 1 } + group.addImmediateTaskUnlessCancelled { () async throws -> Int in 2 } } await withDiscardingTaskGroup { group in - group.startTaskSynchronously { } - group.startTaskSynchronouslyUnlessCancelled { } + group.addImmediateTask { } + group.addImmediateTaskUnlessCancelled { } } try await withThrowingDiscardingTaskGroup { group in - group.startTaskSynchronously { () async throws -> Void in } - group.startTaskSynchronouslyUnlessCancelled { () async throws -> Void in } + group.addImmediateTask { () async throws -> Void in } + group.addImmediateTaskUnlessCancelled { () async throws -> Void in } } } diff --git a/test/abi/macOS/arm64/concurrency.swift b/test/abi/macOS/arm64/concurrency.swift index b75ed24239519..85942a9747bf0 100644 --- a/test/abi/macOS/arm64/concurrency.swift +++ b/test/abi/macOS/arm64/concurrency.swift @@ -389,20 +389,20 @@ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV Added: _swift_task_getCurrentTaskName -// startSynchronously -Added: _swift_task_startSynchronously -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF -Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaYAcntF -Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +// startSynchronously, immediate, addImmediateTask{UnlessCancelled} +Added: _swift_task_immediate Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ -Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKYAcntF -Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF -Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYaYAcntF -Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$sScG16addImmediateTask4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScg31addImmediateTaskUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$sScG31addImmediateTaskUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScTss5NeverORs_rlE9immediate4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScTss5Error_pRs_rlE9immediate4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScg16addImmediateTask4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$ss19DiscardingTaskGroupV012addImmediateB04name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss19DiscardingTaskGroupV012addImmediateB15UnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV012addImmediateC04name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV012addImmediateC15UnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF // isIsolatingCurrentContext Added: _swift_task_invokeSwiftIsIsolatingCurrentContext diff --git a/test/abi/macOS/x86_64/concurrency.swift b/test/abi/macOS/x86_64/concurrency.swift index cfb5cac453634..d960126b0b448 100644 --- a/test/abi/macOS/x86_64/concurrency.swift +++ b/test/abi/macOS/x86_64/concurrency.swift @@ -389,20 +389,20 @@ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV Added: _swift_task_getCurrentTaskName -// startSynchronously -Added: _swift_task_startSynchronously -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF -Added: _$sScG22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaYAcntF -Added: _$sScG37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +// startSynchronously, immediate, addImmediateTask{UnlessCancelled} +Added: _swift_task_immediate Added: _$sScTss5Error_pRs_rlE18startSynchronously4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ Added: _$sScTss5NeverORs_rlE18startSynchronously4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ -Added: _$sScg22startTaskSynchronously4name8priority9operationySSSg_ScPSgxyYaKYAcntF -Added: _$sScg37startTaskSynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF -Added: _$ss19DiscardingTaskGroupV05startB13Synchronously4name8priority9operationySSSg_ScPSgyyYaYAcntF -Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF -Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$sScG16addImmediateTask4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScg31addImmediateTaskUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$sScG31addImmediateTaskUnlessCancelled4name8priority9operationySSSg_ScPSgxyYaYAcntF +Added: _$sScTss5NeverORs_rlE9immediate4name8priority_ScTyxABGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScTss5Error_pRs_rlE9immediate4name8priority_ScTyxsAA_pGSSSg_ScPSgxyYaKYAcntFZ +Added: _$sScg16addImmediateTask4name8priority9operationySSSg_ScPSgxyYaKYAcntF +Added: _$ss19DiscardingTaskGroupV012addImmediateB04name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss19DiscardingTaskGroupV012addImmediateB15UnlessCancelled4name8priority9operationySSSg_ScPSgyyYaYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV012addImmediateC04name8priority9operationySSSg_ScPSgyyYaKYAcntF +Added: _$ss27ThrowingDiscardingTaskGroupV012addImmediateC15UnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF // isIsolatingCurrentContext Added: _swift_task_invokeSwiftIsIsolatingCurrentContext diff --git a/unittests/runtime/CompatibilityOverrideConcurrency.cpp b/unittests/runtime/CompatibilityOverrideConcurrency.cpp index 2f220bcf177ed..bd146a11644db 100644 --- a/unittests/runtime/CompatibilityOverrideConcurrency.cpp +++ b/unittests/runtime/CompatibilityOverrideConcurrency.cpp @@ -118,7 +118,7 @@ static void swift_task_startOnMainActor_override(AsyncTask* task) { } SWIFT_CC(swift) -static void swift_task_startSynchronously_override(AsyncTask* task, SerialExecutorRef targetExecutor) { +static void swift_task_immediate_override(AsyncTask* task, SerialExecutorRef targetExecutor) { Ran = true; } @@ -350,8 +350,8 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startOnMainActorImpl) { swift_task_startOnMainActor(nullptr); } -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startSynchronously) { - swift_task_startSynchronously(nullptr, SerialExecutorRef::generic()); +TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_immediately) { + swift_task_immediate(nullptr, SerialExecutorRef::generic()); } TEST_F(CompatibilityOverrideConcurrencyTest, From b2e962068d8e72df33ebcaedf48879e9ccb59b38 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 9 May 2025 23:58:05 -0700 Subject: [PATCH 5/5] [stdlib] Temporarily bring back `Task.startSynchronously` as deprecated (cherry picked from commit 99d810aa7dcd088985623e91b7da170b2d3e5213) --- stdlib/public/Concurrency/Task+immediate.swift.gyb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stdlib/public/Concurrency/Task+immediate.swift.gyb b/stdlib/public/Concurrency/Task+immediate.swift.gyb index 2e3bf94574fcc..6381db9c968a5 100644 --- a/stdlib/public/Concurrency/Task+immediate.swift.gyb +++ b/stdlib/public/Concurrency/Task+immediate.swift.gyb @@ -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 { + 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.