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