@@ -231,7 +231,28 @@ class TaskFutureWaitAsyncContext : public AsyncContext {
231
231
// / only need to do double wide atomics if we need to reach for the
232
232
// / StatusRecord pointers and therefore have to update the flags at the same
233
233
// / time.
234
- class alignas (sizeof (void *) * 2 ) ActiveTaskStatus {
234
+ // /
235
+ // / Size requirements:
236
+ // / On 64 bit systems or if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION=1,
237
+ // / the field is 16 bytes long.
238
+ // /
239
+ // / Otherwise, it is 8 bytes long.
240
+ // /
241
+ // / Alignment requirements:
242
+ // / On 64 bit systems or if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION=1,
243
+ // / this 16-byte field needs to be 16 byte aligned to be able to do aligned
244
+ // / atomic stores field.
245
+ // /
246
+ // / On all other systems, it needs to be 8 byte aligned for the atomic
247
+ // / stores.
248
+ // /
249
+ // / As a result of varying alignment needs, we've marked the class as
250
+ // / needing 2-word alignment but on arm64_32 with
251
+ // / SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION=1, 16 byte alignment is
252
+ // / achieved through careful arrangement of the storage for this in the
253
+ // / AsyncTask::PrivateStorage. The additional alignment requirements are
254
+ // / enforced by static asserts below.
255
+ class alignas (2 * sizeof (void *)) ActiveTaskStatus {
235
256
enum : uint32_t {
236
257
// / The max priority of the task. This is always >= basePriority in the task
237
258
PriorityMask = 0xFF ,
@@ -465,12 +486,12 @@ class alignas(sizeof(void*) * 2) ActiveTaskStatus {
465
486
};
466
487
467
488
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
468
- static_assert (sizeof (ActiveTaskStatus) == 4 * sizeof(uintptr_t ),
469
- "ActiveTaskStatus is 4 words large");
489
+ #define ACTIVE_TASK_STATUS_SIZE (4 * (sizeof (uintptr_t )))
470
490
#else
471
- static_assert (sizeof (ActiveTaskStatus) == 2 * sizeof(uintptr_t ),
472
- "ActiveTaskStatus is 2 words large");
491
+ #define ACTIVE_TASK_STATUS_SIZE (2 * (sizeof (uintptr_t )))
473
492
#endif
493
+ static_assert (sizeof (ActiveTaskStatus) == ACTIVE_TASK_STATUS_SIZE,
494
+ " ActiveTaskStatus is of incorrect size" );
474
495
475
496
// / The size of an allocator slab. We want the full allocation to fit into a
476
497
// / 1024-byte malloc quantum. We subtract off the slab header size, plus a
@@ -483,9 +504,16 @@ using TaskAllocator = StackAllocator<SlabCapacity, &TaskAllocatorSlabMetadata>;
483
504
484
505
// / Private storage in an AsyncTask object.
485
506
struct AsyncTask ::PrivateStorage {
486
- // / The currently-active information about cancellation.
487
- // / Currently two words.
488
- swift::atomic<ActiveTaskStatus> Status;
507
+ // / State inside the AsyncTask whose state is only managed by the exclusivity
508
+ // / runtime in stdlibCore. We zero initialize to provide a safe initial value,
509
+ // / but actually initialize its bit state to a const global provided by
510
+ // / libswiftCore so that libswiftCore can control the layout of our initial
511
+ // / state.
512
+ uintptr_t ExclusivityAccessSet[2 ] = {0 , 0 };
513
+
514
+ // / Storage for the ActiveTaskStatus. See doc for ActiveTaskStatus for size
515
+ // / and alignment requirements.
516
+ alignas (2 * sizeof (void *)) char StatusStorage[sizeof (ActiveTaskStatus)];
489
517
490
518
// / The allocator for the task stack.
491
519
// / Currently 2 words + 8 bytes.
@@ -495,13 +523,6 @@ struct AsyncTask::PrivateStorage {
495
523
// / Currently one word.
496
524
TaskLocal::Storage Local;
497
525
498
- // / State inside the AsyncTask whose state is only managed by the exclusivity
499
- // / runtime in stdlibCore. We zero initialize to provide a safe initial value,
500
- // / but actually initialize its bit state to a const global provided by
501
- // / libswiftCore so that libswiftCore can control the layout of our initial
502
- // / state.
503
- uintptr_t ExclusivityAccessSet[2 ] = {0 , 0 };
504
-
505
526
// / The top 32 bits of the task ID. The bottom 32 bits are in Job::Id.
506
527
uint32_t Id;
507
528
@@ -516,19 +537,22 @@ struct AsyncTask::PrivateStorage {
516
537
// Always create an async task with max priority in ActiveTaskStatus = base
517
538
// priority. It will be updated later if needed.
518
539
PrivateStorage (JobPriority basePri)
519
- : Status(ActiveTaskStatus(basePri)), Local(TaskLocal::Storage()),
520
- BasePriority (basePri) {}
540
+ : Local(TaskLocal::Storage()), BasePriority(basePri) {
541
+ _status ().store (ActiveTaskStatus (basePri), std::memory_order_relaxed);
542
+ }
521
543
522
544
PrivateStorage (JobPriority basePri, void *slab, size_t slabCapacity)
523
- : Status(ActiveTaskStatus(basePri)), Allocator(slab, slabCapacity),
524
- Local(TaskLocal::Storage()), BasePriority(basePri) {}
545
+ : Allocator(slab, slabCapacity), Local(TaskLocal::Storage()),
546
+ BasePriority (basePri) {
547
+ _status ().store (ActiveTaskStatus (basePri), std::memory_order_relaxed);
548
+ }
525
549
526
550
// / Called on the thread that was previously executing the task that we are
527
551
// / now trying to complete.
528
552
void complete (AsyncTask *task) {
529
553
// Drain unlock the task and remove any overrides on thread as a
530
554
// result of the task
531
- auto oldStatus = task->_private ().Status .load (std::memory_order_relaxed);
555
+ auto oldStatus = task->_private ()._status () .load (std::memory_order_relaxed);
532
556
while (true ) {
533
557
// Task is completing, it shouldn't have any records and therefore
534
558
// cannot be status record locked.
@@ -544,7 +568,7 @@ struct AsyncTask::PrivateStorage {
544
568
545
569
// This can fail since the task can still get concurrently cancelled or
546
570
// escalated.
547
- if (task->_private ().Status .compare_exchange_weak (oldStatus, newStatus,
571
+ if (task->_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
548
572
/* success */ std::memory_order_relaxed,
549
573
/* failure */ std::memory_order_relaxed)) {
550
574
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
@@ -563,12 +587,21 @@ struct AsyncTask::PrivateStorage {
563
587
564
588
this ->~PrivateStorage ();
565
589
}
590
+
591
+ swift::atomic<ActiveTaskStatus> &_status () {
592
+ return reinterpret_cast <swift::atomic<ActiveTaskStatus>&> (this ->StatusStorage );
593
+ }
594
+
595
+ const swift::atomic<ActiveTaskStatus> &_status () const {
596
+ return reinterpret_cast <const swift::atomic<ActiveTaskStatus>&> (this ->StatusStorage );
597
+ }
566
598
};
567
599
568
- static_assert (sizeof (AsyncTask::PrivateStorage)
569
- <= sizeof(AsyncTask::OpaquePrivateStorage) &&
570
- alignof(AsyncTask::PrivateStorage)
571
- <= alignof(AsyncTask::OpaquePrivateStorage),
600
+ // It will be aligned to 2 words on all platforms. On arm64_32, we have an
601
+ // additional requirement where it is aligned to 4 words.
602
+ static_assert (((offsetof(AsyncTask, Private) + offsetof(AsyncTask::PrivateStorage, StatusStorage)) % ACTIVE_TASK_STATUS_SIZE == 0),
603
+ "StatusStorage is not aligned in the AsyncTask");
604
+ static_assert (sizeof (AsyncTask::PrivateStorage) <= sizeof(AsyncTask::OpaquePrivateStorage),
572
605
"Task-private storage doesn't fit in reserved space");
573
606
574
607
inline AsyncTask::PrivateStorage &
@@ -601,7 +634,7 @@ inline const AsyncTask::PrivateStorage &AsyncTask::_private() const {
601
634
}
602
635
603
636
inline bool AsyncTask::isCancelled () const {
604
- return _private ().Status .load (std::memory_order_relaxed)
637
+ return _private ()._status () .load (std::memory_order_relaxed)
605
638
.isCancelled ();
606
639
}
607
640
@@ -614,7 +647,7 @@ inline void AsyncTask::flagAsRunning() {
614
647
qos_class_t overrideFloor = threadOverrideInfo.override_qos_floor ;
615
648
retry:;
616
649
#endif
617
- auto oldStatus = _private ().Status .load (std::memory_order_relaxed);
650
+ auto oldStatus = _private ()._status () .load (std::memory_order_relaxed);
618
651
while (true ) {
619
652
// We can get here from being suspended or being enqueued
620
653
assert (!oldStatus.isRunning ());
@@ -638,7 +671,7 @@ retry:;
638
671
newStatus = newStatus.withoutStoredPriorityEscalation ();
639
672
newStatus = newStatus.withoutEnqueued ();
640
673
641
- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
674
+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
642
675
/* success */ std::memory_order_relaxed,
643
676
/* failure */ std::memory_order_relaxed)) {
644
677
newStatus.traceStatusChanged (this );
@@ -666,10 +699,10 @@ retry:;
666
699
// / original enqueueing thread.
667
700
// /
668
701
// / rdar://88366470 (Direct handoff behaviour when tasks switch executors)
669
- inline void AsyncTask::flagAsEnqueuedOnExecutor (ExecutorRef newExecutor) {
702
+ inline void AsyncTask::flagAsAndEnqueueOnExecutor (ExecutorRef newExecutor) {
670
703
671
- SWIFT_TASK_DEBUG_LOG (" %p->flagAsEnqueuedOnExecutor ()" , this );
672
- auto oldStatus = _private ().Status .load (std::memory_order_relaxed);
704
+ SWIFT_TASK_DEBUG_LOG (" %p->flagAsAndEnqueueOnExecutor ()" , this );
705
+ auto oldStatus = _private ()._status () .load (std::memory_order_relaxed);
673
706
auto newStatus = oldStatus;
674
707
675
708
while (true ) {
@@ -684,7 +717,7 @@ inline void AsyncTask::flagAsEnqueuedOnExecutor(ExecutorRef newExecutor) {
684
717
newStatus = newStatus.withoutStoredPriorityEscalation ();
685
718
newStatus = newStatus.withEnqueued ();
686
719
687
- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
720
+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
688
721
/* success */ std::memory_order_relaxed,
689
722
/* failure */ std::memory_order_relaxed)) {
690
723
break ;
@@ -715,7 +748,7 @@ inline void AsyncTask::flagAsEnqueuedOnExecutor(ExecutorRef newExecutor) {
715
748
716
749
inline void AsyncTask::flagAsSuspended () {
717
750
SWIFT_TASK_DEBUG_LOG (" %p->flagAsSuspended()" , this );
718
- auto oldStatus = _private ().Status .load (std::memory_order_relaxed);
751
+ auto oldStatus = _private ()._status () .load (std::memory_order_relaxed);
719
752
auto newStatus = oldStatus;
720
753
while (true ) {
721
754
// We can only be suspended if we were previously running. See state
@@ -725,7 +758,7 @@ inline void AsyncTask::flagAsSuspended() {
725
758
newStatus = oldStatus.withRunning (false );
726
759
newStatus = newStatus.withoutStoredPriorityEscalation ();
727
760
728
- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
761
+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
729
762
/* success */ std::memory_order_relaxed,
730
763
/* failure */ std::memory_order_relaxed)) {
731
764
break ;
0 commit comments