@@ -490,16 +490,8 @@ void TaskGroupImpl::destroy() {
490
490
// First, remove the group from the task and deallocate the record
491
491
swift_task_removeStatusRecord (getTaskRecord ());
492
492
493
- mutex.lock (); // TODO: remove lock, and use status for synchronization
494
- // Release all ready tasks which are kept retained, the group destroyed,
495
- // so no other task will ever await on them anymore;
496
- ReadyQueueItem item;
497
- bool taskDequeued = readyQueue.dequeue (item);
498
- while (taskDequeued) {
499
- swift_release (item.getTask ());
500
- taskDequeued = readyQueue.dequeue (item);
501
- }
502
- mutex.unlock (); // TODO: remove fragment lock, and use status for synchronization
493
+ // By the time we call destroy, all tasks inside the group must have been
494
+ // awaited on already; We handle this on the swift side.
503
495
}
504
496
505
497
// =============================================================================
@@ -514,16 +506,18 @@ bool TaskGroup::isCancelled() {
514
506
}
515
507
516
508
static void fillGroupNextResult (TaskFutureWaitAsyncContext *context,
517
- PollResult result) {
509
+ PollResult result,
510
+ bool releaseResultRetainedTask) {
518
511
// / Fill in the result value
519
512
switch (result.status ) {
520
513
case PollStatus::MustWait:
521
514
assert (false && " filling a waiting status?" );
522
515
return ;
523
516
524
- case PollStatus::Error:
525
- context->fillWithError (reinterpret_cast <SwiftError*>(result.storage ));
526
- return ;
517
+ case PollStatus::Error: {
518
+ context->fillWithError (reinterpret_cast <SwiftError *>(result.storage ));
519
+ break ;
520
+ }
527
521
528
522
case PollStatus::Success: {
529
523
// Initialize the result as an Optional<Success>.
@@ -534,17 +528,28 @@ static void fillGroupNextResult(TaskFutureWaitAsyncContext *context,
534
528
// remaining references to it.
535
529
successType->vw_initializeWithCopy (destPtr, result.storage );
536
530
successType->vw_storeEnumTagSinglePayload (destPtr, 0 , 1 );
537
- return ;
531
+ break ;
538
532
}
539
533
540
534
case PollStatus::Empty: {
541
535
// Initialize the result as a nil Optional<Success>.
542
536
const Metadata *successType = result.successType ;
543
537
OpaqueValue *destPtr = context->successResultPointer ;
544
538
successType->vw_storeEnumTagSinglePayload (destPtr, 1 , 1 );
545
- return ;
539
+ break ;
546
540
}
547
541
}
542
+
543
+ // We only release if asked to; This is because this function is called in two
544
+ // cases "immediately":
545
+ // a) when a completed task arrives and a waiting one existed then we don't
546
+ // need to retain the completed task at all, thus we also don't release it.
547
+ // b) when the task was stored in the readyQueue it was retained. As a
548
+ // waitingTask arrives we will fill-in with the value from the retained
549
+ // task. In this situation we must release the ready task, to allow it to
550
+ // be destroyed.
551
+ if (releaseResultRetainedTask)
552
+ swift_release (result.retainedTask );
548
553
}
549
554
550
555
void TaskGroupImpl::offer (AsyncTask *completedTask, AsyncContext *context) {
@@ -553,16 +558,7 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
553
558
assert (completedTask->hasChildFragment ());
554
559
assert (completedTask->hasGroupChildFragment ());
555
560
assert (completedTask->groupChildFragment ()->getGroup () == asAbstract (this ));
556
-
557
- // We retain the completed task, because we will either:
558
- // - (a) schedule the waiter to resume on the next() that it is waiting on, or
559
- // - (b) will need to store this task until the group task enters next() and
560
- // picks up this task.
561
- // either way, there is some time between us returning here, and the `completeTask`
562
- // issuing a swift_release on this very task. We need to keep it alive until
563
- // we have the chance to poll it from the queue (via the waiter task entering
564
- // calling next()).
565
- swift_retain (completedTask);
561
+ SWIFT_TASK_DEBUG_LOG (" offer task %p to group %p\n " , completedTask, group);
566
562
567
563
mutex.lock (); // TODO: remove fragment lock, and use status for synchronization
568
564
@@ -586,6 +582,8 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
586
582
// ==== a) has waiting task, so let us complete it right away
587
583
if (assumed.hasWaitingTask ()) {
588
584
auto waitingTask = waitQueue.load (std::memory_order_acquire);
585
+ SWIFT_TASK_DEBUG_LOG (" group has waiting task = %p, complete with = %p" ,
586
+ waitingTask, completedTask);
589
587
while (true ) {
590
588
// ==== a) run waiting task directly -------------------------------------
591
589
assert (assumed.hasWaitingTask ());
@@ -606,15 +604,16 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
606
604
static_cast <TaskFutureWaitAsyncContext *>(
607
605
waitingTask->ResumeContext );
608
606
609
- fillGroupNextResult (waitingContext, result);
610
-
607
+ fillGroupNextResult (waitingContext, result, /* release*/ false );
611
608
_swift_tsan_acquire (static_cast <Job *>(waitingTask));
612
609
613
610
// TODO: allow the caller to suggest an executor
614
611
swift_task_enqueueGlobal (waitingTask);
615
612
return ;
616
613
} // else, try again
617
614
}
615
+
616
+ llvm_unreachable (" should have enqueued and returned." );
618
617
}
619
618
620
619
// ==== b) enqueue completion ------------------------------------------------
@@ -623,7 +622,8 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
623
622
// queue when a task polls during next() it will notice that we have a value
624
623
// ready for it, and will process it immediately without suspending.
625
624
assert (!waitQueue.load (std::memory_order_relaxed));
626
-
625
+ SWIFT_TASK_DEBUG_LOG (" group has no waiting tasks, store ready task = %p" ,
626
+ completedTask);
627
627
// Retain the task while it is in the queue;
628
628
// it must remain alive until the task group is alive.
629
629
swift_retain (completedTask);
@@ -668,6 +668,7 @@ SWIFT_CC(swiftasync) static void workaround_function_swift_taskGroup_wait_next_t
668
668
669
669
// =============================================================================
670
670
// ==== group.next() implementation (wait_next and groupPoll) ------------------
671
+
671
672
SWIFT_CC (swiftasync)
672
673
static void swift_taskGroup_wait_next_throwingImpl(
673
674
OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
@@ -691,6 +692,8 @@ static void swift_taskGroup_wait_next_throwingImpl(
691
692
PollResult polled = group->poll (waitingTask);
692
693
switch (polled.status ) {
693
694
case PollStatus::MustWait:
695
+ SWIFT_TASK_DEBUG_LOG (" poll group = %p, no ready tasks, waiting task = %p\n " ,
696
+ group, waitingTask);
694
697
// The waiting task has been queued on the channel,
695
698
// there were pending tasks so it will be woken up eventually.
696
699
#ifdef __ARM_ARCH_7K__
@@ -703,7 +706,9 @@ static void swift_taskGroup_wait_next_throwingImpl(
703
706
case PollStatus::Empty:
704
707
case PollStatus::Error:
705
708
case PollStatus::Success:
706
- fillGroupNextResult (context, polled);
709
+ SWIFT_TASK_DEBUG_LOG (" poll group = %p, task = %p, ready task available = %p\n " ,
710
+ group, waitingTask, polled.retainedTask );
711
+ fillGroupNextResult (context, polled, /* release*/ true );
707
712
return waitingTask->runInFullyEstablishedContext ();
708
713
}
709
714
}
0 commit comments