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