@@ -231,7 +231,28 @@ class TaskFutureWaitAsyncContext : public AsyncContext {
231231// / only need to do double wide atomics if we need to reach for the
232232// / StatusRecord pointers and therefore have to update the flags at the same
233233// / 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 {
235256 enum : uint32_t {
236257 // / The max priority of the task. This is always >= basePriority in the task
237258 PriorityMask = 0xFF ,
@@ -465,12 +486,12 @@ class alignas(sizeof(void*) * 2) ActiveTaskStatus {
465486};
466487
467488#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 )))
470490#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 )))
473492#endif
493+ static_assert (sizeof (ActiveTaskStatus) == ACTIVE_TASK_STATUS_SIZE,
494+ " ActiveTaskStatus is of incorrect size" );
474495
475496// / The size of an allocator slab. We want the full allocation to fit into a
476497// / 1024-byte malloc quantum. We subtract off the slab header size, plus a
@@ -483,9 +504,16 @@ using TaskAllocator = StackAllocator<SlabCapacity, &TaskAllocatorSlabMetadata>;
483504
484505// / Private storage in an AsyncTask object.
485506struct 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)];
489517
490518 // / The allocator for the task stack.
491519 // / Currently 2 words + 8 bytes.
@@ -495,13 +523,6 @@ struct AsyncTask::PrivateStorage {
495523 // / Currently one word.
496524 TaskLocal::Storage Local;
497525
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-
505526 // / The top 32 bits of the task ID. The bottom 32 bits are in Job::Id.
506527 uint32_t Id;
507528
@@ -516,19 +537,22 @@ struct AsyncTask::PrivateStorage {
516537 // Always create an async task with max priority in ActiveTaskStatus = base
517538 // priority. It will be updated later if needed.
518539 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+ }
521543
522544 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+ }
525549
526550 // / Called on the thread that was previously executing the task that we are
527551 // / now trying to complete.
528552 void complete (AsyncTask *task) {
529553 // Drain unlock the task and remove any overrides on thread as a
530554 // 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);
532556 while (true ) {
533557 // Task is completing, it shouldn't have any records and therefore
534558 // cannot be status record locked.
@@ -544,7 +568,7 @@ struct AsyncTask::PrivateStorage {
544568
545569 // This can fail since the task can still get concurrently cancelled or
546570 // escalated.
547- if (task->_private ().Status .compare_exchange_weak (oldStatus, newStatus,
571+ if (task->_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
548572 /* success */ std::memory_order_relaxed,
549573 /* failure */ std::memory_order_relaxed)) {
550574#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
@@ -563,12 +587,21 @@ struct AsyncTask::PrivateStorage {
563587
564588 this ->~PrivateStorage ();
565589 }
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+ }
566598};
567599
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),
572605 " Task-private storage doesn't fit in reserved space" );
573606
574607inline AsyncTask::PrivateStorage &
@@ -601,7 +634,7 @@ inline const AsyncTask::PrivateStorage &AsyncTask::_private() const {
601634}
602635
603636inline bool AsyncTask::isCancelled () const {
604- return _private ().Status .load (std::memory_order_relaxed)
637+ return _private ()._status () .load (std::memory_order_relaxed)
605638 .isCancelled ();
606639}
607640
@@ -614,7 +647,7 @@ inline void AsyncTask::flagAsRunning() {
614647 qos_class_t overrideFloor = threadOverrideInfo.override_qos_floor ;
615648retry:;
616649#endif
617- auto oldStatus = _private ().Status .load (std::memory_order_relaxed);
650+ auto oldStatus = _private ()._status () .load (std::memory_order_relaxed);
618651 while (true ) {
619652 // We can get here from being suspended or being enqueued
620653 assert (!oldStatus.isRunning ());
@@ -638,7 +671,7 @@ retry:;
638671 newStatus = newStatus.withoutStoredPriorityEscalation ();
639672 newStatus = newStatus.withoutEnqueued ();
640673
641- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
674+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
642675 /* success */ std::memory_order_relaxed,
643676 /* failure */ std::memory_order_relaxed)) {
644677 newStatus.traceStatusChanged (this );
@@ -666,10 +699,10 @@ retry:;
666699// / original enqueueing thread.
667700// /
668701// / rdar://88366470 (Direct handoff behaviour when tasks switch executors)
669- inline void AsyncTask::flagAsEnqueuedOnExecutor (ExecutorRef newExecutor) {
702+ inline void AsyncTask::flagAsAndEnqueueOnExecutor (ExecutorRef newExecutor) {
670703
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);
673706 auto newStatus = oldStatus;
674707
675708 while (true ) {
@@ -684,7 +717,7 @@ inline void AsyncTask::flagAsEnqueuedOnExecutor(ExecutorRef newExecutor) {
684717 newStatus = newStatus.withoutStoredPriorityEscalation ();
685718 newStatus = newStatus.withEnqueued ();
686719
687- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
720+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
688721 /* success */ std::memory_order_relaxed,
689722 /* failure */ std::memory_order_relaxed)) {
690723 break ;
@@ -715,7 +748,7 @@ inline void AsyncTask::flagAsEnqueuedOnExecutor(ExecutorRef newExecutor) {
715748
716749inline void AsyncTask::flagAsSuspended () {
717750 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);
719752 auto newStatus = oldStatus;
720753 while (true ) {
721754 // We can only be suspended if we were previously running. See state
@@ -725,7 +758,7 @@ inline void AsyncTask::flagAsSuspended() {
725758 newStatus = oldStatus.withRunning (false );
726759 newStatus = newStatus.withoutStoredPriorityEscalation ();
727760
728- if (_private ().Status .compare_exchange_weak (oldStatus, newStatus,
761+ if (_private ()._status () .compare_exchange_weak (oldStatus, newStatus,
729762 /* success */ std::memory_order_relaxed,
730763 /* failure */ std::memory_order_relaxed)) {
731764 break ;
0 commit comments