Skip to content

Commit 9b2392b

Browse files
committed
Hollow out swift_asyncLet_startImpl to just a swift_task_create call.
Introduce a task option record to capture the async-let storage, so that `swift_task_create` can perform the appropriate initialization.
1 parent 127a851 commit 9b2392b

File tree

6 files changed

+61
-47
lines changed

6 files changed

+61
-47
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,7 +2030,7 @@ class TaskCreateFlags : public FlagSet<size_t> {
20302030
Priority_width = 8,
20312031

20322032
Task_IsChildTask = 8,
2033-
Task_IsAsyncLetTask = 9,
2033+
// bit 9 is unused
20342034
Task_CopyTaskLocals = 10,
20352035
Task_InheritContext = 11,
20362036
Task_EnqueueJob = 12,
@@ -2045,9 +2045,6 @@ class TaskCreateFlags : public FlagSet<size_t> {
20452045
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask,
20462046
isChildTask,
20472047
setIsChildTask)
2048-
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsAsyncLetTask,
2049-
isAsyncLetTask,
2050-
setIsAsyncLetTask)
20512048
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_CopyTaskLocals,
20522049
copyTaskLocals,
20532050
setCopyTaskLocals)
@@ -2148,6 +2145,8 @@ enum class TaskOptionRecordKind : uint8_t {
21482145
Executor = 0,
21492146
/// Request a child task to be part of a specific task group.
21502147
TaskGroup = 1,
2148+
/// Request a child task for an 'async let'.
2149+
AsyncLet = 2,
21512150
};
21522151

21532152
/// Flags for cancellation records.

include/swift/ABI/TaskOptions.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,24 @@ class ExecutorTaskOptionRecord : public TaskOptionRecord {
9898
}
9999
};
100100

101+
/// Task option to specify that the created task is for an 'async let'.
102+
class AsyncLetTaskOptionRecord : public TaskOptionRecord {
103+
AsyncLet *asyncLet;
104+
105+
public:
106+
AsyncLetTaskOptionRecord(AsyncLet *asyncLet)
107+
: TaskOptionRecord(TaskOptionRecordKind::AsyncLet),
108+
asyncLet(asyncLet) {}
109+
110+
AsyncLet *getAsyncLet() const {
111+
return asyncLet;
112+
}
113+
114+
static bool classof(const TaskOptionRecord *record) {
115+
return record->getKind() == TaskOptionRecordKind::AsyncLet;
116+
}
117+
};
118+
101119
} // end namespace swift
102120

103121
#endif

stdlib/public/Concurrency/AsyncLet.cpp

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616

1717
#include "../CompatibilityOverride/CompatibilityOverride.h"
1818
#include "swift/Runtime/Concurrency.h"
19-
#include "swift/ABI/Task.h"
2019
#include "swift/ABI/AsyncLet.h"
2120
#include "swift/ABI/Metadata.h"
21+
#include "swift/ABI/Task.h"
22+
#include "swift/ABI/TaskOptions.h"
2223
#include "swift/Runtime/Mutex.h"
2324
#include "swift/Runtime/HeapObject.h"
2425
#include "TaskPrivate.h"
@@ -86,6 +87,16 @@ static AsyncLetImpl *asImpl(const AsyncLet *alet) {
8687
const_cast<AsyncLet*>(alet));
8788
}
8889

90+
void swift::asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet) {
91+
AsyncLetImpl *impl = new (asyncLet) AsyncLetImpl(task);
92+
93+
auto record = impl->getTaskRecord();
94+
assert(impl == record && "the async-let IS the task record");
95+
96+
// ok, now that the group actually is initialized: attach it to the task
97+
swift_task_addStatusRecord(record);
98+
}
99+
89100
// =============================================================================
90101
// ==== start ------------------------------------------------------------------
91102

@@ -95,35 +106,17 @@ static void swift_asyncLet_startImpl(AsyncLet *alet,
95106
const Metadata *futureResultType,
96107
void *closureEntryPoint,
97108
HeapObject *closureContext) {
98-
AsyncTask *parent = swift_task_getCurrent();
99-
assert(parent && "async-let cannot be created without parent task");
100-
101109
auto flags = TaskCreateFlags();
102-
flags.setPriority(parent->getPriority());
103-
flags.setIsChildTask(true);
104-
flags.setIsAsyncLetTask(true);
110+
flags.setEnqueueJob(true);
111+
112+
AsyncLetTaskOptionRecord asyncLetOptionRecord(alet);
113+
asyncLetOptionRecord.Parent = options;
105114

106-
auto childTaskAndContext = swift_task_create(
115+
swift_task_create(
107116
flags.getOpaqueValue(),
108-
options,
117+
&asyncLetOptionRecord,
109118
futureResultType,
110119
closureEntryPoint, closureContext);
111-
112-
AsyncTask *childTask = childTaskAndContext.Task;
113-
114-
assert(childTask->isFuture());
115-
assert(childTask->hasChildFragment());
116-
AsyncLetImpl *impl = new (alet) AsyncLetImpl(childTask);
117-
118-
auto record = impl->getTaskRecord();
119-
assert(impl == record && "the async-let IS the task record");
120-
121-
// ok, now that the group actually is initialized: attach it to the task
122-
swift_task_addStatusRecord(record);
123-
124-
// schedule the task
125-
// TODO: use the executor that may have been suggested in options
126-
swift_task_enqueueGlobal(childTask);
127120
}
128121

129122
// =============================================================================

stdlib/public/Concurrency/Task.cpp

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,6 @@ task_future_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) {
393393
}
394394

