Skip to content

Commit

Permalink
Don't require std::atomic to be trivial. Fixes C++20 compile errors. R…
Browse files Browse the repository at this point in the history
…esolves #66.
  • Loading branch information
Maxim Egorushkin committed Mar 4, 2024
1 parent bad99b6 commit eb0a1bd
Showing 1 changed file with 12 additions and 9 deletions.
21 changes: 12 additions & 9 deletions include/atomic_queue/atomic_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,10 @@ constexpr T nil() noexcept {
return {};
}

template<class T, class U>
inline void trivial_uninitialized_fill_n(std::atomic<T>* p, unsigned n, U value) noexcept {
static_assert(std::is_trivial<std::atomic<T>>::value, "std::atomic<T> must be a trivial type, its constructor and destructor aren't invoked.");
for(auto q = p + n; p < q; ++p)
p->store(value, X);
template<class T>
inline void destroy_n(T* p, unsigned n) noexcept {
for(auto q = p + n; p != q;)
(p++)->~T();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -379,7 +378,8 @@ class AtomicQueue : public AtomicQueueCommon<AtomicQueue<T, SIZE, NIL, MINIMIZE_

AtomicQueue() noexcept {
assert(std::atomic<T>{NIL}.is_lock_free()); // Queue element type T is not atomic. Use AtomicQueue2/AtomicQueueB2 for such element types.
details::trivial_uninitialized_fill_n(elements_, size_, NIL);
for(auto p = elements_, q = elements_ + size_; p != q; ++p)
p->store(NIL, X);
}

AtomicQueue(AtomicQueue const&) = delete;
Expand Down Expand Up @@ -467,7 +467,7 @@ class AtomicQueueB : private std::allocator_traits<A>::template rebind_alloc<std
, size_(std::max(details::round_up_to_power_of_2(size), 1u << (SHUFFLE_BITS * 2)))
, elements_(AllocatorElements::allocate(size_)) {
assert(std::atomic<T>{NIL}.is_lock_free()); // Queue element type T is not atomic. Use AtomicQueue2/AtomicQueueB2 for such element types.
details::trivial_uninitialized_fill_n(elements_, size_, NIL);
std::uninitialized_fill_n(elements_, size_, NIL);
assert(get_allocator() == allocator); // The standard requires the original and rebound allocators to manage the same state.
}

Expand All @@ -484,8 +484,10 @@ class AtomicQueueB : private std::allocator_traits<A>::template rebind_alloc<std
}

~AtomicQueueB() noexcept {
if(elements_)
if(elements_) {
details::destroy_n(elements_, size_);
AllocatorElements::deallocate(elements_, size_); // TODO: This must be noexcept, static_assert that.
}
}

A get_allocator() const noexcept {
Expand Down Expand Up @@ -566,7 +568,7 @@ class AtomicQueueB2 : private std::allocator_traits<A>::template rebind_alloc<un
, size_(std::max(details::round_up_to_power_of_2(size), 1u << (SHUFFLE_BITS * 2)))
, states_(allocate_<AtomicState>())
, elements_(allocate_<T>()) {
details::trivial_uninitialized_fill_n(states_, size_, Base::EMPTY);
std::uninitialized_fill_n(states_, size_, Base::EMPTY);
A a = get_allocator();
assert(a == allocator); // The standard requires the original and rebound allocators to manage the same state.
for(auto p = elements_, q = elements_ + size_; p < q; ++p)
Expand All @@ -592,6 +594,7 @@ class AtomicQueueB2 : private std::allocator_traits<A>::template rebind_alloc<un
for(auto p = elements_, q = elements_ + size_; p < q; ++p)
std::allocator_traits<A>::destroy(a, p);
deallocate_(elements_);
details::destroy_n(states_, size_);
deallocate_(states_);
}
}
Expand Down

0 comments on commit eb0a1bd

Please sign in to comment.