@@ -174,23 +174,31 @@ static void destroyJob(SWIFT_CONTEXT HeapObject *obj) {
174
174
assert (false && " A non-task job should never be destroyed as heap metadata." );
175
175
}
176
176
177
- SWIFT_CC (swift)
178
- static void destroyTask(SWIFT_CONTEXT HeapObject *obj) {
179
- auto task = static_cast <AsyncTask*>(obj);
177
+ AsyncTask::~AsyncTask () {
180
178
// For a future, destroy the result.
181
- if (task-> isFuture ()) {
182
- task-> futureFragment ()->destroy ();
179
+ if (isFuture ()) {
180
+ futureFragment ()->destroy ();
183
181
}
184
182
185
183
// Release any objects potentially held as task local values.
186
- task->Local .destroy (task);
184
+ Local.destroy (this );
185
+ }
186
+
187
+ SWIFT_CC (swift)
188
+ static void destroyTask(SWIFT_CONTEXT HeapObject *obj) {
189
+ auto task = static_cast <AsyncTask*>(obj);
190
+
191
+ task->~AsyncTask ();
187
192
188
193
// The task execution itself should always hold a reference to it, so
189
194
// if we get here, we know the task has finished running, which means
190
195
// swift_task_complete should have been run, which will have torn down
191
196
// the task-local allocator. There's actually nothing else to clean up
192
197
// here.
193
198
199
+ #if SWIFT_TASK_PRINTF_DEBUG
200
+ fprintf (stderr, " [%p] destroy task %p\n " , pthread_self (), task);
201
+ #endif
194
202
free (task);
195
203
}
196
204
@@ -250,13 +258,9 @@ static FullMetadata<DispatchClassMetadata> taskHeapMetadata = {
250
258
const void *const swift::_swift_concurrency_debug_asyncTaskMetadata =
251
259
static_cast <Metadata *>(&taskHeapMetadata);
252
260
253
- // / The function that we put in the context of a simple task
254
- // / to handle the final return.
255
- SWIFT_CC (swiftasync)
256
- static void completeTask(SWIFT_ASYNC_CONTEXT AsyncContext *context,
257
- SWIFT_CONTEXT SwiftError *error) {
258
- // Set that there's no longer a running task in the current thread.
259
- auto task = _swift_task_clearCurrent ();
261
+ static void completeTaskImpl (AsyncTask *task,
262
+ AsyncContext *context,
263
+ SwiftError *error) {
260
264
assert (task && " completing task, but there is no active task registered" );
261
265
262
266
// Store the error result.
@@ -277,15 +281,41 @@ static void completeTask(SWIFT_ASYNC_CONTEXT AsyncContext *context,
277
281
#endif
278
282
279
283
// Complete the future.
284
+ // Warning: This deallocates the task in case it's an async let task.
285
+ // The task must not be accessed afterwards.
280
286
if (task->isFuture ()) {
281
287
task->completeFuture (context);
282
288
}
283
289
284
290
// TODO: set something in the status?
285
- if (task->hasChildFragment ()) {
291
+ // if (task->hasChildFragment()) {
286
292
// TODO: notify the parent somehow?
287
293
// TODO: remove this task from the child-task chain?
288
- }
294
+ // }
295
+ }
296
+
297
+ // / The function that we put in the context of a simple task
298
+ // / to handle the final return.
299
+ SWIFT_CC (swiftasync)
300
+ static void completeTask(SWIFT_ASYNC_CONTEXT AsyncContext *context,
301
+ SWIFT_CONTEXT SwiftError *error) {
302
+ // Set that there's no longer a running task in the current thread.
303
+ auto task = _swift_task_clearCurrent ();
304
+ assert (task && " completing task, but there is no active task registered" );
305
+
306
+ completeTaskImpl (task, context, error);
307
+ }
308
+
309
+ // / The function that we put in the context of a simple task
310
+ // / to handle the final return.
311
+ SWIFT_CC (swiftasync)
312
+ static void completeTaskAndRelease(SWIFT_ASYNC_CONTEXT AsyncContext *context,
313
+ SWIFT_CONTEXT SwiftError *error) {
314
+ // Set that there's no longer a running task in the current thread.
315
+ auto task = _swift_task_clearCurrent ();
316
+ assert (task && " completing task, but there is no active task registered" );
317
+
318
+ completeTaskImpl (task, context, error);
289
319
290
320
// Release the task, balancing the retain that a running task has on itself.
291
321
// If it was a group child task, it will remain until the group returns it.
@@ -304,7 +334,7 @@ static void completeTaskWithClosure(SWIFT_ASYNC_CONTEXT AsyncContext *context,
304
334
swift_release ((HeapObject *)asyncContextPrefix->closureContext );
305
335
306
336
// Clean up the rest of the task.
307
- return completeTask (context, error);
337
+ return completeTaskAndRelease (context, error);
308
338
}
309
339
310
340
SWIFT_CC (swiftasync)
@@ -332,11 +362,16 @@ static void task_wait_throwing_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *
332
362
}
333
363
334
364
// / All `swift_task_create*` variants funnel into this common implementation.
365
+ // /
366
+ // / If \p isAsyncLetTask is true, the \p closureContext is not heap allocated,
367
+ // / but stack-allocated (and must not be ref-counted).
368
+ // / Also, async-let tasks are not heap allcoated, but allcoated with the parent
369
+ // / task's stack allocator.
335
370
static AsyncTaskAndContext swift_task_create_group_future_commonImpl (
336
371
JobFlags flags, TaskGroup *group,
337
372
const Metadata *futureResultType,
338
373
FutureAsyncSignature::FunctionType *function,
339
- void *closureContext, bool owningClosureContext ,
374
+ void *closureContext, bool isAsyncLetTask ,
340
375
size_t initialContextSize) {
341
376
assert ((futureResultType != nullptr ) == flags.task_isFuture ());
342
377
assert (!flags.task_isFuture () ||
@@ -378,10 +413,19 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
378
413
379
414
assert (amountToAllocate % MaximumAlignment == 0 );
380
415
381
- // TODO: allow optionally passing in an allocation+sizeOfIt to reuse for the task
382
- // if the necessary space is enough, we can initialize into it rather than malloc.
383
- // this would allow us to stack-allocate async-let related tasks.
384
- void *allocation = malloc (amountToAllocate);
416
+ constexpr unsigned initialSlabSize = 512 ;
417
+
418
+ void *allocation = nullptr ;
419
+ if (isAsyncLetTask) {
420
+ assert (parent);
421
+ allocation = _swift_task_alloc_specific (parent,
422
+ amountToAllocate + initialSlabSize);
423
+ } else {
424
+ allocation = malloc (amountToAllocate);
425
+ }
426
+ #if SWIFT_TASK_PRINTF_DEBUG
427
+ fprintf (stderr, " [%p] allocate task %p, parent = %p\n " , pthread_self (), allocation, parent);
428
+ #endif
385
429
386
430
AsyncContext *initialContext =
387
431
reinterpret_cast <AsyncContext*>(
@@ -417,9 +461,17 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
417
461
418
462
// Initialize the task so that resuming it will run the given
419
463
// function on the initial context.
420
- AsyncTask *task =
421
- new (allocation) AsyncTask (&taskHeapMetadata, flags,
422
- function, initialContext);
464
+ AsyncTask *task = nullptr ;
465
+ if (isAsyncLetTask) {
466
+ // Initialize the refcount bits to "immortal", so that
467
+ // ARC operations don't have any effect on the task.
468
+ task = new (allocation) AsyncTask (&taskHeapMetadata,
469
+ InlineRefCounts::Immortal, flags,
470
+ function, initialContext);
471
+ } else {
472
+ task = new (allocation) AsyncTask (&taskHeapMetadata, flags,
473
+ function, initialContext);
474
+ }
423
475
424
476
// Initialize the child fragment if applicable.
425
477
if (parent) {
@@ -471,15 +523,21 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
471
523
// as if they might be null, even though the only time they ever might
472
524
// be is the final hop. Store a signed null instead.
473
525
initialContext->Parent = nullptr ;
474
- initialContext->ResumeParent = reinterpret_cast <TaskContinuationFunction *>(
475
- (closureContext && owningClosureContext) ? &completeTaskWithClosure :
476
- &completeTask);
477
526
initialContext->Flags = AsyncContextKind::Ordinary;
478
527
initialContext->Flags .setShouldNotDeallocateInCallee (true );
479
528
480
529
// Initialize the task-local allocator.
481
- // TODO: consider providing an initial pre-allocated first slab to the allocator.
482
- _swift_task_alloc_initialize (task);
530
+ if (isAsyncLetTask) {
531
+ initialContext->ResumeParent = reinterpret_cast <TaskContinuationFunction *>(
532
+ &completeTask);
533
+ assert (parent);
534
+ void *initialSlab = (char *)allocation + amountToAllocate;
535
+ _swift_task_alloc_initialize_with_slab (task, initialSlab, initialSlabSize);
536
+ } else {
537
+ initialContext->ResumeParent = reinterpret_cast <TaskContinuationFunction *>(
538
+ closureContext ? &completeTaskWithClosure : &completeTaskAndRelease);
539
+ _swift_task_alloc_initialize (task);
540
+ }
483
541
484
542
// TODO: if the allocator would be prepared earlier we could do this in some
485
543
// other existing if-parent if rather than adding another one here.
@@ -494,7 +552,7 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
494
552
static AsyncTaskAndContext swift_task_create_group_future_common (
495
553
JobFlags flags, TaskGroup *group, const Metadata *futureResultType,
496
554
FutureAsyncSignature::FunctionType *function,
497
- void *closureContext, bool owningClosureContext ,
555
+ void *closureContext, bool isAsyncLetTask ,
498
556
size_t initialContextSize);
499
557
500
558
AsyncTaskAndContext
@@ -524,7 +582,7 @@ AsyncTaskAndContext swift::swift_task_create_group_future_f(
524
582
return swift_task_create_group_future_common (flags, group,
525
583
futureResultType,
526
584
function, nullptr ,
527
- /* owningClosureContext */ false ,
585
+ /* isAsyncLetTask */ false ,
528
586
initialContextSize);
529
587
}
530
588
@@ -560,11 +618,11 @@ AsyncTaskAndContext swift::swift_task_create_future(JobFlags flags,
560
618
return swift_task_create_group_future_common (
561
619
flags, nullptr , futureResultType,
562
620
taskEntry, closureContext,
563
- /* owningClosureContext */ true ,
621
+ /* isAsyncLetTask */ false ,
564
622
initialContextSize);
565
623
}
566
624
567
- AsyncTaskAndContext swift::swift_task_create_future_no_escaping (JobFlags flags,
625
+ AsyncTaskAndContext swift::swift_task_create_async_let_future (JobFlags flags,
568
626
const Metadata *futureResultType,
569
627
void *closureEntry,
570
628
void *closureContext) {
@@ -579,7 +637,7 @@ AsyncTaskAndContext swift::swift_task_create_future_no_escaping(JobFlags flags,
579
637
return swift_task_create_group_future_common (
580
638
flags, nullptr , futureResultType,
581
639
taskEntry, closureContext,
582
- /* owningClosureContext */ false ,
640
+ /* isAsyncLetTask */ true ,
583
641
initialContextSize);
584
642
}
585
643
@@ -599,7 +657,7 @@ swift::swift_task_create_group_future(
599
657
return swift_task_create_group_future_common (
600
658
flags, group, futureResultType,
601
659
taskEntry, closureContext,
602
- /* owningClosureContext */ true ,
660
+ /* isAsyncLetTask */ false ,
603
661
initialContextSize);
604
662
}
605
663
0 commit comments