Skip to content

Commit 5dd0bce

Browse files
authored
Merge pull request #34703 from DougGregor/concurrency-task-future
[Concurrency] Implement basic runtime support for task futures.
2 parents b72558e + ede5aa3 commit 5dd0bce

File tree

6 files changed

+534
-18
lines changed

6 files changed

+534
-18
lines changed

include/swift/ABI/MetadataKind.def

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

88-
/// A heap-allocated simple task.
89-
METADATAKIND(SimpleTask,
88+
/// A heap-allocated task.
89+
METADATAKIND(Task,
9090
2 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
9191

9292
// getEnumeratedMetadataKind assumes that all the enumerated values here

include/swift/ABI/Task.h

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "swift/Basic/RelativePointer.h"
2121
#include "swift/ABI/HeapObject.h"
22+
#include "swift/ABI/Metadata.h"
2223
#include "swift/ABI/MetadataValues.h"
2324
#include "swift/Runtime/Config.h"
2425
#include "swift/Basic/STLExtras.h"
@@ -29,6 +30,8 @@ class AsyncTask;
2930
class AsyncContext;
3031
class Executor;
3132
class Job;
33+
struct OpaqueValue;
34+
struct SwiftError;
3235
class TaskStatusRecord;
3336

3437
/// An ExecutorRef isn't necessarily just a pointer to an executor
@@ -86,6 +89,13 @@ class AsyncFunctionPointer {
8689

8790
/// A schedulable job.
8891
class alignas(2 * alignof(void*)) Job {
92+
protected:
93+
// Indices into SchedulerPrivate, for use by the runtime.
94+
enum {
95+
/// The next waiting task link, an AsyncTask that is waiting on a future.
96+
NextWaitingTaskIndex = 0,
97+
};
98+
8999
public:
90100
// Reserved for the use of the scheduler.
91101
void *SchedulerPrivate[2];
@@ -230,19 +240,142 @@ class AsyncTask : public HeapObject, public Job {
230240
}
231241
};
232242

233-
bool isFuture() const { return Flags.task_isFuture(); }
234-
235243
bool hasChildFragment() const { return Flags.task_isChildTask(); }
236244
ChildFragment *childFragment() {
237245
assert(hasChildFragment());
238246
return reinterpret_cast<ChildFragment*>(this + 1);
239247
}
240248

241-
// TODO: Future fragment
249+
class FutureFragment {
250+
public:
251+
/// Describes the status of the future.
252+
///
253+
/// Futures always begin in the "Executing" state, and will always
254+
/// make a single state change to either Success or Error.
255+
enum class Status : uintptr_t {
256+
/// The future is executing or ready to execute. The storage
257+
/// is not accessible.
258+
Executing = 0,
259+
260+
/// The future has completed with result (of type \c resultType).
261+
Success,
262+
263+
/// The future has completed by throwing an error (an \c Error
264+
/// existential).
265+
Error,
266+
};
267+
268+
/// An item within the wait queue, which includes the status and the
269+
/// head of the list of tasks.
270+
struct WaitQueueItem {
271+
/// Mask used for the low status bits in a wait queue item.
272+
static const uintptr_t statusMask = 0x03;
273+
274+
uintptr_t storage;
275+
276+
Status getStatus() const {
277+
return static_cast<Status>(storage & statusMask);
278+
}
279+
280+
AsyncTask *getTask() const {
281+
return reinterpret_cast<AsyncTask *>(storage & ~statusMask);
282+
}
283+
284+
static WaitQueueItem get(Status status, AsyncTask *task) {
285+
return WaitQueueItem{
286+
reinterpret_cast<uintptr_t>(task) | static_cast<uintptr_t>(status)};
287+
}
288+
};
289+
290+
private:
291+
/// Queue containing all of the tasks that are waiting in `get()`.
292+
///
293+
/// The low bits contain the status, the rest of the pointer is the
294+
/// AsyncTask.
295+
std::atomic<WaitQueueItem> waitQueue;
296+
297+
/// The type of the result that will be produced by the future.
298+
const Metadata *resultType;
299+
300+
// Trailing storage for the result itself. The storage will be uninitialized,
301+
// contain an instance of \c resultType, or contaon an an \c Error.
302+
303+
friend class AsyncTask;
304+
305+
public:
306+
explicit FutureFragment(const Metadata *resultType)
307+
: waitQueue(WaitQueueItem::get(Status::Executing, nullptr)),
308+
resultType(resultType) { }
309+
310+
/// Destroy the storage associated with the future.
311+
void destroy();
312+
313+
/// Retrieve a pointer to the storage of result.
314+
OpaqueValue *getStoragePtr() {
315+
return reinterpret_cast<OpaqueValue *>(
316+
reinterpret_cast<char *>(this) + storageOffset(resultType));
317+
}
318+
319+
/// Retrieve the error.
320+
SwiftError *&getError() {
321+
return *reinterpret_cast<SwiftError **>(
322+
reinterpret_cast<char *>(this) + storageOffset(resultType));
323+
}
324+
325+
/// Compute the offset of the storage from the base of the future
326+
/// fragment.
327+
static size_t storageOffset(const Metadata *resultType) {
328+
size_t offset = sizeof(FutureFragment);
329+
size_t alignment =
330+
std::max(resultType->vw_alignment(), alignof(SwiftError *));
331+
return (offset + alignment - 1) & ~(alignment - 1);
332+
}
333+
334+
/// Determine the size of the future fragment given a particular future
335+
/// result type.
336+
static size_t fragmentSize(const Metadata *resultType) {
337+
return storageOffset(resultType) +
338+
std::max(resultType->vw_size(), sizeof(SwiftError *));
339+
}
340+
};
341+
342+
bool isFuture() const { return Flags.task_isFuture(); }
343+
344+
FutureFragment *futureFragment() {
345+
assert(isFuture());
346+
if (hasChildFragment()) {
347+
return reinterpret_cast<FutureFragment *>(
348+
reinterpret_cast<ChildFragment*>(this + 1) + 1);
349+
}
350+
351+
return reinterpret_cast<FutureFragment *>(this + 1);
352+
}
353+
354+
/// Wait for this future to complete.
355+
///
356+
/// \returns the status of the future. If this result is
357+
/// \c Executing, then \c waitingTask has been added to the
358+
/// wait queue and will be scheduled when the future completes. Otherwise,
359+
/// the future has completed and can be queried.
360+
FutureFragment::Status waitFuture(AsyncTask *waitingTask);
361+
362+
/// Complete this future.
363+
///
364+
/// Upon completion, any waiting tasks will be scheduled on the given
365+
/// executor.
366+
void completeFuture(AsyncContext *context, ExecutorRef executor);
242367

243368
static bool classof(const Job *job) {
244369
return job->isAsyncTask();
245370
}
371+
372+
private:
373+
/// Access the next waiting task, which establishes a singly linked list of
374+
/// tasks that are waiting on a future.
375+
AsyncTask *&getNextWaitingTask() {
376+
return reinterpret_cast<AsyncTask *&>(
377+
SchedulerPrivate[NextWaitingTaskIndex]);
378+
}
246379
};
247380

248381
// The compiler will eventually assume these.
@@ -327,6 +460,20 @@ class YieldingAsyncContext : public AsyncContext {
327460
}
328461
};
329462

463+
/// An asynchronous context within a task that describes a general "Future".
464+
/// task.
465+
///
466+
/// This type matches the ABI of a function `<T> () async throws -> T`, which
467+
/// is the type used by `Task.runDetached` and `Task.group.add` to create
468+
/// futures.
469+
class FutureAsyncContext : public AsyncContext {
470+
public:
471+
SwiftError *errorResult = nullptr;
472+
OpaqueValue *indirectResult;
473+
474+
using AsyncContext::AsyncContext;
475+
};
476+
330477
} // end namespace swift
331478

