@@ -73,6 +73,56 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask) {
73
73
}
74
74
}
75
75
76
+
77
+ namespace {
78
+
79
+ // / An asynchronous context within a task that describes a general "Future".
80
+ // / task.
81
+ // /
82
+ // / This type matches the ABI of a function `<T> () async throws -> T`, which
83
+ // / is the type used by `Task.runDetached` and `Task.group.add` to create
84
+ // / futures.
85
+ class TaskFutureWaitAsyncContext : public AsyncContext {
86
+ public:
87
+ // Error result is always present.
88
+ SwiftError *errorResult = nullptr ;
89
+
90
+ // No indirect results.
91
+
92
+ TaskFutureWaitResult result;
93
+
94
+ // FIXME: Currently, this is always here, but it isn't technically
95
+ // necessary.
96
+ void * Self;
97
+
98
+ // Arguments.
99
+ AsyncTask *task;
100
+
101
+ using AsyncContext::AsyncContext;
102
+ };
103
+
104
+ }
105
+
106
+ // / Run the given task, privoding it with the result of the future.
107
+ static void runTaskWithFutureResult (
108
+ AsyncTask *waitingTask, ExecutorRef executor,
109
+ FutureFragment *futureFragment, bool hadErrorResult) {
110
+ auto waitingTaskContext =
111
+ static_cast <TaskFutureWaitAsyncContext *>(waitingTask->ResumeContext );
112
+
113
+ waitingTaskContext->result .hadErrorResult = hadErrorResult;
114
+ if (hadErrorResult) {
115
+ waitingTaskContext->result .storage =
116
+ reinterpret_cast <OpaqueValue *>(futureFragment->getError ());
117
+ } else {
118
+ waitingTaskContext->result .storage = futureFragment->getStoragePtr ();
119
+ }
120
+
121
+ // TODO: schedule this task on the executor rather than running it
122
+ // directly.
123
+ waitingTask->run (executor);
124
+ }
125
+
76
126
void AsyncTask::completeFuture (AsyncContext *context, ExecutorRef executor) {
77
127
using Status = FutureFragment::Status;
78
128
using WaitQueueItem = FutureFragment::WaitQueueItem;
@@ -103,9 +153,8 @@ void AsyncTask::completeFuture(AsyncContext *context, ExecutorRef executor) {
103
153
// Find the next waiting task.
104
154
auto nextWaitingTask = waitingTask->getNextWaitingTask ();
105
155
106
- // TODO: schedule this task on the executor rather than running it
107
- // directly.
108
- waitingTask->run (executor);
156
+ // Run the task.
157
+ runTaskWithFutureResult (waitingTask, executor, fragment, hadErrorResult);
109
158
110
159
// Move to the next task.
111
160
waitingTask = nextWaitingTask;
@@ -264,21 +313,37 @@ AsyncTaskAndContext swift::swift_task_create_future_f(
264
313
return {task, initialContext};
265
314
}
266
315
267
- TaskFutureWaitResult
268
- swift::swift_task_future_wait (AsyncTask *task, AsyncTask *waitingTask) {
316
+ void swift::swift_task_future_wait (
317
+ AsyncTask *waitingTask, ExecutorRef executor,
318
+ AsyncContext *rawContext) {
319
+ // Suspend the waiting task.
320
+ waitingTask->ResumeTask = rawContext->ResumeParent ;
321
+ waitingTask->ResumeContext = rawContext;
322
+
323
+ // Wait on the future.
324
+ auto context = static_cast <TaskFutureWaitAsyncContext *>(rawContext);
325
+ auto task = context->task ;
269
326
assert (task->isFuture ());
270
327
switch (task->waitFuture (waitingTask)) {
271
328
case FutureFragment::Status::Executing:
272
- return TaskFutureWaitResult{TaskFutureWaitResult::Waiting, nullptr };
329
+ // The waiting task has been queued on the future.
330
+ return ;
273
331
274
332
case FutureFragment::Status::Success:
275
- return TaskFutureWaitResult{
276
- TaskFutureWaitResult::Success, task->futureFragment ()->getStoragePtr ()};
277
-
278
- case FutureFragment::Status::Error:
279
- return TaskFutureWaitResult{
280
- TaskFutureWaitResult::Error,
281
- reinterpret_cast <OpaqueValue *>(task->futureFragment ()->getError ())};
333
+ // Run the task with a successful result.
334
+ // FIXME: Want to guarantee a tail call here
335
+ runTaskWithFutureResult (
336
+ waitingTask, executor, task->futureFragment (),
337
+ /* hadErrorResult=*/ false );
338
+ return ;
339
+
340
+ case FutureFragment::Status::Error:
341
+ // Run the task with an error result.
342
+ // FIXME: Want to guarantee a tail call here
343
+ runTaskWithFutureResult (
344
+ waitingTask, executor, task->futureFragment (),
345
+ /* hadErrorResult=*/ true );
346
+ return ;
282
347
}
283
348
}
284
349
0 commit comments