Skip to content

Commit f55964d

Browse files
authored
[Concurrency] Initial steps for startSynchronously for Task (#79608)
* [Concurrency] Initial steps for startSynchronously for Task * [Concurrency] Rename to _startSynchronously while in development * [Concurrency] StartSynchronously special executor to avoid switching * startSynchronously bring back more info output * [Concurrency] startSynchronously with more custom executor tests * add missing ABI additions to test for x86 * [Concurrency] gyb generate _startSynchronously * [Concurrency] %import dispatch for Linux startSynchronously test * [Concurrency] Add TaskGroup.startTaskSynchronously funcs * [Concurrency] DispatchSerialQueue does not exist on linux still
1 parent af3e7e7 commit f55964d

23 files changed

+869
-105
lines changed

Runtimes/Core/Concurrency/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
add_subdirectory(InternalShims)
22

3+
gyb_expand(Task+startSynchronously.swift.gyb Task+startSynchronously.swift)
4+
35
add_library(swift_Concurrency
46
Actor.cpp
57
AsyncLet.cpp
@@ -80,7 +82,9 @@ add_library(swift_Concurrency
8082
TaskGroup+TaskExecutor.swift
8183
TaskLocal.swift
8284
TaskSleep.swift
83-
TaskSleepDuration.swift)
85+
TaskSleepDuration.swift
86+
"${CMAKE_CURRENT_BINARY_DIR}/Task+startSynchronously.swift")
87+
8488
include(${SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR}.cmake)
8589
target_compile_definitions(swift_Concurrency PRIVATE
8690
$<$<COMPILE_LANGUAGE:C,CXX>:-DSWIFT_TARGET_LIBRARY_NAME=swift_Concurrency>

include/swift/ABI/Executor.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ class SerialExecutorRef {
7777
/// Executor that may need to participate in complex "same context" checks,
7878
/// by invoking `isSameExclusiveExecutionContext` when comparing execution contexts.
7979
ComplexEquality = 0b01,
80+
/// Mark this executor as the one used by `Task.startSynchronously`,
81+
/// It cannot participate in switching.
82+
// TODO: Perhaps make this a generic "cannot switch" rather than start synchronously specific.
83+
StartSynchronously = 0b10,
8084
};
8185

8286
static_assert(static_cast<uintptr_t>(ExecutorKind::Ordinary) == 0);
@@ -101,6 +105,16 @@ class SerialExecutorRef {
101105
return SerialExecutorRef(actor, 0);
102106
}
103107

108+
static SerialExecutorRef forSynchronousStart() {
109+
auto wtable = reinterpret_cast<uintptr_t>(nullptr) |
110+
static_cast<uintptr_t>(ExecutorKind::StartSynchronously);
111+
return SerialExecutorRef(nullptr, wtable);
112+
}
113+
bool isForSynchronousStart() const {
114+
return getIdentity() == nullptr &&
115+
getExecutorKind() == ExecutorKind::StartSynchronously;
116+
}
117+
104118
/// Given a pointer to a serial executor and its SerialExecutor
105119
/// conformance, return an executor reference for it.
106120
static SerialExecutorRef forOrdinary(HeapObject *identity,
@@ -127,7 +141,7 @@ class SerialExecutorRef {
127141

128142
const char* getIdentityDebugName() const {
129143
return isMainExecutor() ? " (MainActorExecutor)"
130-
: isGeneric() ? " (GenericExecutor)"
144+
: isGeneric() ? (isForSynchronousStart() ? " (GenericExecutor/SynchronousStart)" : " (GenericExecutor)")
131145
: "";
132146
}
133147

include/swift/ABI/MetadataValues.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,13 +2668,13 @@ class TaskCreateFlags : public FlagSet<size_t> {
26682668
Task_EnqueueJob = 12,
26692669
Task_AddPendingGroupTaskUnconditionally = 13,
26702670
Task_IsDiscardingTask = 14,
2671-
26722671
/// The task function is consumed by calling it (@callee_owned).
26732672
/// The context pointer should be treated as opaque and non-copyable;
26742673
/// in particular, it should not be retained or released.
26752674
///
26762675
/// Supported starting in Swift 6.1.
2677-
Task_IsTaskFunctionConsumed = 15,
2676+
Task_IsTaskFunctionConsumed = 15,
2677+
Task_IsStartSynchronouslyTask = 16,
26782678
};
26792679

26802680
explicit constexpr TaskCreateFlags(size_t bits) : FlagSet(bits) {}
@@ -2707,6 +2707,9 @@ class TaskCreateFlags : public FlagSet<size_t> {
27072707
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsTaskFunctionConsumed,
27082708
isTaskFunctionConsumed,
27092709
setIsTaskFunctionConsumed)
2710+
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsStartSynchronouslyTask,
2711+
isSynchronousStartTask,
2712+
setIsSYnchronousStartTask)
27102713
};
27112714

27122715
/// Flags for schedulable jobs.

include/swift/Runtime/Concurrency.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,9 @@ JobPriority swift_task_getCurrentThreadPriority(void);
10251025
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
10261026
void swift_task_startOnMainActor(AsyncTask* job);
10271027

1028+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
1029+
void swift_task_startSynchronously(AsyncTask* job);
1030+
10281031
/// Donate this thread to the global executor until either the
10291032
/// given condition returns true or we've run out of cooperative
10301033
/// tasks to run.

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,11 @@ OVERRIDE_TASK(task_startOnMainActor, void,
438438
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
439439
swift::, (AsyncTask *task), (task))
440440

441+
// In ACTOR since we need ExecutorTracking info
442+
OVERRIDE_ACTOR(task_startSynchronously, void,
443+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
444+
swift::, (AsyncTask *task), (task))
445+
441446
#undef OVERRIDE
442447
#undef OVERRIDE_ACTOR
443448
#undef OVERRIDE_TASK

stdlib/public/Concurrency/Actor.cpp

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,8 +1683,8 @@ static void defaultActorDrain(DefaultActorImpl *actor) {
16831683
trackingInfo.setTaskExecutor(taskExecutor);
16841684
}
16851685

1686-
// This thread is now going to follow the task on this actor. It may hop off
1687-
// the actor
1686+
// This thread is now going to follow the task on this actor.
1687+
// It may hop off the actor
16881688
runJobInEstablishedExecutorContext(job);
16891689

16901690
// We could have come back from the job on a generic executor and not as
@@ -2027,7 +2027,9 @@ static void swift_job_runImpl(Job *job, SerialExecutorRef executor) {
20272027
// run jobs. Actor executors won't expect us to switch off them
20282028
// during this operation. But do allow switching if the executor
20292029
// is generic.
2030-
if (!executor.isGeneric()) trackingInfo.disallowSwitching();
2030+
if (!executor.isGeneric()) {
2031+
trackingInfo.disallowSwitching();
2032+
}
20312033

20322034
auto taskExecutor = executor.isGeneric()
20332035
? TaskExecutorRef::fromTaskExecutorPreference(job)
@@ -2149,13 +2151,19 @@ static bool canGiveUpThreadForSwitch(ExecutorTrackingInfo *trackingInfo,
21492151
assert(trackingInfo || currentExecutor.isGeneric());
21502152

21512153
// Some contexts don't allow switching at all.
2152-
if (trackingInfo && !trackingInfo->allowsSwitching())
2154+
if (trackingInfo && !trackingInfo->allowsSwitching()) {
21532155
return false;
2156+
}
21542157

21552158
// We can certainly "give up" a generic executor to try to run
21562159
// a task for an actor.
2157-
if (currentExecutor.isGeneric())
2160+
if (currentExecutor.isGeneric()) {
2161+
if (currentExecutor.isForSynchronousStart()) {
2162+
return false;
2163+
}
2164+
21582165
return true;
2166+
}
21592167

21602168
// If the current executor is a default actor, we know how to make
21612169
// it give up its thread.
@@ -2276,7 +2284,8 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
22762284
: TaskExecutorRef::undefined());
22772285
auto newTaskExecutor = task->getPreferredTaskExecutor();
22782286
SWIFT_TASK_DEBUG_LOG("Task %p trying to switch executors: executor %p%s to "
2279-
"new serial executor: %p%s; task executor: from %p%s to %p%s",
2287+
"new serial executor: %p%s; task executor: from %p%s to %p%s"
2288+
"%s",
22802289
task,
22812290
currentExecutor.getIdentity(),
22822291
currentExecutor.getIdentityDebugName(),
@@ -2285,13 +2294,15 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
22852294
currentTaskExecutor.getIdentity(),
22862295
currentTaskExecutor.isDefined() ? "" : " (undefined)",
22872296
newTaskExecutor.getIdentity(),
2288-
newTaskExecutor.isDefined() ? "" : " (undefined)");
2297+
newTaskExecutor.isDefined() ? "" : " (undefined)",
2298+
trackingInfo->isSynchronousStart() ? "[synchronous start]" : "");
22892299

22902300
// If the current executor is compatible with running the new executor,
22912301
// we can just immediately continue running with the resume function
22922302
// we were passed in.
22932303
if (!mustSwitchToRun(currentExecutor, newExecutor, currentTaskExecutor,
22942304
newTaskExecutor)) {
2305+
SWIFT_TASK_DEBUG_LOG("Task %p run inline", task);
22952306
return resumeFunction(resumeContext); // 'return' forces tail call
22962307
}
22972308

@@ -2303,6 +2314,7 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
23032314
// If the current executor can give up its thread, and the new executor
23042315
// can take over a thread, try to do so; but don't do this if we've
23052316
// been asked to yield the thread.
2317+
SWIFT_TASK_DEBUG_LOG("Task %p can give up thread?", task);
23062318
if (currentTaskExecutor.isUndefined() &&
23072319
canGiveUpThreadForSwitch(trackingInfo, currentExecutor) &&
23082320
!shouldYieldThread() &&
@@ -2325,6 +2337,27 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
23252337
task->flagAsAndEnqueueOnExecutor(newExecutor);
23262338
}
23272339

2340+
SWIFT_CC(swift)
2341+
static void swift_task_startSynchronouslyImpl(AsyncTask* task) {
2342+
swift_retain(task);
2343+
2344+
auto currentTracking = ExecutorTrackingInfo::current();
2345+
if (currentTracking) {
2346+
auto currentExecutor = currentTracking->getActiveExecutor();
2347+
AsyncTask * originalTask = _swift_task_clearCurrent();
2348+
2349+
swift_job_run(task, currentExecutor);
2350+
_swift_task_setCurrent(originalTask);
2351+
} else {
2352+
auto originalTask = ActiveTask::swap(task);
2353+
assert(!originalTask);
2354+
2355+
SerialExecutorRef executor = SerialExecutorRef::forSynchronousStart();
2356+
swift_job_run(task, executor);
2357+
_swift_task_setCurrent(originalTask);
2358+
}
2359+
}
2360+
23282361
#if !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
23292362
namespace {
23302363
/// Job that allows to use executor API to schedule a block of task-less

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
183183
${SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES}
184184
${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES}
185185

186+
GYB_SOURCES
187+
Task+startSynchronously.swift.gyb
188+
186189
SWIFT_MODULE_DEPENDS_ANDROID Android
187190
SWIFT_MODULE_DEPENDS_LINUX Glibc
188191
SWIFT_MODULE_DEPENDS_LINUX_STATIC Musl

stdlib/public/Concurrency/DiscardingTaskGroup.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,15 @@ public struct DiscardingTaskGroup {
205205
let flags = taskCreateFlags(
206206
priority: priority, isChildTask: true, copyTaskLocals: false,
207207
inheritContext: false, enqueueJob: false,
208-
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
208+
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
209+
isSynchronousStart: false
209210
)
210211
#else
211212
let flags = taskCreateFlags(
212213
priority: priority, isChildTask: true, copyTaskLocals: false,
213214
inheritContext: false, enqueueJob: true,
214-
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
215+
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
216+
isSynchronousStart: false
215217
)
216218
#endif
217219

@@ -252,13 +254,15 @@ public struct DiscardingTaskGroup {
252254
let flags = taskCreateFlags(
253255
priority: priority, isChildTask: true, copyTaskLocals: false,
254256
inheritContext: false, enqueueJob: false,
255-
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
257+
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
258+
isSynchronousStart: false
256259
)
257260
#else
258261
let flags = taskCreateFlags(
259262
priority: priority, isChildTask: true, copyTaskLocals: false,
260263
inheritContext: false, enqueueJob: true,
261-
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
264+
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
265+
isSynchronousStart: false
262266
)
263267
#endif
264268

@@ -281,7 +285,8 @@ public struct DiscardingTaskGroup {
281285
let flags = taskCreateFlags(
282286
priority: nil, isChildTask: true, copyTaskLocals: false,
283287
inheritContext: false, enqueueJob: true,
284-
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
288+
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
289+
isSynchronousStart: false
285290
)
286291

287292
// Create the task in this group.
@@ -317,7 +322,8 @@ public struct DiscardingTaskGroup {
317322
let flags = taskCreateFlags(
318323
priority: nil, isChildTask: true, copyTaskLocals: false,
319324
inheritContext: false, enqueueJob: true,
320-
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
325+
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
326+
isSynchronousStart: false
321327
)
322328

323329
// Create the task in this group.
@@ -635,7 +641,8 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
635641
let flags = taskCreateFlags(
636642
priority: priority, isChildTask: true, copyTaskLocals: false,
637643
inheritContext: false, enqueueJob: true,
638-
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
644+
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
645+
isSynchronousStart: false
639646
)
640647

641648
// Create the task in this group.
@@ -666,7 +673,8 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
666673
let flags = taskCreateFlags(
667674
priority: priority, isChildTask: true, copyTaskLocals: false,
668675
inheritContext: false, enqueueJob: true,
669-
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
676+
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
677+
isSynchronousStart: false
670678
)
671679

672680
// Create the task in this group.

stdlib/public/Concurrency/Executor.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,6 @@ internal final class DispatchQueueShim: @unchecked Sendable, SerialExecutor {
542542
}
543543
#endif // SWIFT_CONCURRENCY_USES_DISPATCH
544544

545-
546545
@available(SwiftStdlib 6.1, *)
547546
@_silgen_name("swift_task_deinitOnExecutor")
548547
@usableFromInline

stdlib/public/Concurrency/Task+TaskExecutor.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ extension Task where Failure == Never {
240240
priority: priority, isChildTask: false, copyTaskLocals: true,
241241
inheritContext: true, enqueueJob: true,
242242
addPendingGroupTaskUnconditionally: false,
243-
isDiscardingTask: false)
243+
isDiscardingTask: false, isSynchronousStart: false)
244244

245245
#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
246246
let (task, _) = Builtin.createTask(
@@ -303,7 +303,7 @@ extension Task where Failure == Error {
303303
priority: priority, isChildTask: false, copyTaskLocals: true,
304304
inheritContext: true, enqueueJob: true,
305305
addPendingGroupTaskUnconditionally: false,
306-
isDiscardingTask: false)
306+
isDiscardingTask: false, isSynchronousStart: false)
307307

308308
#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
309309
let (task, _) = Builtin.createTask(
@@ -364,7 +364,7 @@ extension Task where Failure == Never {
364364
priority: priority, isChildTask: false, copyTaskLocals: false,
365365
inheritContext: false, enqueueJob: true,
366366
addPendingGroupTaskUnconditionally: false,
367-
isDiscardingTask: false)
367+
isDiscardingTask: false, isSynchronousStart: false)
368368

369369
#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
370370
let (task, _) = Builtin.createTask(
@@ -425,7 +425,7 @@ extension Task where Failure == Error {
425425
priority: priority, isChildTask: false, copyTaskLocals: false,
426426
inheritContext: false, enqueueJob: true,
427427
addPendingGroupTaskUnconditionally: false,
428-
isDiscardingTask: false)
428+
isDiscardingTask: false, isSynchronousStart: false)
429429

430430
#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
431431
let (task, _) = Builtin.createTask(

0 commit comments

Comments
 (0)