2
2
//
3
3
// This source file is part of the Swift.org open source project
4
4
//
5
- // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
5
+ // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6
6
// Licensed under Apache License v2.0 with Runtime Library Exception
7
7
//
8
8
// See https://swift.org/LICENSE.txt for license information
@@ -279,7 +279,7 @@ class TaskGroupImpl: public TaskGroupTaskStatusRecord {
279
279
280
280
private:
281
281
282
- // // TODO: move to lockless via the status atomic
282
+ // TODO: move to lockless via the status atomic (make readyQueue an mpsc_queue_t<ReadyQueueItem>)
283
283
mutable std::mutex mutex;
284
284
285
285
// / Used for queue management, counting number of waiting and ready tasks
@@ -290,7 +290,6 @@ class TaskGroupImpl: public TaskGroupTaskStatusRecord {
290
290
// / The low bits contain the status, the rest of the pointer is the
291
291
// / AsyncTask.
292
292
NaiveQueue<ReadyQueueItem> readyQueue;
293
- // mpsc_queue_t<ReadyQueueItem> readyQueue; // TODO: can we get away with an MPSC queue here once actor executors land?
294
293
295
294
// / Single waiting `AsyncTask` currently waiting on `group.next()`,
296
295
// / or `nullptr` if no task is currently waiting.
@@ -305,10 +304,8 @@ class TaskGroupImpl: public TaskGroupTaskStatusRecord {
305
304
: TaskGroupTaskStatusRecord(),
306
305
status(GroupStatus::initial().status),
307
306
readyQueue(),
308
- // readyQueue(ReadyQueueItem::get(ReadyStatus::Empty, nullptr)),
309
307
waitQueue(nullptr ), successType(T) {}
310
308
311
-
312
309
TaskGroupTaskStatusRecord *getTaskRecord () {
313
310
return reinterpret_cast <TaskGroupTaskStatusRecord *>(this );
314
311
}
@@ -472,11 +469,18 @@ static void swift_taskGroup_initializeImpl(TaskGroup *group, const Metadata *T)
472
469
473
470
// =============================================================================
474
471
// ==== add / attachChild ------------------------------------------------------
472
+
475
473
SWIFT_CC (swift)
476
474
static void swift_taskGroup_attachChildImpl(TaskGroup *group,
477
475
AsyncTask *child) {
476
+ SWIFT_TASK_DEBUG_LOG (" attach child task = %p to group = %p\n " ,
477
+ child, group);
478
+
479
+ // The counterpart of this (detachChild) is performed by the group itself,
480
+ // when it offers the completed (child) task's value to a waiting task -
481
+ // during the implementation of `await group.next()`.
478
482
auto groupRecord = asImpl (group)->getTaskRecord ();
479
- return groupRecord->attachChild (child);
483
+ groupRecord->attachChild (child);
480
484
}
481
485
482
486
// =============================================================================
@@ -490,16 +494,8 @@ void TaskGroupImpl::destroy() {
490
494
// First, remove the group from the task and deallocate the record
491
495
swift_task_removeStatusRecord (getTaskRecord ());
492
496
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
497
+ // By the time we call destroy, all tasks inside the group must have been
498
+ // awaited on already; We handle this on the swift side.
503
499
}
504
500
505
501
// =============================================================================
@@ -521,9 +517,10 @@ static void fillGroupNextResult(TaskFutureWaitAsyncContext *context,
521
517
assert (false && " filling a waiting status?" );
522
518
return ;
523
519
524
- case PollStatus::Error:
525
- context->fillWithError (reinterpret_cast <SwiftError*>(result.storage ));
520
+ case PollStatus::Error: {
521
+ context->fillWithError (reinterpret_cast <SwiftError *>(result.storage ));
526
522
return ;
523
+ }
527
524
528
525
case PollStatus::Success: {
529
526
// Initialize the result as an Optional<Success>.
@@ -553,16 +550,7 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
553
550
assert (completedTask->hasChildFragment ());
554
551
assert (completedTask->hasGroupChildFragment ());
555
552
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);
553
+ SWIFT_TASK_DEBUG_LOG (" offer task %p to group %p" , completedTask, this );
566
554
567
555
mutex.lock (); // TODO: remove fragment lock, and use status for synchronization
568
556
@@ -586,6 +574,8 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
586
574
// ==== a) has waiting task, so let us complete it right away
587
575
if (assumed.hasWaitingTask ()) {
588
576
auto waitingTask = waitQueue.load (std::memory_order_acquire);
577
+ SWIFT_TASK_DEBUG_LOG (" group has waiting task = %p, complete with = %p" ,
578
+ waitingTask, completedTask);
589
579
while (true ) {
590
580
// ==== a) run waiting task directly -------------------------------------
591
581
assert (assumed.hasWaitingTask ());
@@ -607,6 +597,7 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
607
597
waitingTask->ResumeContext );
608
598
609
599
fillGroupNextResult (waitingContext, result);
600
+ detachChild (result.retainedTask );
610
601
611
602
_swift_tsan_acquire (static_cast <Job *>(waitingTask));
612
603
@@ -615,6 +606,8 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
615
606
return ;
616
607
} // else, try again
617
608
}
609
+
610
+ llvm_unreachable (" should have enqueued and returned." );
618
611
}
619
612
620
613
// ==== b) enqueue completion ------------------------------------------------
@@ -623,10 +616,12 @@ void TaskGroupImpl::offer(AsyncTask *completedTask, AsyncContext *context) {
623
616
// queue when a task polls during next() it will notice that we have a value
624
617
// ready for it, and will process it immediately without suspending.
625
618
assert (!waitQueue.load (std::memory_order_relaxed));
626
-
619
+ SWIFT_TASK_DEBUG_LOG (" group has no waiting tasks, RETAIN and store ready task = %p" ,
620
+ completedTask);
627
621
// Retain the task while it is in the queue;
628
622
// it must remain alive until the task group is alive.
629
623
swift_retain (completedTask);
624
+
630
625
auto readyItem = ReadyQueueItem::get (
631
626
hadErrorResult ? ReadyStatus::Error : ReadyStatus::Success,
632
627
completedTask
@@ -668,6 +663,7 @@ SWIFT_CC(swiftasync) static void workaround_function_swift_taskGroup_wait_next_t
668
663
669
664
// =============================================================================
670
665
// ==== group.next() implementation (wait_next and groupPoll) ------------------
666
+
671
667
SWIFT_CC (swiftasync)
672
668
static void swift_taskGroup_wait_next_throwingImpl(
673
669
OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
@@ -691,6 +687,8 @@ static void swift_taskGroup_wait_next_throwingImpl(
691
687
PollResult polled = group->poll (waitingTask);
692
688
switch (polled.status ) {
693
689
case PollStatus::MustWait:
690
+ SWIFT_TASK_DEBUG_LOG (" poll group = %p, no ready tasks, waiting task = %p" ,
691
+ group, waitingTask);
694
692
// The waiting task has been queued on the channel,
695
693
// there were pending tasks so it will be woken up eventually.
696
694
#ifdef __ARM_ARCH_7K__
@@ -703,13 +701,22 @@ static void swift_taskGroup_wait_next_throwingImpl(
703
701
case PollStatus::Empty:
704
702
case PollStatus::Error:
705
703
case PollStatus::Success:
704
+ SWIFT_TASK_DEBUG_LOG (" poll group = %p, task = %p, ready task available = %p" ,
705
+ group, waitingTask, polled.retainedTask );
706
706
fillGroupNextResult (context, polled);
707
+ if (auto completedTask = polled.retainedTask ) {
708
+ // it would be null for PollStatus::Empty, then we don't need to release
709
+ group->detachChild (polled.retainedTask );
710
+ swift_release (polled.retainedTask );
711
+ }
712
+
707
713
return waitingTask->runInFullyEstablishedContext ();
708
714
}
709
715
}
710
716
711
717
PollResult TaskGroupImpl::poll (AsyncTask *waitingTask) {
712
718
mutex.lock (); // TODO: remove group lock, and use status for synchronization
719
+ SWIFT_TASK_DEBUG_LOG (" poll group = %p" , this );
713
720
auto assumed = statusMarkWaitingAssumeAcquire ();
714
721
715
722
PollResult result;
@@ -719,6 +726,7 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) {
719
726
720
727
// ==== 1) bail out early if no tasks are pending ----------------------------
721
728
if (assumed.isEmpty ()) {
729
+ SWIFT_TASK_DEBUG_LOG (" poll group = %p, group is empty, no pending tasks" , this );
722
730
// No tasks in flight, we know no tasks were submitted before this poll
723
731
// was issued, and if we parked here we'd potentially never be woken up.
724
732
// Bail out and return `nil` from `group.next()`.
@@ -736,6 +744,9 @@ PollResult TaskGroupImpl::poll(AsyncTask *waitingTask) {
736
744
737
745
// ==== 2) Ready task was polled, return with it immediately -----------------
738
746
if (assumed.readyTasks ()) {
747
+ SWIFT_TASK_DEBUG_LOG (" poll group = %p, group has ready tasks = %d" ,
748
+ this , assumed.readyTasks ());
749
+
739
750
auto assumedStatus = assumed.status ;
740
751
auto newStatus = TaskGroupImpl::GroupStatus{assumedStatus};
741
752
if (status.compare_exchange_weak (
@@ -839,13 +850,16 @@ static void swift_taskGroup_cancelAllImpl(TaskGroup *group) {
839
850
}
840
851
841
852
bool TaskGroupImpl::cancelAll () {
853
+ SWIFT_TASK_DEBUG_LOG (" cancel all tasks in group = %p" , this );
854
+
842
855
// store the cancelled bit
843
856
auto old = statusCancel ();
844
857
if (old.isCancelled ()) {
845
858
// already was cancelled previously, nothing to do?
846
859
return false ;
847
860
}
848
861
862
+ // FIXME: must also remove the records!!!!
849
863
// cancel all existing tasks within the group
850
864
swift_task_cancel_group_child_tasks (asAbstract (this ));
851
865
return true ;
0 commit comments