Skip to content

Commit 785b182

Browse files
authored
Merge pull request #34381 from rjmccall/task-creation-api
Refine task APIs and introduce an API for task creation
2 parents 2f08835 + a06d18c commit 785b182

File tree

14 files changed

+436
-102
lines changed

14 files changed

+436
-102
lines changed

include/swift/ABI/MetadataKind.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ METADATAKIND(HeapGenericLocalVariable,
8585
METADATAKIND(ErrorObject,
8686
1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
8787

88+
/// A heap-allocated simple task.
89+
METADATAKIND(SimpleTask,
90+
2 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
91+
8892
// getEnumeratedMetadataKind assumes that all the enumerated values here
8993
// will be <= LastEnumeratedMetadataKind.
9094

include/swift/ABI/MetadataValues.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,9 +1914,8 @@ class JobFlags : public FlagSet<size_t> {
19141914

19151915
// Kind-specific flags.
19161916

1917-
Task_IsHeapObject = 24,
1918-
Task_IsChildTask = 25,
1919-
Task_IsFuture = 26
1917+
Task_IsChildTask = 24,
1918+
Task_IsFuture = 25
19201919
};
19211920

19221921
explicit JobFlags(size_t bits) : FlagSet(bits) {}
@@ -1933,9 +1932,6 @@ class JobFlags : public FlagSet<size_t> {
19331932
return getKind() == JobKind::Task;
19341933
}
19351934

1936-
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsHeapObject,
1937-
task_isHeapObject,
1938-
task_setIsHeapObject)
19391935
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask,
19401936
task_isChildTask,
19411937
task_setIsChildTask)
@@ -2034,8 +2030,8 @@ class AsyncContextFlags : public FlagSet<uint32_t> {
20342030
/// allocated as part of the caller's context, or if the callee will
20352031
/// be called multiple times.
20362032
FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate,
2037-
shouldNotDeallocateInCaller,
2038-
setShouldNotDeallocateInCaller)
2033+
shouldNotDeallocateInCallee,
2034+
setShouldNotDeallocateInCallee)
20392035
};
20402036

20412037
} // end namespace swift

include/swift/ABI/Task.h

Lines changed: 86 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_ABI_TASK_H
1818
#define SWIFT_ABI_TASK_H
1919

20+
#include "swift/Basic/RelativePointer.h"
2021
#include "swift/ABI/HeapObject.h"
2122
#include "swift/ABI/MetadataValues.h"
2223
#include "swift/Runtime/Config.h"
@@ -32,7 +33,21 @@ class TaskStatusRecord;
3233

3334
/// An ExecutorRef isn't necessarily just a pointer to an executor
3435
/// object; it may have other bits set.
35-
using ExecutorRef = Executor *;
36+
struct ExecutorRef {
37+
Executor *Pointer;
38+
39+
/// Get an executor ref that represents a lack of preference about
40+
/// where execution resumes. This is only valid in continuations,
41+
/// return contexts, and so on; it is not generally passed to
42+
/// executing functions.
43+
static ExecutorRef noPreference() {
44+
return { nullptr };
45+
}
46+
47+
bool operator==(ExecutorRef other) const {
48+
return Pointer == other.Pointer;
49+
}
50+
};
3651

3752
using JobInvokeFunction =
3853
SWIFT_CC(swift)
@@ -42,6 +57,33 @@ using TaskContinuationFunction =
4257
SWIFT_CC(swift)
4358
void (AsyncTask *, ExecutorRef, AsyncContext *);
4459

60+
template <class Fn>
61+
struct AsyncFunctionTypeImpl;
62+
template <class Result, class... Params>
63+
struct AsyncFunctionTypeImpl<Result(Params...)> {
64+
// TODO: expand and include the arguments in the parameters.
65+
using type = TaskContinuationFunction;
66+
};
67+
68+
template <class Fn>
69+
using AsyncFunctionType = typename AsyncFunctionTypeImpl<Fn>::type;
70+
71+
/// A "function pointer" for an async function.
72+
///
73+
/// Eventually, this will always be signed with the data key
74+
/// using a type-specific discriminator.
75+
template <class FnType>
76+
class AsyncFunctionPointer {
77+
public:
78+
/// The function to run.
79+
RelativeDirectPointer<AsyncFunctionType<FnType>,
80+
/*nullable*/ false,
81+
int32_t> Function;
82+
83+
/// The expected size of the context.
84+
uint32_t ExpectedContextSize;
85+
};
86+
4587
/// A schedulable job.
4688
class alignas(2 * alignof(void*)) Job {
4789
public:
@@ -76,7 +118,7 @@ class alignas(2 * alignof(void*)) Job {
76118
}
77119

78120
/// Run this job.
79-
void run(Executor *currentExecutor);
121+
void run(ExecutorRef currentExecutor);
80122
};
81123