332479
#endif

include/swift/Runtime/Concurrency.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@ AsyncTaskAndContext swift_task_create_f(JobFlags flags,
4949
AsyncFunctionType<void()> *function,
5050
size_t initialContextSize);
5151

52+
/// Create a task object with a future which will run the given
53+
/// function.
54+
///
55+
/// The task is not yet scheduled.
56+
///
57+
/// If a parent task is provided, flags.task_hasChildFragment() must
58+
/// be true, and this must be called synchronously with the parent.
59+
/// The parent is responsible for creating a ChildTaskStatusRecord.
60+
/// TODO: should we have a single runtime function for creating a task
61+
/// and doing this child task status record management?
62+
///
63+
/// flags.task_isFuture must be set. \c futureResultType is the type
64+
///
65+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
66+
AsyncTaskAndContext swift_task_create_future(
67+
JobFlags flags, AsyncTask *parent, const Metadata *futureResultType,
68+
const AsyncFunctionPointer<void()> *function);
69+
70+
/// Create a task object with a future which will run the given
71+
/// function.
72+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
73+
AsyncTaskAndContext swift_task_create_future_f(
74+
JobFlags flags, AsyncTask *parent, const Metadata *futureResultType,
75+
AsyncFunctionType<void()> *function, size_t initialContextSize);
76+
5277
/// Allocate memory in a task.
5378
///
5479
/// This must be called synchronously with the task.
@@ -83,6 +108,34 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
83108
JobPriority
84109
swift_task_escalate(AsyncTask *task, JobPriority newPriority);
85110

111+
/// The result of waiting for a task future.
112+
struct TaskFutureWaitResult {
113+
enum Kind : uintptr_t {
114+
/// The waiting task has been added to the future's wait queue, and will
115+
/// be scheduled once the future has completed.
116+
Waiting,
117+
118+
/// The future succeeded and produced a result value. \c storage points
119+
/// at that value.
120+
Success,
121+
122+
/// The future finished by throwing an error. \c storage is that error
123+
/// existential.
124+
Error,
125+
};
126+
127+
Kind kind;
128+
OpaqueValue *storage;
129+
};
130+
131+
/// Wait for a future task to complete.
132+
///
133+
/// This can be called from any thread.
134+
///
135+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
136+
TaskFutureWaitResult
137+
swift_task_future_wait(AsyncTask *task, AsyncTask *waitingTask);
138+
86139
/// Add a status record to a task. The record should not be
87140
/// modified while it is registered with a task.
88141
///

0 commit comments

Comments
 (0)