@@ -273,22 +273,36 @@ class MPMCConcurrentQueue
273273 return score::cpp::blank{};
274274 }
275275
276+ // / @brief Helper type to align members to the cache lines.
277+ // / @details Using padding rather than alignas(CacheLineSize) so that
278+ // / ASan's fake stack doesn't cause UBSan misalignment.
279+ template <class Atomic >
280+ struct CacheLinePaddedAtomic : public std ::atomic<Atomic>
281+ {
282+ static_assert (sizeof (std::atomic<Atomic>) < CacheLineSize,
283+ " atomic<Atomic> is too large to pad to one cache line" );
284+ using std::atomic<Atomic>::atomic;
285+
286+ private:
287+ char _pad[CacheLineSize - sizeof (std::atomic<Atomic>)]{};
288+ };
289+
276290 // / @brief Underlying storage.
277291 std::array<Slot, Capacity> m_slots;
278292
279293 // / @brief The front of the queue; claimed by consumers via fetch_add in pop.
280294 // / @details Aligned so that m_head and m_tail do not share a cache line.
281- alignas (CacheLineSize) std::atomic <std::size_t > m_head{0 };
295+ CacheLinePaddedAtomic <std::size_t > m_head{0 };
282296
283297 // / @brief The back of the queue; claimed by producers via fetch_add in push_impl.
284298 // / @details Aligned so that m_head and m_tail do not share a cache line.
285- alignas (CacheLineSize) std::atomic <std::size_t > m_tail{0 };
299+ CacheLinePaddedAtomic <std::size_t > m_tail{0 };
286300
287301 // / @brief Set to true by stop(); causes push() to return false and pop() to
288302 // / return std::nullopt instead of blocking.
289303 // / @details Aligned on its own cache line so that the single stop() write
290304 // / does not cause false sharing with m_tail updates in push_impl().
291- alignas (CacheLineSize) std::atomic <bool > m_stopped{false };
305+ CacheLinePaddedAtomic <bool > m_stopped{false };
292306
293307 // / @brief Counts items currently in the queue; consumers block on this when
294308 // / the queue is empty.
0 commit comments