82124
// The compiler will eventually assume these.
@@ -128,7 +170,7 @@ class ActiveTaskStatus {
128170
/// An asynchronous task. Tasks are the analogue of threads for
129171
/// asynchronous functions: that is, they are a persistent identity
130172
/// for the overall async computation.
131-
class AsyncTask : public Job {
173+
class AsyncTask : public HeapObject, public Job {
132174
public:
133175
/// The context for resuming the job. When a task is scheduled
134176
/// as a job, the next continuation should be installed as the
@@ -146,9 +188,10 @@ class AsyncTask : public Job {
146188
/// Reserved for the use of the task-local stack allocator.
147189
void *AllocatorPrivate[4];
148190

149-
AsyncTask(JobFlags flags, TaskContinuationFunction *run,
191+
AsyncTask(const HeapMetadata *metadata, JobFlags flags,
192+
TaskContinuationFunction *run,
150193
AsyncContext *initialContext)
151-
: Job(flags, run),
194+
: HeapObject(metadata), Job(flags, run),
152195
ResumeContext(initialContext),
153196
Status(ActiveTaskStatus()) {
154197
assert(flags.isAsyncTask());
@@ -164,12 +207,6 @@ class AsyncTask : public Job {
164207
return Status.load(std::memory_order_relaxed).isCancelled();
165208
}
166209

167-
bool isHeapObject() const { return Flags.task_isHeapObject(); }
168-
HeapObject *heapObjectHeader() {
169-
assert(isHeapObject());
170-
return reinterpret_cast<HeapObject*>(this) - 1;
171-
}
172-
173210
/// A fragment of an async task structure that happens to be a child task.
174211
class ChildFragment {
175212
/// The parent task of this task.
@@ -182,6 +219,8 @@ class AsyncTask : public Job {
182219
AsyncTask *NextChild = nullptr;
183220

184221
public:
222+
ChildFragment(AsyncTask *parent) : Parent(parent) {}
223+
185224
AsyncTask *getParent() const {
186225
return Parent;
187226
}
@@ -191,6 +230,8 @@ class AsyncTask : public Job {
191230
}
192231
};
193232

233+
bool isFuture() const { return Flags.task_isFuture(); }
234+
194235
bool hasChildFragment() const { return Flags.task_isChildTask(); }
195236
ChildFragment *childFragment() {
196237
assert(hasChildFragment());
@@ -205,7 +246,7 @@ class AsyncTask : public Job {
205246
};
206247

207248
// The compiler will eventually assume these.
208-
static_assert(sizeof(AsyncTask) == 10 * sizeof(void*),
249+
static_assert(sizeof(AsyncTask) == 12 * sizeof(void*),
209250
"AsyncTask size is wrong");
210251
static_assert(alignof(AsyncTask) == 2 * alignof(void*),
211252
"AsyncTask alignment is wrong");
@@ -237,6 +278,9 @@ class alignas(MaximumAlignment) AsyncContext {
237278
TaskContinuationFunction * __ptrauth_swift_async_context_resume
238279
ResumeParent;
239280

281+
/// The executor that the parent needs to be resumed on.
282+
ExecutorRef ResumeParentExecutor;
283+
240284
/// Flags describing this context.
241285
///
242286
/// Note that this field is only 32 bits; any alignment padding
@@ -245,31 +289,44 @@ class alignas(MaximumAlignment) AsyncContext {
245289
/// is of course interrupted by the YieldToParent field.
246290
AsyncContextFlags Flags;
247291

248-
// Fields following this point may not be valid in all instances
249-
// of AsyncContext.
250-
251-
/// The function to call to temporarily resume running in the
252-
/// parent context temporarily. Generally this means a semantic
253-
/// yield. Requires Flags.hasYieldFunction().
254-
TaskContinuationFunction * __ptrauth_swift_async_context_yield
255-
YieldToParent;
256-
257-
AsyncContext(AsyncContextFlags flags,
258-
TaskContinuationFunction *resumeParent,
259-
AsyncContext *parent)
260-
: Parent(parent), ResumeParent(resumeParent), Flags(flags) {}
261-
262292
AsyncContext(AsyncContextFlags flags,
263293
TaskContinuationFunction *resumeParent,
264-
TaskContinuationFunction *yieldToParent,
294+
ExecutorRef resumeParentExecutor,
265295
AsyncContext *parent)
266-
: Parent(parent), ResumeParent(resumeParent), Flags(flags),
267-
YieldToParent(yieldToParent) {}
296+
: Parent(parent), ResumeParent(resumeParent),
297+
ResumeParentExecutor(resumeParentExecutor),
298+
Flags(flags) {}
268299

269300
AsyncContext(const AsyncContext &) = delete;
270301
AsyncContext &operator=(const AsyncContext &) = delete;
271302
};
272303

304+
/// An async context that supports yielding.
305+
class YieldingAsyncContext : public AsyncContext {
306+
public:
307+
/// The function to call to temporarily resume running in the
308+
/// parent context. Generally this means a semantic yield.
309+
TaskContinuationFunction * __ptrauth_swift_async_context_yield
310+
YieldToParent;
311+
312+
/// The executor that the parent context needs to be yielded to on.
313+
ExecutorRef YieldToParentExecutor;
314+
315+
YieldingAsyncContext(AsyncContextFlags flags,
316+
TaskContinuationFunction *resumeParent,
317+
ExecutorRef resumeParentExecutor,
318+
TaskContinuationFunction *yieldToParent,
319+
ExecutorRef yieldToParentExecutor,
320+
AsyncContext *parent)
321+
: AsyncContext(flags, resumeParent, resumeParentExecutor, parent),
322+
YieldToParent(yieldToParent),
323+
YieldToParentExecutor(yieldToParentExecutor) {}
324+
325+
static bool classof(const AsyncContext *context) {
326+
return context->Flags.getKind() == AsyncContextKind::Yielding;
327+
}
328+
};
329+
273330
} // end namespace swift
274331

275332
#endif

include/swift/ABI/TaskStatus.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class TaskStatusRecord {
8282
}
8383
};
8484

85-
TaskStatusRecord *
85+
inline TaskStatusRecord *
8686
ActiveTaskStatus::getStatusRecordParent(TaskStatusRecord *ptr) {
8787
return ptr->getParent();
8888
}

include/swift/Runtime/Concurrency.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,50 @@
2121

2222
namespace swift {
2323

24+
struct AsyncTaskAndContext {
25+
AsyncTask *Task;
26+
AsyncContext *InitialContext;
27+
};
28+
29+
/// Create a task object with no future which will run the given
30+
/// function.
31+
///
32+
/// The task is not yet scheduled.
33+
///
34+
/// If a parent task is provided, flags.task_hasChildFragment() must
35+
/// be true, and this must be called synchronously with the parent.
36+
/// The parent is responsible for creating a ChildTaskStatusRecord.
37+
/// TODO: should we have a single runtime function for creating a task
38+
/// and doing this child task status record management?
39+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
40+
AsyncTaskAndContext swift_task_create(JobFlags flags,
41+
AsyncTask *parent,
42+
const AsyncFunctionPointer<void()> *function);
43+
44+
/// Create a task object with no future which will run the given
45+
/// function.
46+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
47+
AsyncTaskAndContext swift_task_create_f(JobFlags flags,
48+
AsyncTask *parent,
49+
AsyncFunctionType<void()> *function,
50+
size_t initialContextSize);
51+
52+
/// Allocate memory in a task.
53+
///
54+
/// This must be called synchronously with the task.
55+
///
56+
/// All allocations will be rounded to a multiple of MAX_ALIGNMENT.
57+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
58+
void *swift_task_alloc(AsyncTask *task, size_t size);
59+
60+
/// Deallocate memory in a task.
61+
///
62+
/// The pointer provided must be the last pointer allocated on
63+
/// this task that has not yet been deallocated; that is, memory
64+
/// must be allocated and deallocated in a strict stack discipline.
65+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
66+
void swift_task_dealloc(AsyncTask *task, void *ptr);
67+
2468
/// Cancel a task and all of its child tasks.
2569
///
2670
/// This can be called from any thread.

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,17 +1461,17 @@ FUNCTION(GetTypeByMangledNameInContextInMetadataState,
14611461
Int8PtrPtrTy),
14621462
ATTRS(NoUnwind, ArgMemOnly))
14631463

1464-
// void *swift_taskAlloc(SwiftTask *task, size_t size);
1464+
// void *swift_task_alloc(AsyncTask *task, size_t size);
14651465
FUNCTION(TaskAlloc,
1466-
swift_taskAlloc, SwiftCC,
1466+
swift_task_alloc, SwiftCC,
14671467
ConcurrencyAvailability,
14681468
RETURNS(Int8PtrTy),
14691469
ARGS(SwiftTaskPtrTy, SizeTy),
14701470
ATTRS(NoUnwind, ArgMemOnly))
14711471

1472-
// void swift_taskDealloc(SwiftTask *task, void *ptr);
1472+
// void swift_task_dealloc(AsyncTask *task, void *ptr);
14731473
FUNCTION(TaskDealloc,
1474-
swift_taskDealloc, SwiftCC,
1474+
swift_task_dealloc, SwiftCC,
14751475
ConcurrencyAvailability,
14761476
RETURNS(VoidTy),
14771477
ARGS(SwiftTaskPtrTy, Int8PtrTy),

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
1414
Actor.swift
1515
PartialAsyncTask.swift
16+
Task.cpp
17+
TaskAlloc.cpp
1618
TaskStatus.cpp
1719
Mutex.cpp
1820

0 commit comments

Comments
 (0)