@@ -490,16 +490,8 @@ void TaskGroupImpl::destroy() {
490490 // First, remove the group from the task and deallocate the record
491491 swift_task_removeStatusRecord (getTaskRecord ());
492492
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.
503495}
504496
505497// =============================================================================
@@ -514,16 +506,18 @@ bool TaskGroup::isCancelled() {
514506}
515507
516508static void fillGroupNextResult (TaskFutureWaitAsyncContext *context,
517- PollResult result) {
509+ PollResult result,
510+ bool releaseResultRetainedTask) {
518511 // / Fill in the result value
519512 switch (result.status ) {
520513 case PollStatus::MustWait:
521514 assert (false && " filling a waiting status?" );
522515 return ;
523516
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+ }
527521
528522 case PollStatus::Success: {
529523 // Initialize the result as an Optional<Success>.
@@ -534,17 +528,28 @@ static void fillGroupNextResult(TaskFutureWaitAsyncContext *context,
534528 // remaining references to it.
535529 successType->vw_initializeWithCopy (destPtr, result.storage );
536530 successType->vw_storeEnumTagSinglePayload (destPtr, 0 , 1 );
537- return ;
531+ break ;
538532 }
539533
540534 case PollStatus::Empty: {
541535 // Initialize the result as a nil Optional<Success>.
542536 const Metadata *successType = result.successType ;
543537 OpaqueValue *destPtr = context->successResultPointer ;
544538 successType->vw_storeEnumTagSinglePayload (destPtr, 1 , 1 );
545- return ;
539+ break ;
546540 }
547541 }
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 );
548553}
549554
550555void TaskGroupImpl::offer (AsyncTask *completedTask, AsyncContext *context) {
@@ -553,16 +558,7 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
553558 assert (completedTask->hasChildFragment ());
554559 assert (completedTask->hasGroupChildFragment ());
555560 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);
566562
567563 mutex.lock (); // TODO: remove fragment lock, and use status for synchronization
568564
@@ -586,6 +582,8 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
586582 // ==== a) has waiting task, so let us complete it right away
587583 if (assumed.hasWaitingTask ()) {
588584 auto waitingTask = waitQueue.load (std::memory_order_acquire);
585+ SWIFT_TASK_DEBUG_LOG (" group has waiting task = %p, complete with = %p" ,
586+ waitingTask, completedTask);
589587 while (true ) {
590588 // ==== a) run waiting task directly -------------------------------------
591589 assert (assumed.hasWaitingTask ());
@@ -606,15 +604,16 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
606604 static_cast <TaskFutureWaitAsyncContext *>(
607605 waitingTask->ResumeContext );
608606
609- fillGroupNextResult (waitingContext, result);
610-
607+ fillGroupNextResult (waitingContext, result, /* release*/ false );
611608 _swift_tsan_acquire (static_cast <Job *>(waitingTask));
612609
613610 // TODO: allow the caller to suggest an executor
614611 swift_task_enqueueGlobal (waitingTask);
615612 return ;
616613 } // else, try again
617614 }
615+
616+ llvm_unreachable (" should have enqueued and returned." );
618617 }
619618
620619 // ==== b) enqueue completion ------------------------------------------------
@@ -623,7 +622,8 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
623622 // queue when a task polls during next() it will notice that we have a value
624623 // ready for it, and will process it immediately without suspending.
625624 assert (!waitQueue.load (std::memory_order_relaxed));
626-
625+ SWIFT_TASK_DEBUG_LOG (" group has no waiting tasks, store ready task = %p" ,
626+ completedTask);
627627 // Retain the task while it is in the queue;
628628 // it must remain alive until the task group is alive.
629629 swift_retain (completedTask);
@@ -668,6 +668,7 @@ SWIFT_CC(swiftasync) static void workaround_function_swift_taskGroup_wait_next_t
668668
669669// =============================================================================
670670// ==== group.next() implementation (wait_next and groupPoll) ------------------
671+
671672SWIFT_CC (swiftasync)
672673static void swift_taskGroup_wait_next_throwingImpl(
673674 OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
@@ -691,6 +692,8 @@ static void swift_taskGroup_wait_next_throwingImpl(
691692 PollResult polled = group->poll (waitingTask);
692693 switch (polled.status ) {
693694 case PollStatus::MustWait:
695+ SWIFT_TASK_DEBUG_LOG (" poll group = %p, no ready tasks, waiting task = %p\n " ,
696+ group, waitingTask);
694697 // The waiting task has been queued on the channel,
695698 // there were pending tasks so it will be woken up eventually.
696699#ifdef __ARM_ARCH_7K__
@@ -703,7 +706,9 @@ static void swift_taskGroup_wait_next_throwingImpl(
703706 case PollStatus::Empty:
704707 case PollStatus::Error:
705708 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 );
707712 return waitingTask->runInFullyEstablishedContext ();
708713 }
709714}
0 commit comments