@@ -229,7 +229,28 @@ class TaskFutureWaitAsyncContext : public AsyncContext {
229229// / only need to do double wide atomics if we need to reach for the
230230// / StatusRecord pointers and therefore have to update the flags at the same
231231// / 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 {
233254 enum : uint32_t {
234255 // / The max priority of the task. This is always >= basePriority in the task
235256 PriorityMask = 0xFF ,
@@ -463,12 +484,12 @@ class alignas(sizeof(void*) * 2) ActiveTaskStatus {
463484};
464485
465486#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 )))
468488#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 )))
471490#endif
491+ static_assert (sizeof (ActiveTaskStatus) == ACTIVE_TASK_STATUS_SIZE,
492+ " ActiveTaskStatus is of incorrect size" );
472493
473494// / The size of an allocator slab. We want the full allocation to fit into a
474495// / 1024-byte malloc quantum. We subtract off the slab header size, plus a
@@ -481,9 +502,16 @@ using TaskAllocator = StackAllocator<SlabCapacity, &TaskAllocatorSlabMetadata>;
481502
482503// / Private storage in an AsyncTask object.
483504struct 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)];
487515
488516 // / The allocator for the task stack.
489517 // / Currently 2 words + 8 bytes.
@@ -493,13 +521,6 @@ struct AsyncTask::PrivateStorage {
493521 // / Currently one word.
494522 TaskLocal::Storage Local;
495523
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-
503524 // / The top 32 bits of the task ID. The bottom 32 bits are in Job::Id.
504525 uint32_t Id;
505526
@@ -514,19 +535,22 @@ struct AsyncTask::PrivateStorage {
514535 // Always create an async task with max priority in ActiveTaskStatus = base
515536 // priority. It will be updated later if needed.
516537 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+ }
519541
520542 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+ }
523547
524548 // / Called on the thread that was previously executing the task that we are
525549 // / now trying to complete.
526550 void complete (AsyncTask *task) {
527551 // Drain unlock the task and remove any overrides on thread as a
528552 // 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);
530554 while (true ) {
531555 // Task is completing, it shouldn't have any records and therefore
532556 // cannot be status record locked.
@@ -542,7 +566,7 @@ struct AsyncTask::PrivateStorage {
542566
543567 // This can fail since the task can still get concurrently cancelled or
544568 // escalated.
545- if (task->_private ().Status .compare_exchange_weak (oldStatus, newStatus,
569+ if (task->_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
546570 /* success */ std::memory_order_relaxed,
547571 /* failure */ std::memory_order_relaxed)) {
548572#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
@@ -561,12 +585,21 @@ struct AsyncTask::PrivateStorage {
561585
562586 this ->~PrivateStorage ();
563587 }
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+ }
564596};
565597
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),
570603 " Task-private storage doesn't fit in reserved space" );
571604
572605inline AsyncTask::PrivateStorage &
@@ -599,7 +632,7 @@ inline const AsyncTask::PrivateStorage &AsyncTask::_private() const {
599632}
600633
601634inline bool AsyncTask::isCancelled () const {
602- return _private ().Status .load (std::memory_order_relaxed)
635+ return _private ()._status () .load (std::memory_order_relaxed)
603636 .isCancelled ();
604637}
605638
@@ -612,7 +645,7 @@ inline void AsyncTask::flagAsRunning() {
612645 qos_class_t overrideFloor = threadOverrideInfo.override_qos_floor ;
613646retry:;
614647#endif
615- auto oldStatus = _private ().Status .load (std::memory_order_relaxed);
648+ auto oldStatus = _private ()._status () .load (std::memory_order_relaxed);
616649 while (true ) {
617650 // We can get here from being suspended or being enqueued
618651 assert (!oldStatus.isRunning ());
@@ -636,7 +669,7 @@ retry:;
636669 newStatus = newStatus.withoutStoredPriorityEscalation ();
637670 newStatus = newStatus.withoutEnqueued ();
638671
639- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
672+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
640673 /* success */ std::memory_order_relaxed,
641674 /* failure */ std::memory_order_relaxed)) {
642675 newStatus.traceStatusChanged (this );
@@ -667,7 +700,7 @@ retry:;
667700inline void AsyncTask::flagAsEnqueuedOnExecutor (ExecutorRef newExecutor) {
668701
669702 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);
671704 auto newStatus = oldStatus;
672705
673706 while (true ) {
@@ -682,7 +715,7 @@ inline void AsyncTask::flagAsEnqueuedOnExecutor(ExecutorRef newExecutor) {
682715 newStatus = newStatus.withoutStoredPriorityEscalation ();
683716 newStatus = newStatus.withEnqueued ();
684717
685- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
718+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
686719 /* success */ std::memory_order_relaxed,
687720 /* failure */ std::memory_order_relaxed)) {
688721 break ;
@@ -713,7 +746,7 @@ inline void AsyncTask::flagAsEnqueuedOnExecutor(ExecutorRef newExecutor) {
713746
714747inline void AsyncTask::flagAsSuspended () {
715748 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);
717750 auto newStatus = oldStatus;
718751 while (true ) {
719752 // We can only be suspended if we were previously running. See state
@@ -723,7 +756,7 @@ inline void AsyncTask::flagAsSuspended() {
723756 newStatus = oldStatus.withRunning (false );
724757 newStatus = newStatus.withoutStoredPriorityEscalation ();
725758
726- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
759+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
727760 /* success */ std::memory_order_relaxed,
728761 /* failure */ std::memory_order_relaxed)) {
729762 break ;
0 commit comments