395395
/// Implementation of task creation.
396-
///
397-
/// If \p isAsyncLetTask is true, the \p closureContext is not heap allocated,
398-
/// but stack-allocated (and must not be ref-counted).
399-
/// Also, async-let tasks are not heap allocated, but allocated with the parent
400-
/// task's stack allocator.
401396
SWIFT_CC(swift)
402397
static AsyncTaskAndContext swift_task_create_commonImpl(
403398
size_t rawTaskCreateFlags,
@@ -409,15 +404,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
409404

410405
// Propagate task-creation flags to job flags as appropriate.
411406
JobFlags jobFlags(JobKind::Task, taskCreateFlags.getPriority());
412-
413-
bool isAsyncLetTask = taskCreateFlags.isAsyncLetTask();
414-
if (isAsyncLetTask) {
415-
jobFlags.task_setIsAsyncLetTask(true);
416-
jobFlags.task_setIsChildTask(true);
417-
} else {
418-
jobFlags.task_setIsChildTask(taskCreateFlags.isChildTask());
419-
}
420-
407+
jobFlags.task_setIsChildTask(taskCreateFlags.isChildTask());
421408
if (futureResultType) {
422409
jobFlags.task_setIsFuture(true);
423410
assert(initialContextSize >= sizeof(FutureAsyncContext));
@@ -426,6 +413,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
426413
// Collect the options we know about.
427414
ExecutorRef executor = ExecutorRef::generic();
428415
TaskGroup *group = nullptr;
416+
AsyncLet *asyncLet = nullptr;
429417
for (auto option = options; option; option = option->getParent()) {
430418
switch (option->getKind()) {
431419
case TaskOptionRecordKind::Executor:
@@ -437,6 +425,13 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
437425
assert(group && "Missing group");
438426
jobFlags.task_setIsGroupChildTask(true);
439427
break;
428+
429+
case TaskOptionRecordKind::AsyncLet:
430+
asyncLet = cast<AsyncLetTaskOptionRecord>(option)->getAsyncLet();
431+
assert(asyncLet && "Missing async let storage");
432+
jobFlags.task_setIsAsyncLetTask(true);
433+
jobFlags.task_setIsChildTask(true);
434+
break;
440435
}
441436
}
442437

@@ -484,7 +479,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
484479
constexpr unsigned initialSlabSize = 512;
485480

486481
void *allocation = nullptr;
487-
if (isAsyncLetTask) {
482+
if (asyncLet) {
488483
assert(parent);
489484
allocation = _swift_task_alloc_specific(parent,
490485
amountToAllocate + initialSlabSize);
@@ -530,7 +525,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
530525
// Initialize the task so that resuming it will run the given
531526
// function on the initial context.
532527
AsyncTask *task = nullptr;
533-
if (isAsyncLetTask) {
528+
if (asyncLet) {
534529
// Initialize the refcount bits to "immortal", so that
535530
// ARC operations don't have any effect on the task.
536531
task = new(allocation) AsyncTask(&taskHeapMetadata,
@@ -574,7 +569,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
574569
#endif
575570

576571
// Initialize the task-local allocator.
577-
if (isAsyncLetTask) {
572+
if (asyncLet) {
578573
initialContext->ResumeParent = reinterpret_cast<TaskContinuationFunction *>(
579574
&completeTask);
580575
assert(parent);
@@ -620,6 +615,11 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
620615
swift_task_localsCopyTo(task);
621616
}
622617

618+
// Push the async let task status record.
619+
if (asyncLet) {
620+
asyncLet_addImpl(task, asyncLet);
621+
}
622+
623623
// If we're supposed to enqueue the task, do so now.
624624
if (taskCreateFlags.enqueueJob()) {
625625
swift_retain(task);

stdlib/public/Concurrency/Task.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,8 +858,9 @@ extension UnsafeCurrentTask: Equatable {
858858
struct TaskOptionRecord {
859859
// The kind of option record.
860860
enum Kind: UInt8 {
861-
case executor = 0
861+
case executor = 0
862862
case taskGroup = 1
863+
case asyncLet = 2
863864
}
864865

865866
// Flags stored within an option record.

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ void _swift_task_dealloc_specific(AsyncTask *task, void *ptr);
4747
/// related to the active task.
4848
void runJobInEstablishedExecutorContext(Job *job);
4949

50+
/// Initialize the async let storage for the given async-let child task.
51+
void asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet);
52+
5053
/// Clear the active task reference for the current thread.
5154
AsyncTask *_swift_task_clearCurrent();
5255

0 commit comments

Comments
 (0)