xenium
chase_work_stealing_deque.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_CHASE_WORK_STEALING_DEQUE_HPP
7 #define XENIUM_CHASE_WORK_STEALING_DEQUE_HPP
8 
9 #include <xenium/detail/fixed_size_circular_array.hpp>
10 #include <xenium/detail/growing_circular_array.hpp>
11 #include <xenium/parameter.hpp>
12 #include <xenium/policy.hpp>
13 
14 #include <atomic>
15 #include <cassert>
16 
17 namespace xenium {
37 template <class T, class... Policies>
39  using value_type = T*;
40  static constexpr std::size_t capacity =
41  parameter::value_param_t<std::size_t, policy::capacity, 128, Policies...>::value;
42  using container =
43  parameter::type_param_t<policy::container, detail::growing_circular_array<T, capacity>, Policies...>;
44 
46 
49 
50  chase_work_stealing_deque& operator=(const chase_work_stealing_deque&) = delete;
52 
53  bool try_push(value_type item);
54  [[nodiscard]] bool try_pop(value_type& result);
55  [[nodiscard]] bool try_steal(value_type& result);
56 
57  std::size_t size() {
58  auto t = _top.load(std::memory_order_relaxed);
59  return _bottom.load(std::memory_order_relaxed) - t;
60  }
61 
62 private:
63  container _items;
64  std::atomic<std::size_t> _bottom;
65  std::atomic<std::size_t> _top;
66 };
67 
68 template <class T, class... Policies>
70 
71 template <class T, class... Policies>
72 bool chase_work_stealing_deque<T, Policies...>::try_push(value_type item) {
73  auto b = _bottom.load(std::memory_order_relaxed);
74  auto t = _top.load(std::memory_order_relaxed);
75  auto size = b - t;
76  if (size >= _items.capacity()) {
77  if (_items.can_grow()) {
78  _items.grow(b, t);
79  assert(size < _items.capacity());
80  // TODO - need to update _top??
81  } else {
82  return false;
83  }
84  }
85 
86  _items.put(b, item, std::memory_order_relaxed);
87 
88  // (1) - this release-store synchronizes-with the seq-cst-load (4)
89  _bottom.store(b + 1, std::memory_order_release);
90  return true;
91 }
92 
93 template <class T, class... Policies>
94 bool chase_work_stealing_deque<T, Policies...>::try_pop(value_type& result) {
95  auto b = _bottom.load(std::memory_order_relaxed);
96  auto t = _top.load(std::memory_order_relaxed);
97  if (b == t) {
98  return false;
99  }
100 
101  // We have to use seq-cst order for operations on _bottom as well as _top to ensure
102  // that when two threads compete for the last item either one sees the updated _bottom
103  // (pop wins), or one sees the updated _top (steal wins).
104 
105  --b;
106  // (2) - this seq-cst-store enforces a total order with the seq-cst-load (4)
107  _bottom.store(b, std::memory_order_seq_cst);
108 
109  auto* item = _items.get(b, std::memory_order_relaxed);
110  // (3) - this seq-cst-load enforces a total order with the seq-cst-CAS (5)
111  t = _top.load(std::memory_order_seq_cst);
112  if (b > t) {
113  result = item;
114  return true;
115  }
116 
117  if (b == t) {
118  if (_top.compare_exchange_strong(t, t + 1, std::memory_order_relaxed)) {
119  _bottom.store(t + 1, std::memory_order_relaxed);
120  result = item;
121  return true;
122  }
123  _bottom.store(t, std::memory_order_relaxed);
124  return false;
125  }
126 
127  assert(b == t - 1);
128  _bottom.store(t, std::memory_order_relaxed);
129  return false;
130 }
131 
132 template <class T, class... Policies>
133 bool chase_work_stealing_deque<T, Policies...>::try_steal(value_type& result) {
134  auto t = _top.load(std::memory_order_relaxed);
135 
136  // (4) - this seq-cst-load enforces a total order with the seq-cst-store (2)
137  // and synchronizes-with the release-store (1)
138  auto b = _bottom.load(std::memory_order_seq_cst);
139  auto size = static_cast<std::intptr_t>(b) - static_cast<std::intptr_t>(t);
140  if (size <= 0) {
141  return false;
142  }
143 
144  auto* item = _items.get(t, std::memory_order_relaxed);
145  // (5) - this seq-cst-CAS enforces a total order with the seq-cst-load (3)
146  if (_top.compare_exchange_strong(t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
147  result = item;
148  return true;
149  }
150 
151  return false;
152 }
153 } // namespace xenium
154 
155 #endif
xenium::chase_work_stealing_deque
A lock-free work stealing deque.
Definition: chase_work_stealing_deque.hpp:38
xenium::policy::capacity
Policy to configure the capacity of various containers.
Definition: policy.hpp:61