Skip to content

Commit 828f589

Browse files
ktosorjmccall
andauthored
Initial Task Executor implementation Task(on:), addTask(on:) etc. (#68793)
Co-authored-by: John McCall <rjmccall@gmail.com>
1 parent 7a3e3ae commit 828f589

File tree

68 files changed

+2680
-277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+2680
-277
lines changed

include/swift/ABI/Executor.h

+8
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ class TaskExecutorRef {
208208

209209
public:
210210

211+
// Only public for CompatibilityOverrideConcurrency stubs.
212+
// Prefer `TaskExecutorRef::undefined` instead.
213+
explicit TaskExecutorRef() : Identity(nullptr), Implementation(0) {
214+
assert(isUndefined());
215+
}
216+
211217
constexpr static TaskExecutorRef undefined() {
212218
return TaskExecutorRef(nullptr, 0);
213219
}
@@ -232,6 +238,8 @@ class TaskExecutorRef {
232238
return Identity == 0;
233239
}
234240

241+
bool isDefined() const { return !isUndefined(); }
242+
235243
TaskExecutorKind getExecutorKind() const {
236244
return static_cast<TaskExecutorKind>(Implementation & ~WitnessTableMask);
237245
}

include/swift/ABI/MetadataValues.h

+16-6
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,7 @@ class TaskCreateFlags : public FlagSet<size_t> {
24372437
/// Flags for schedulable jobs.
24382438
class JobFlags : public FlagSet<uint32_t> {
24392439
public:
2440+
// clang-format off
24402441
enum {
24412442
Kind = 0,
24422443
Kind_width = 8,
@@ -2448,12 +2449,14 @@ class JobFlags : public FlagSet<uint32_t> {
24482449

24492450
// Kind-specific flags.
24502451

2451-
Task_IsChildTask = 24,
2452-
Task_IsFuture = 25,
2453-
Task_IsGroupChildTask = 26,
2452+
Task_IsChildTask = 24,
2453+
Task_IsFuture = 25,
2454+
Task_IsGroupChildTask = 26,
24542455
// 27 is currently unused
2455-
Task_IsAsyncLetTask = 28,
2456+
Task_IsAsyncLetTask = 28,
2457+
Task_HasInitialTaskExecutorPreference = 29,
24562458
};
2459+
// clang-format on
24572460

24582461
explicit JobFlags(uint32_t bits) : FlagSet(bits) {}
24592462
JobFlags(JobKind kind) { setKind(kind); }
@@ -2485,6 +2488,9 @@ class JobFlags : public FlagSet<uint32_t> {
24852488
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsAsyncLetTask,
24862489
task_isAsyncLetTask,
24872490
task_setIsAsyncLetTask)
2491+
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_HasInitialTaskExecutorPreference,
2492+
task_hasInitialTaskExecutorPreference,
2493+
task_setHasInitialTaskExecutorPreference)
24882494
};
24892495

24902496
/// Kinds of task status record.
@@ -2510,6 +2516,10 @@ enum class TaskStatusRecordKind : uint8_t {
25102516
/// escalated.
25112517
EscalationNotification = 4,
25122518

2519+
/// A task executor preference, which may impact what executor a task will be
2520+
/// enqueued on.
2521+
TaskExecutorPreference = 5,
2522+
25132523
// Kinds >= 192 are private to the implementation.
25142524
First_Reserved = 192,
25152525
Private_RecordLock = 192
@@ -2518,12 +2528,12 @@ enum class TaskStatusRecordKind : uint8_t {
25182528
/// Kinds of option records that can be passed to creating asynchronous tasks.
25192529
enum class TaskOptionRecordKind : uint8_t {
25202530
/// Request a task to be kicked off, or resumed, on a specific executor.
2521-
Executor = 0,
2531+
InitialTaskExecutor = 0,
25222532
/// Request a child task to be part of a specific task group.
25232533
TaskGroup = 1,
25242534
/// DEPRECATED. AsyncLetWithBuffer is used instead.
25252535
/// Request a child task for an 'async let'.
2526-
AsyncLet = 2,
2536+
AsyncLet = 2,
25272537
/// Request a child task for an 'async let'.
25282538
AsyncLetWithBuffer = 3,
25292539
/// Information about the result type of the task, used in embedded Swift.

include/swift/ABI/Task.h

+46-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct OpaqueValue;
3838
struct SwiftError;
3939
class TaskStatusRecord;
4040
class TaskDependencyStatusRecord;
41+
class TaskExecutorPreferenceStatusRecord;
4142
class TaskOptionRecord;
4243
class TaskGroup;
4344
class ContinuationAsyncContext;
@@ -399,7 +400,7 @@ class AsyncTask : public Job {
399400

400401
public:
401402
/// Flag that the task is to be enqueued on the provided executor and actually
402-
/// enqueue it
403+
/// enqueue it.
403404
void flagAsAndEnqueueOnExecutor(SerialExecutorRef newExecutor);
404405

405406
/// Flag that this task is now completed. This normally does not do anything
@@ -410,6 +411,30 @@ class AsyncTask : public Job {
410411
/// Checking this is, of course, inherently race-prone on its own.
411412
bool isCancelled() const;
412413

414+
// ==== Task Executor Preference ---------------------------------------------
415+
416+
/// Get the preferred task executor reference if there is one set for this
417+
/// task.
418+
TaskExecutorRef getPreferredTaskExecutor();
419+
420+
/// WARNING: Only to be used during task creation, in other situations prefer
421+
/// to use `swift_task_pushTaskExecutorPreference` and
422+
/// `swift_task_popTaskExecutorPreference`.
423+
void pushInitialTaskExecutorPreference(TaskExecutorRef preferred);
424+
425+
/// WARNING: Only to be used during task completion (destroy).
426+
///
427+
/// This is because between task creation and its destory, we cannot carry the
428+
/// exact record to `pop(record)`, and instead assume that there will be
429+
/// exactly one record remaining -- the "initial" record (added during
430+
/// creating the task), and it must be that record that is removed by this
431+
/// api.
432+
///
433+
/// All other situations from user code should be using the
434+
/// `swift_task_pushTaskExecutorPreference`, and
435+
/// `swift_task_popTaskExecutorPreference(record)` method pair.
436+
void dropInitialTaskExecutorPreferenceRecord();
437+
413438
// ==== Task Local Values ----------------------------------------------------
414439

415440
void localValuePush(const HeapObject *key,
@@ -513,6 +538,26 @@ class AsyncTask : public Job {
513538
return reinterpret_cast<GroupChildFragment *>(offset);
514539
}
515540

541+
// ==== Task Executor Preference --------------------------------------------
542+
543+
/// Returns true if the task has a task executor preference set,
544+
/// specifically at creation time of the task. This may be from
545+
/// inheriting the preference from a parent task, or by explicitly
546+
/// setting it during creation (`Task(_on:...)`).
547+
///
548+
/// This means that during task tear down the record should be deallocated
549+
/// because it will not be taken care of by a paired "pop" as the normal
550+
/// user-land "push / pop" pair of setting a task executor preference would
551+
/// have been.
552+
bool hasInitialTaskExecutorPreferenceRecord() const {
553+
return Flags.task_hasInitialTaskExecutorPreference();
554+
}
555+
556+
/// Returns true if the current task has any task preference record,
557+
/// including if it has an initial task preference record or onces
558+
/// set during the lifetime of the task.
559+
bool hasTaskExecutorPreferenceRecord() const;
560+
516561
// ==== Future ---------------------------------------------------------------
517562

518563
class FutureFragment {

include/swift/ABI/TaskOptions.h

+12-13
Original file line numberDiff line numberDiff line change
@@ -75,26 +75,25 @@ class TaskGroupTaskOptionRecord : public TaskOptionRecord {
7575
}
7676
};
7777

78-
7978
/// Task option to specify on what executor the task should be executed.
8079
///
81-
/// Not passing this option implies that a "best guess" or good default
82-
/// executor should be used instead, most often this may mean the global
83-
/// concurrent executor, or the enclosing actor's executor.
84-
class ExecutorTaskOptionRecord : public TaskOptionRecord {
85-
const SerialExecutorRef Executor;
80+
/// Not passing this option implies that an inferred (e.g. surrounding actor
81+
/// when we inherit execution context) or the default executor should be used.
82+
///
83+
/// Lack of this option usually means that the global concurrent executor, or
84+
/// the executor of the enclosing actor will be used.
85+
class InitialTaskExecutorPreferenceTaskOptionRecord : public TaskOptionRecord {
86+
const TaskExecutorRef Executor;
8687

8788
public:
88-
ExecutorTaskOptionRecord(SerialExecutorRef executor)
89-
: TaskOptionRecord(TaskOptionRecordKind::Executor),
90-
Executor(executor) {}
89+
InitialTaskExecutorPreferenceTaskOptionRecord(TaskExecutorRef executor)
90+
: TaskOptionRecord(TaskOptionRecordKind::InitialTaskExecutor),
91+
Executor(executor) {}
9192

92-
SerialExecutorRef getExecutor() const {
93-
return Executor;
94-
}
93+
TaskExecutorRef getExecutorRef() const { return Executor; }
9594

9695
static bool classof(const TaskOptionRecord *record) {
97-
return record->getKind() == TaskOptionRecordKind::Executor;
96+
return record->getKind() == TaskOptionRecordKind::InitialTaskExecutor;
9897
}
9998
};
10099

include/swift/ABI/TaskStatus.h

+23
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,29 @@ class EscalationNotificationStatusRecord : public TaskStatusRecord {
276276
}
277277
};
278278

279+
/// This record signifies that the task has an executor preference.
280+
/// This preference may be added or removed at runtime, e.g. when multiple
281+
/// `_withTaskExecutor { ... }` blocks are nested, they add more executor
282+
/// preferences.
283+
///
284+
/// Any number of these preferences may be present at runtime, and the
285+
/// innermost preference takes priority.
286+
class TaskExecutorPreferenceStatusRecord : public TaskStatusRecord {
287+
private:
288+
const TaskExecutorRef Preferred;
289+
290+
public:
291+
TaskExecutorPreferenceStatusRecord(TaskExecutorRef executor)
292+
: TaskStatusRecord(TaskStatusRecordKind::TaskExecutorPreference),
293+
Preferred(executor) {}
294+
295+
TaskExecutorRef getPreferredExecutor() { return Preferred; }
296+
297+
static bool classof(const TaskStatusRecord *record) {
298+
return record->getKind() == TaskStatusRecordKind::TaskExecutorPreference;
299+
}
300+
};
301+
279302
// This record is allocated for a task to record what it is dependent on before
280303
// the task can make progress again.
281304
class TaskDependencyStatusRecord : public TaskStatusRecord {

include/swift/AST/ASTContext.h

+3
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,9 @@ class ASTContext final {
916916
/// Get the runtime availability of support for concurrency.
917917
AvailabilityContext getConcurrencyAvailability();
918918

919+
/// Get the runtime availability of task executors.
920+
AvailabilityContext getTaskExecutorAvailability();
921+
919922
/// Get the runtime availability of the `DiscardingTaskGroup`,
920923
/// and supporting runtime functions function
921924
AvailabilityContext getConcurrencyDiscardingTaskGroupAvailability();

include/swift/AST/ASTSynthesis.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ enum SingletonTypeSynthesizer {
4141
_any,
4242
_bridgeObject,
4343
_error,
44-
_executor,
44+
_executor, // the 'BuiltinExecutor' type
4545
_job,
4646
_nativeObject,
4747
_never,
4848
_rawPointer,
4949
_rawUnsafeContinuation,
5050
_void,
5151
_word,
52-
_serialExecutor,
53-
_taskExecutor,
52+
_serialExecutor, // the '_Concurrency.SerialExecutor' protocol
53+
_taskExecutor, // the '_Concurrency._TaskExecutor' protocol
5454
};
5555
inline Type synthesizeType(SynthesisContext &SC,
5656
SingletonTypeSynthesizer kind) {
@@ -71,7 +71,7 @@ inline Type synthesizeType(SynthesisContext &SC,
7171
return SC.Context.getProtocol(KnownProtocolKind::SerialExecutor)
7272
->getDeclaredInterfaceType();
7373
case _taskExecutor:
74-
return SC.Context.getProtocol(KnownProtocolKind::TaskExecutor)
74+
return SC.Context.getProtocol(KnownProtocolKind::_TaskExecutor)
7575
->getDeclaredInterfaceType();
7676
}
7777
}

include/swift/AST/Builtins.def

+28
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,34 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTask,
986986
BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTaskInGroup,
987987
"createAsyncTaskInGroup", "", Special)
988988

989+
/// createAsyncTaskWithExecutor(): (
990+
/// Int, // flags
991+
/// Builtin.Executor, // executor
992+
/// @escaping () async throws -> T // function
993+
/// ) -> Builtin.NativeObject
994+
///
995+
/// Create a new asynchronous task future, given flags, a parent task,
996+
/// task group and a function to execute.
997+
BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTaskWithExecutor,
998+
"createAsyncTaskWithExecutor", "", Special)
999+
1000+
/// createAsyncTaskWithExecutor(): (
1001+
/// Int, // flags
1002+
/// Builtin.RawPointer, // group
1003+
/// Builtin.Executor, // executor
1004+
/// @escaping () async throws -> T // function
1005+
/// ) -> Builtin.NativeObject
1006+
///
1007+
/// Create a new asynchronous task future, given flags, a parent task,
1008+
/// task group and a function to execute.
1009+
BUILTIN_MISC_OPERATION_WITH_SILGEN(CreateAsyncTaskInGroupWithExecutor,
1010+
"createAsyncTaskInGroupWithExecutor", "", Special)
1011+
1012+
/// Build a Builtin.Executor value from an "ordinary" task executor
1013+
/// reference.
1014+
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildOrdinaryTaskExecutorRef,
1015+
"buildOrdinaryTaskExecutorRef", "n", Special)
1016+
9891017
/// globalStringTablePointer has type String -> Builtin.RawPointer.
9901018
/// It returns an immortal, global string table pointer for strings constructed
9911019
/// from string literals. We consider it effects as readnone meaning that it

include/swift/AST/KnownProtocols.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ PROTOCOL(FixedWidthInteger)
8686
PROTOCOL(RangeReplaceableCollection)
8787
PROTOCOL(Executor)
8888
PROTOCOL(SerialExecutor)
89-
PROTOCOL(TaskExecutor)
89+
PROTOCOL(_TaskExecutor)
9090
PROTOCOL(GlobalActor)
9191

9292
PROTOCOL_(BridgedNSError)

include/swift/AST/KnownSDKTypes.def

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
4040
KNOWN_SDK_TYPE_DECL(Concurrency, CheckedContinuation, NominalTypeDecl, 2)
4141
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
4242
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
43-
KNOWN_SDK_TYPE_DECL(Concurrency, Job, StructDecl, 0) // TODO: remove in favor of ExecutorJob
43+
KNOWN_SDK_TYPE_DECL(Concurrency, Job, StructDecl, 0) // legacy type; prefer ExecutorJob
4444
KNOWN_SDK_TYPE_DECL(Concurrency, ExecutorJob, StructDecl, 0)
4545
KNOWN_SDK_TYPE_DECL(Concurrency, UnownedJob, StructDecl, 0)
4646
KNOWN_SDK_TYPE_DECL(Concurrency, Executor, NominalTypeDecl, 0)
47-
KNOWN_SDK_TYPE_DECL(Concurrency, TaskExecutor, NominalTypeDecl, 0)
47+
KNOWN_SDK_TYPE_DECL(Concurrency, _TaskExecutor, NominalTypeDecl, 0)
4848
KNOWN_SDK_TYPE_DECL(Concurrency, SerialExecutor, NominalTypeDecl, 0)
4949
KNOWN_SDK_TYPE_DECL(Concurrency, UnownedSerialExecutor, NominalTypeDecl, 0)
5050

include/swift/Basic/Features.def

+4-1
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,13 @@ LANGUAGE_FEATURE(BuiltinHopToActor, 0, "Builtin.HopToActor", true)
8282
LANGUAGE_FEATURE(BuiltinTaskGroupWithArgument, 0, "TaskGroup builtins", true)
8383
LANGUAGE_FEATURE(InheritActorContext, 0, "@_inheritActorContext attribute", true)
8484
LANGUAGE_FEATURE(ImplicitSelfCapture, 0, "@_implicitSelfCapture attribute", true)
85+
LANGUAGE_FEATURE(BuiltinBuildTaskExecutor, 0, "TaskExecutor-building builtins", true)
8586
LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true)
8687
LANGUAGE_FEATURE(BuiltinBuildComplexEqualityExecutor, 0, "Executor-building for 'complexEquality executor' builtins", true)
8788
LANGUAGE_FEATURE(BuiltinBuildMainExecutor, 0, "MainActor executor building builtin", true)
88-
LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "MainActor executor building builtin", true)
89+
LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "Task create in task group builtin with extra flags", true)
90+
LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroupWithExecutor, 0, "Task create in task group builtin with extra flags", true)
91+
LANGUAGE_FEATURE(BuiltinCreateAsyncTaskWithExecutor, 0, "Task create builtin with extra executor preference", true)
8992
LANGUAGE_FEATURE(BuiltinCopy, 0, "Builtin.copy()", true)
9093
LANGUAGE_FEATURE(BuiltinStackAlloc, 0, "Builtin.stackAlloc", true)
9194
LANGUAGE_FEATURE(BuiltinUnprotectedStackAlloc, 0, "Builtin.unprotectedStackAlloc", true)

include/swift/Demangling/StandardTypesMangling.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ STANDARD_TYPE_CONCURRENCY(Protocol, F, Executor)
8888
STANDARD_TYPE_CONCURRENCY(Protocol, f, SerialExecutor)
8989
STANDARD_TYPE_CONCURRENCY(Structure, G, TaskGroup)
9090
STANDARD_TYPE_CONCURRENCY(Structure, g, ThrowingTaskGroup)
91-
STANDARD_TYPE_CONCURRENCY(Protocol, h, TaskExecutor)
91+
STANDARD_TYPE_CONCURRENCY(Protocol, h, _TaskExecutor)
9292
STANDARD_TYPE_CONCURRENCY(Protocol, I, AsyncIteratorProtocol)
9393
STANDARD_TYPE_CONCURRENCY(Protocol, i, AsyncSequence)
9494
STANDARD_TYPE_CONCURRENCY(Structure, J, UnownedJob)

0 commit comments

Comments
 (0)