Skip to content

Commit 1bd1c46

Browse files
committed
Concurrency: Task.startOnMainActor
This patch adds an SPI to run the first partial function of a MainActor asynchronous function on the MainActor synchronously. This is effectively like the asynchronous program entrypoint behavior. The first partial function is run synchronously. Following continuations are enqueued for execution like any other asynchronous function.
1 parent 75995f0 commit 1bd1c46

File tree

5 files changed

+66
-0
lines changed

5 files changed

+66
-0
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,9 @@ void swift_task_reportUnexpectedExecutor(
899899
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
900900
JobPriority swift_task_getCurrentThreadPriority(void);
901901

902+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
903+
void swift_task_startOnMainActor(AsyncTask* job);
904+
902905
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
903906

904907
/// Donate this thread to the global executor until either the

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ OVERRIDE_TASK_STATUS(task_escalate, JobPriority,
382382
swift::, (AsyncTask *task, JobPriority newPriority),
383383
(task, newPriority))
384384

385+
OVERRIDE_TASK(task_startOnMainActor, void,
386+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
387+
swift::, (AsyncTask *task), (task))
388+
385389
#undef OVERRIDE
386390
#undef OVERRIDE_ACTOR
387391
#undef OVERRIDE_TASK

stdlib/public/Concurrency/Task.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,17 @@ void (*swift::swift_task_asyncMainDrainQueue_hook)(
15941594
swift_task_asyncMainDrainQueue_original original,
15951595
swift_task_asyncMainDrainQueue_override compatOverride) = nullptr;
15961596

1597+
SWIFT_CC(swift)
1598+
static void swift_task_startOnMainActorImpl(AsyncTask* task) {
1599+
AsyncTask * originalTask = _swift_task_clearCurrent();
1600+
ExecutorRef mainExecutor = swift_task_getMainExecutor();
1601+
if (swift_task_getCurrentExecutor() != swift_task_getMainExecutor())
1602+
swift_Concurrency_fatalError(0, "Not on the main executor");
1603+
swift_retain(task);
1604+
swift_job_run(task, mainExecutor);
1605+
_swift_task_setCurrent(originalTask);
1606+
}
1607+
15971608
#define OVERRIDE_TASK COMPATIBILITY_OVERRIDE
15981609

15991610
#ifdef SWIFT_STDLIB_SUPPORT_BACK_DEPLOYMENT

stdlib/public/Concurrency/Task.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,42 @@ extension Task: Equatable {
186186
}
187187
}
188188

189+
@available(SwiftStdlib 5.9, *)
190+
extension Task where Failure == Error {
191+
@_spi(MainActorUtilities)
192+
@MainActor
193+
public static func startOnMainActor(
194+
priority: TaskPriority? = nil,
195+
@_inheritActorContext @_implicitSelfCapture _ work: __owned @Sendable @escaping @MainActor() async throws -> Success
196+
) -> Task<Success, Error> {
197+
let flags = taskCreateFlags(priority: priority, isChildTask: false,
198+
copyTaskLocals: true, inheritContext: true,
199+
enqueueJob: false,
200+
addPendingGroupTaskUnconditionally: false)
201+
let (task, _) = Builtin.createAsyncTask(flags, work)
202+
_startTaskOnMainActor(task)
203+
return Task<Success, Error>(task)
204+
}
205+
}
206+
207+
@available(SwiftStdlib 5.9, *)
208+
extension Task where Failure == Never {
209+
@_spi(MainActorUtilities)
210+
@MainActor
211+
public static func startOnMainActor(
212+
priority: TaskPriority? = nil,
213+
@_inheritActorContext @_implicitSelfCapture _ work: __owned @Sendable @escaping @MainActor() async -> Success
214+
) -> Task<Success, Never> {
215+
let flags = taskCreateFlags(priority: priority, isChildTask: false,
216+
copyTaskLocals: true, inheritContext: true,
217+
enqueueJob: false,
218+
addPendingGroupTaskUnconditionally: false)
219+
let (task, _) = Builtin.createAsyncTask(flags, work)
220+
_startTaskOnMainActor(task)
221+
return Task(task)
222+
}
223+
}
224+
189225
// ==== Task Priority ----------------------------------------------------------
190226

191227
/// The priority of a task.
@@ -880,6 +916,9 @@ extension UnsafeCurrentTask: Equatable {
880916
@_silgen_name("swift_task_getCurrent")
881917
func _getCurrentAsyncTask() -> Builtin.NativeObject?
882918

919+
@_silgen_name("swift_task_startOnMainActor")
920+
fileprivate func _startTaskOnMainActor(_ task: Builtin.NativeObject) -> Builtin.NativeObject?
921+
883922
@available(SwiftStdlib 5.1, *)
884923
@_silgen_name("swift_task_getJobFlags")
885924
func getJobFlags(_ task: Builtin.NativeObject) -> JobFlags

unittests/runtime/CompatibilityOverrideConcurrency.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ static void swift_task_enqueueMainExecutor_override(
9797
Ran = true;
9898
}
9999

100+
SWIFT_CC(swift)
101+
static void swift_task_startOnMainActor_override(AsyncTask* task) {
102+
Ran = true;
103+
}
104+
100105
#ifdef RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST
101106
[[noreturn]] SWIFT_CC(swift)
102107
static void swift_task_asyncMainDrainQueue_override_fn(
@@ -284,6 +289,10 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_escalate) {
284289
swift_task_escalate(nullptr, {});
285290
}
286291

292+
TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startOnMainActorImpl) {
293+
swift_task_startOnMainActor(nullptr);
294+
}
295+
287296
#if RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST
288297
TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_asyncMainDrainQueue) {
289298

0 commit comments

Comments
 (0)