@@ -528,8 +528,64 @@ class JobRef {
528
528
}
529
529
};
530
530
531
- // / This is designed to fit into two words, which can generally be
532
- // / done lock-free on all our supported platforms.
531
+ // / Similar to the ActiveTaskStatus, this denotes the ActiveActorState for
532
+ // / tracking the atomic state of the actor
533
+ // /
534
+ // / The runtime needs to track the following state about the actor within the
535
+ // / same atomic:
536
+ // /
537
+ // / * The current status of the actor - scheduled, running, idle, etc
538
+ // / * The current maximum priority of the jobs enqueued in the actor
539
+ // / * The identity of the thread currently holding the actor lock
540
+ // / * Pointer to list of jobs enqueued in actor
541
+ // /
542
+ // / It is important for all of this information to be in the same atomic so that
543
+ // / when the actor's state changes, the information is visible to all threads that
544
+ // / may be modifying the actor, allowing the algorithm to eventually converge.
545
+ // /
546
+ // / In order to provide priority escalation support with actors, deeper integration is
547
+ // / required with the OS in order to have the intended side effects. On Darwin, Swift
548
+ // / Concurrency Tasks runs on dispatch's queues. As such, we need to use an
549
+ // / encoding of thread identity vended by libdispatch called dispatch_lock_t,
550
+ // / and a futex-style dispatch API in order to escalate the priority of a
551
+ // / thread. Henceforth, the dispatch_lock_t tracked in the ActiveActorStatus
552
+ // / will be called the DrainLock.
553
+ // /
554
+ // / When a thread starts running on an actor, it's identity is recorded in the
555
+ // / ActiveActorStatus. This way, if a higher priority job is enqueued behind the
556
+ // / thread executing the actor, we can escalate the thread holding the actor
557
+ // / lock, thereby resolving the priority inversion. When a thread hops off of
558
+ // / the actor, any priority boosts it may have gotten as a result of contention
559
+ // / on the actor, is removed as well.
560
+ // /
561
+ // / In order to keep the entire ActiveActorStatus size to 2 words, the thread
562
+ // / identity is only tracked on platforms which can support 128 bit atomic
563
+ // / operations. The ActiveActorStatus's layout has thus been changed to have the
564
+ // / following layout depending on the system configuration supported:
565
+ // /
566
+ // / 32 bit systems with SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION=1
567
+ // /
568
+ // / Flags Drain Lock Unused JobRef
569
+ // / |----------------------|----------------------|----------------------|-------------------|
570
+ // / 32 bits 32 bits 32 bits 32 bits
571
+ // /
572
+ // / 64 bit systems with SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION=1
573
+ // /
574
+ // / Flags Drain Lock JobRef
575
+ // / |----------------------|-------------------|----------------------|
576
+ // / 32 bits 32 bits 64 bits
577
+ // /
578
+ // / 32 bit systems with SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION=0
579
+ // /
580
+ // / Flags JobRef
581
+ // / |----------------------|----------------------|
582
+ // / 32 bits 32 bits
583
+ //
584
+ // / 64 bit systems with SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION=0
585
+ // /
586
+ // / Flags Unused JobRef
587
+ // / |----------------------|----------------------|---------------------|
588
+ // / 32 bits 32 bits 64 bits
533
589
class alignas (sizeof (void *) * 2 ) ActiveActorStatus {
534
590
enum : uint32_t {
535
591
// Bits 0-2: Actor state
@@ -567,11 +623,29 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus {
567
623
PriorityMask = 0xFF00 ,
568
624
PriorityShift = 0x8 ,
569
625
};
626
+
627
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
628
+ uint32_t Flags;
629
+ dispatch_lock_t DrainLock;
630
+ uint32_t Unused;
631
+ #elif SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_8_BYTES
570
632
uint32_t Flags;
633
+ dispatch_lock_t DrainLock;
634
+ #elif !SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
635
+ uint32_t Flags;
636
+ #else /* !SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_8_BYTES */
637
+ uint32_t Flags;
638
+ uint32_t Unused;
639
+ #endif
571
640
JobRef FirstJob;
572
641
642
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
643
+ ActiveActorStatus (uint32_t flags, dispatch_lock_t drainLockValue, JobRef job)
644
+ : Flags (flags), DrainLock (drainLockValue), FirstJob (job) {}
645
+ #else
573
646
ActiveActorStatus (uint32_t flags, JobRef job)
574
647
: Flags (flags), FirstJob (job) {}
648
+ #endif
575
649
576
650
public:
577
651
#ifdef __GLIBCXX__
@@ -582,11 +656,15 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus {
582
656
#endif
583
657
584
658
constexpr ActiveActorStatus ()
585
- : Flags (), FirstJob (JobRef ()) {}
659
+ : Flags (), DrainLock (DLOCK_OWNER_NULL), FirstJob (JobRef ()) {}
586
660
587
661
bool isDistributedRemote () const { return Flags & DistributedRemote; }
588
662
ActiveActorStatus withDistributedRemote () const {
663
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
664
+ return ActiveActorStatus (Flags | DistributedRemote, DrainLock, FirstJob);
665
+ #else
589
666
return ActiveActorStatus (Flags | DistributedRemote, FirstJob);
667
+ #endif
590
668
}
591
669
592
670
bool isIdle () const {
@@ -597,7 +675,11 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus {
597
675
return isIdle;
598
676
}
599
677
ActiveActorStatus withIdle () const {
678
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
679
+ return ActiveActorStatus ((Flags & ~ActorStateMask) | Idle, DLOCK_OWNER_NULL, FirstJob);
680
+ #else
600
681
return ActiveActorStatus ((Flags & ~ActorStateMask) | Idle, FirstJob);
682
+ #endif
601
683
}
602
684
603
685
bool isAnyRunning () const {
@@ -609,44 +691,78 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus {
609
691
}
610
692
ActiveActorStatus withRunning () const {
611
693
uint32_t flags = (Flags & ~ActorStateMask) | Running;
694
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
695
+ return ActiveActorStatus (flags, dispatch_lock_value_for_self (), FirstJob);
696
+ #else
612
697
return ActiveActorStatus (flags, FirstJob);
698
+ #endif
613
699
}
614
700
615
701
bool isScheduled () const {
616
702
return (Flags & ActorStateMask) == Scheduled;
617
703
}
704
+
618
705
ActiveActorStatus withScheduled () const {
706
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
707
+ return ActiveActorStatus ((Flags & ~ActorStateMask) | Scheduled, DLOCK_OWNER_NULL, FirstJob);
708
+ #else
619
709
return ActiveActorStatus ((Flags & ~ActorStateMask) | Scheduled, FirstJob);
710
+ #endif
620
711
}
621
712
622
713
bool isZombie_ReadyForDeallocation () const {
623
714
return (Flags & ActorStateMask) == Zombie_ReadyForDeallocation;
624
715
}
625
716
ActiveActorStatus withZombie_ReadyForDeallocation () const {
717
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
718
+ assert (dispatch_lock_owner (DrainLock) != DLOCK_OWNER_NULL);
719
+ return ActiveActorStatus ((Flags & ~ActorStateMask) | Zombie_ReadyForDeallocation, DrainLock, FirstJob);
720
+ #else
626
721
return ActiveActorStatus ((Flags & ~ActorStateMask) | Zombie_ReadyForDeallocation, FirstJob);
722
+ #endif
627
723
}
628
724
629
725
JobPriority getMaxPriority () const {
630
726
return (JobPriority) ((Flags & PriorityMask) >> PriorityShift);
631
727
}
632
728
ActiveActorStatus withMaxPriority (JobPriority priority) const {
633
- return ActiveActorStatus ((Flags & ~PriorityMask) | (uint32_t (priority) << PriorityShift) , FirstJob);
729
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
730
+ return ActiveActorStatus ((Flags & ~PriorityMask) | (uint32_t (priority) << PriorityShift), DrainLock, FirstJob);
731
+ #else
732
+ return ActiveActorStatus ((Flags & ~PriorityMask) | (uint32_t (priority) << PriorityShift), FirstJob);
733
+ #endif
634
734
}
635
735
636
736
JobRef getFirstJob () const {
637
737
return FirstJob;
638
738
}
639
739
ActiveActorStatus withFirstJob (JobRef firstJob) const {
740
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
741
+ return ActiveActorStatus (Flags, DrainLock, firstJob);
742
+ #else
640
743
return ActiveActorStatus (Flags, firstJob);
744
+ #endif
641
745
}
642
746
643
747
uint32_t getOpaqueFlags () const {
644
748
return Flags;
645
749
}
750
+ uint32_t currentDrainer () const {
751
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
752
+ return dispatch_lock_owner (DrainLock);
753
+ #else
754
+ return 0 ;
755
+ #endif
756
+ }
646
757
};
647
758
759
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
760
+ static_assert (sizeof (ActiveActorStatus) == 4 * sizeof(uintptr_t ),
761
+ "ActiveActorStatus is 4 words large");
762
+ #else
648
763
static_assert (sizeof (ActiveActorStatus) == 2 * sizeof(uintptr_t ),
649
- " ActiveTaskStatus is 2 words large" );
764
+ "ActiveActorStatus is 2 words large");
765
+ #endif
650
766
651
767
// / The default actor implementation.
652
768
// /
0 commit comments