Skip to content

Commit 95e3883

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 e94c827 commit 95e3883

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
@@ -398,11 +398,6 @@ task_future_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) {
398398
}
399399

400400
/// Implementation of task creation.
401-
///
402-
/// If \p isAsyncLetTask is true, the \p closureContext is not heap allocated,
403-
/// but stack-allocated (and must not be ref-counted).
404-
/// Also, async-let tasks are not heap allocated, but allocated with the parent
405-
/// task's stack allocator.
406401
SWIFT_CC(swift)
407402
static AsyncTaskAndContext swift_task_create_commonImpl(
408403
size_t rawTaskCreateFlags,
@@ -414,15 +409,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
414409

415410
// Propagate task-creation flags to job flags as appropriate.
416411
JobFlags jobFlags(JobKind::Task, taskCreateFlags.getPriority());
417-
418-
bool isAsyncLetTask = taskCreateFlags.isAsyncLetTask();
419-
if (isAsyncLetTask) {
420-
jobFlags.task_setIsAsyncLetTask(true);
421-
jobFlags.task_setIsChildTask(true);
422-
} else {
423-
jobFlags.task_setIsChildTask(taskCreateFlags.isChildTask());
424-
}
425-
412+
jobFlags.task_setIsChildTask(taskCreateFlags.isChildTask());
426413
if (futureResultType) {
427414
jobFlags.task_setIsFuture(true);
428415
assert(initialContextSize >= sizeof(FutureAsyncContext));
@@ -431,6 +418,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
431418
// Collect the options we know about.
432419
ExecutorRef executor = ExecutorRef::generic();
433420
TaskGroup *group = nullptr;
421+
AsyncLet *asyncLet = nullptr;
434422
for (auto option = options; option; option = option->getParent()) {
435423
switch (option->getKind()) {
436424
case TaskOptionRecordKind::Executor:
@@ -442,6 +430,13 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
442430
assert(group && "Missing group");
443431
jobFlags.task_setIsGroupChildTask(true);
444432
break;
433+
434+
case TaskOptionRecordKind::AsyncLet:
435+
asyncLet = cast<AsyncLetTaskOptionRecord>(option)->getAsyncLet();
436+
assert(asyncLet && "Missing async let storage");
437+
jobFlags.task_setIsAsyncLetTask(true);
438+
jobFlags.task_setIsChildTask(true);
439+
break;
445440
}
446441
}
447442

@@ -489,7 +484,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
489484
constexpr unsigned initialSlabSize = 512;
490485

491486
void *allocation = nullptr;
492-
if (isAsyncLetTask) {
487+
if (asyncLet) {
493488
assert(parent);
494489
allocation = _swift_task_alloc_specific(parent,
495490
amountToAllocate + initialSlabSize);
@@ -536,7 +531,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
536531
// Initialize the task so that resuming it will run the given
537532
// function on the initial context.
538533
AsyncTask *task = nullptr;
539-
if (isAsyncLetTask) {
534+
if (asyncLet) {
540535
// Initialize the refcount bits to "immortal", so that
541536
// ARC operations don't have any effect on the task.
542537
task = new(allocation) AsyncTask(&taskHeapMetadata,
@@ -580,7 +575,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
580575
#endif
581576

582577
// Initialize the task-local allocator.
583-
if (isAsyncLetTask) {
578+
if (asyncLet) {
584579
initialContext->ResumeParent = reinterpret_cast<TaskContinuationFunction *>(
585580
&completeTask);
586581
assert(parent);
@@ -627,6 +622,11 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
627622
swift_task_localsCopyTo(task);
628623
}
629624

625+
// Push the async let task status record.
626+
if (asyncLet) {
627+
asyncLet_addImpl(task, asyncLet);
628+
}
629+
630630
// If we're supposed to enqueue the task, do so now.
631631
if (taskCreateFlags.enqueueJob()) {
632632
swift_retain(task);

stdlib/public/Concurrency/Task.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,8 +794,9 @@ extension UnsafeCurrentTask: Equatable {
794794
struct TaskOptionRecord {
795795
// The kind of option record.
796796
enum Kind: UInt8 {
797-
case executor = 0
797+
case executor = 0
798798
case taskGroup = 1
799+
case asyncLet = 2
799800
}
800801

801802
// 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
@@ -74,6 +74,9 @@ void _swift_task_dealloc_specific(AsyncTask *task, void *ptr);
7474
/// related to the active task.
7575
void runJobInEstablishedExecutorContext(Job *job);
7676

77+
/// Initialize the async let storage for the given async-let child task.
78+
void asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet);
79+
7780
/// Clear the active task reference for the current thread.
7881
AsyncTask *_swift_task_clearCurrent();
7982

0 commit comments

Comments
 (0)