xenium
nikolaev_bounded_queue.hpp
1 //
2 // Copyright (c) 2018-2020 Manuel Pöter.
3 // Licensed under the MIT License. See LICENSE file in the project root for full license information.
4 //
5 
6 #ifndef XENIUM_NIKOLAEV_BOUNDED_QUEUE_HPP
7 #define XENIUM_NIKOLAEV_BOUNDED_QUEUE_HPP
8 
9 #include <xenium/parameter.hpp>
10 #include <xenium/policy.hpp>
11 #include <xenium/utils.hpp>
12 
13 #include <xenium/detail/nikolaev_scq.hpp>
14 
15 #include <atomic>
16 #include <cassert>
17 #include <cstdint>
18 #include <memory>
19 
20 namespace xenium {
41 template <class T, class... Policies>
43 public:
44  using value_type = T;
45  static constexpr unsigned pop_retries =
46  parameter::value_param_t<unsigned, policy::pop_retries, 1000, Policies...>::value;
47 
53  explicit nikolaev_bounded_queue(std::size_t capacity);
55 
58 
59  nikolaev_bounded_queue& operator=(const nikolaev_bounded_queue&) = delete;
60  nikolaev_bounded_queue& operator=(nikolaev_bounded_queue&&) = delete;
61 
70  bool try_push(value_type value);
71 
80  bool try_pop(value_type& result);
81 
85  [[nodiscard]] std::size_t capacity() const noexcept { return _capacity; }
86 
87 private:
88  using storage_t = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
89  const std::size_t _capacity;
90  const std::size_t _remap_shift;
91  std::unique_ptr<storage_t[]> _storage;
92  detail::nikolaev_scq _allocated_queue;
93  detail::nikolaev_scq _free_queue;
94 };
95 
96 template <class T, class... Policies>
98  _capacity(utils::next_power_of_two(capacity)),
99  _remap_shift(detail::nikolaev_scq::calc_remap_shift(_capacity)),
100  _storage(new storage_t[_capacity]),
101  _allocated_queue(_capacity, _remap_shift, detail::nikolaev_scq::empty_tag{}),
102  _free_queue(_capacity, _remap_shift, detail::nikolaev_scq::full_tag{}) {
103  assert(capacity > 0);
104 }
105 
106 template <class T, class... Policies>
107 nikolaev_bounded_queue<T, Policies...>::~nikolaev_bounded_queue() {
108  std::uint64_t eidx;
109  while (_allocated_queue.dequeue<false, pop_retries>(eidx, _capacity, _remap_shift)) {
110  reinterpret_cast<T&>(_storage[eidx]).~T();
111  }
112 }
113 
114 template <class T, class... Policies>
116  std::uint64_t eidx;
117  // TODO - make nonempty checks configurable
118  if (!_free_queue.dequeue<false, pop_retries>(eidx, _capacity, _remap_shift)) {
119  return false;
120  }
121 
122  assert(eidx < _capacity);
123  new (&_storage[eidx]) T(std::move(value));
124  _allocated_queue.enqueue<false, false>(eidx, _capacity, _remap_shift);
125  return true;
126 }
127 
128 template <class T, class... Policies>
130  std::uint64_t eidx;
131  // TODO - make nonempty checks configurable
132  if (!_allocated_queue.dequeue<false, pop_retries>(eidx, _capacity, _remap_shift)) {
133  return false;
134  }
135 
136  assert(eidx < _capacity);
137  T& data = reinterpret_cast<T&>(_storage[eidx]);
138  result = std::move(data);
139  data.~T(); // NOLINT (use-after-move)
140  _free_queue.enqueue<false, false>(eidx, _capacity, _remap_shift);
141  return true;
142 }
143 } // namespace xenium
144 
145 #endif
xenium::nikolaev_bounded_queue::nikolaev_bounded_queue
nikolaev_bounded_queue(std::size_t capacity)
Constructs a new instance with the specified maximum size.
Definition: nikolaev_bounded_queue.hpp:97
xenium::policy::pop_retries
Policy to configure the number of iterations to spin on a queue entry while waiting for a pending pus...
Definition: policy.hpp:124
xenium::nikolaev_bounded_queue::try_pop
bool try_pop(value_type &result)
Tries to pop an element from the queue.
Definition: nikolaev_bounded_queue.hpp:129
xenium::nikolaev_bounded_queue::try_push
bool try_push(value_type value)
Tries to push a new element to the queue.
Definition: nikolaev_bounded_queue.hpp:115
xenium::nikolaev_bounded_queue
A bounded lock-free multi-producer/multi-consumer queue.
Definition: nikolaev_bounded_queue.hpp:42
xenium::nikolaev_bounded_queue::capacity
std::size_t capacity() const noexcept
Returns the (rounded) capacity of the queue.
Definition: nikolaev_bounded_queue.hpp:85