Library of Assembled Shared Sources
 
Loading...
Searching...
No Matches
lock_free_spmc_ring_buffer.inl
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2026 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
44
45namespace lass
46{
47namespace stde
48{
49namespace impl
50{
51
52// --- lock_free_spmc_ring_buffer_base -------------------------------------------------------------
53
54inline lock_free_spmc_ring_buffer_base::lock_free_spmc_ring_buffer_base(size_t capacity):
55 head_(tagged_index_t(0, 0)),
56 tail_(tagged_index_t(0, 0)),
57 ring_size_(static_cast<index_type>(enforce_valid_size(capacity)))
58{
59 static_assert(std::atomic<tagged_index_t>::is_always_lock_free);
60}
61
62
63/** Return true if ring buffer is empty
64 */
65inline bool lock_free_spmc_ring_buffer_base::empty() const
66{
67 return tagged_index_t::empty(head_.load(std::memory_order_acquire), tail_.load(std::memory_order_acquire));
68}
69
70
71
72/** Return true if ring buffer is empty
73 */
74inline bool lock_free_spmc_ring_buffer_base::full() const
75{
76 return tagged_index_t::full(head_.load(std::memory_order_acquire), tail_.load(std::memory_order_acquire));
77}
78
79
80
81lock_free_spmc_ring_buffer_base::tagged_index_t
82inline lock_free_spmc_ring_buffer_base::next_index(tagged_index_t index) const
83{
84 const index_type i = ++index.index;
85 return i < ring_size_
86 ? tagged_index_t(i, index.tag)
87 : tagged_index_t(0, index.tag + 1);
88}
89
90
91
92inline size_t lock_free_spmc_ring_buffer_base::enforce_valid_size(size_t size)
93{
94 if (size == 0)
95 {
96 throw std::length_error("ring buffer capacitance cannot be zero");
97 }
98 if (size > num::NumTraits<index_type>::max)
99 {
100 throw std::length_error("exceeded maximum ring buffer capacitance");
101 }
102 return size;
103}
104
105
106
107// --- lock_free_spmc_value_ring_buffer ------------------------------------------------------------
108
109template <typename T>
110lock_free_spmc_value_ring_buffer<T>::lock_free_spmc_value_ring_buffer(size_t capacity):
111 lock_free_spmc_ring_buffer_base(capacity),
112 ring_(capacity)
113{
114}
115
116
117
118/** Try to push a value x on the front.
119 * @return false if buffer was full and x could not be pushed.
120 */
121template <typename T>
122bool lock_free_spmc_value_ring_buffer<T>::try_push(const value_type& x)
123{
124 const tagged_index_t head = head_.load(std::memory_order_relaxed);
125 const tagged_index_t tail = tail_.load(std::memory_order_acquire);
126 if (tagged_index_t::full(head, tail))
127 {
128 LASS_ASSERT(head.tag == tail.tag + 1);
129 return false;
130 }
131 ring_[head.index].store(x, std::memory_order_release);
132 head_.store(next_index(head), std::memory_order_release);
133 return true;
134}
135
136
137
138/** Try to push a value x on the front.
139 * @return false if buffer was full and x could not be pushed.
140 */
141template <typename T>
142bool lock_free_spmc_value_ring_buffer<T>::try_push(value_type&& x)
143{
144 const tagged_index_t head = head_.load(std::memory_order_relaxed);
145 const tagged_index_t tail = tail_.load(std::memory_order_acquire);
146 if (tagged_index_t::full(head, tail))
147 {
148 LASS_ASSERT(head.tag == tail.tag + 1);
149 return false;
150 }
151 ring_[head.index].store(std::move(x), std::memory_order_release);
152 head_.store(next_index(head), std::memory_order_release);
153 return true;
154}
155
156
157
158/** Try to emplace a value on the front.
159 * @return false if buffer was full and value could not be pushed.
160 */
161template <typename T>
162template <class... Args>
163bool lock_free_spmc_value_ring_buffer<T>::try_emplace(Args&&... args)
164{
165 const tagged_index_t head = head_.load(std::memory_order_relaxed);
166 const tagged_index_t tail = tail_.load(std::memory_order_acquire);
167 if (tagged_index_t::full(head, tail))
168 {
169 LASS_ASSERT(head.tag == tail.tag + 1);
170 return false;
171 }
172 ring_[head.index].store(value_type(std::forward<Args...>(args...)), std::memory_order_release);
173 head_.store(next_index(head), std::memory_order_release);
174 return true;
175}
176
177
178
179/** Try to pop a value from the back and store it in x.
180 * @return false if buffer was empty and no element could be popped.
181 */
182template <typename T>
183bool lock_free_spmc_value_ring_buffer<T>::try_pop(value_type& x)
184{
185 tagged_index_t tail = tail_.load(std::memory_order_acquire);
186 while (true)
187 {
188 const tagged_index_t head = head_.load(std::memory_order_acquire);
189 if (tagged_index_t::empty(head, tail))
190 {
191 return false;
192 }
193 x = ring_[tail.index].load(std::memory_order_acquire);
194 if (tail_.compare_exchange_weak(tail, next_index(tail)))
195 {
196 return true;
197 }
198 }
199}
200
201
202
203// --- lock_free_spmc_object_ring_buffer ----------------------------------------------------------
204
205template <typename T, typename A>
206lock_free_spmc_object_ring_buffer<T, A>::lock_free_spmc_object_ring_buffer(size_t capacity):
207 lock_free_spmc_ring_buffer_base(capacity),
208 value_allocator_(sizeof(T)),
209 ring_(capacity)
210{
211}
212
213
214
215template <typename T, typename A>
216lock_free_spmc_object_ring_buffer<T, A>::~lock_free_spmc_object_ring_buffer()
217{
218 const tagged_index_t head = head_.load(std::memory_order_acquire);
219 const tagged_index_t tail = tail_.load(std::memory_order_acquire);
220 for (tagged_index_t i = tail; !tagged_index_t::empty(i, head); i = next_index(i))
221 {
222 value_type* p = ring_[i.index].load(std::memory_order_relaxed);
223 p->~value_type();
224 value_allocator_.deallocate(p);
225 }
226}
227
228
229
230/** Try to push a value x on the front.
231 * @return false if buffer was full and x could not be pushed.
232 */
233template <typename T, typename A>
234bool lock_free_spmc_object_ring_buffer<T, A>::try_push(const value_type& x)
235{
236 const tagged_index_t head = head_.load(std::memory_order_relaxed);
237 const tagged_index_t tail = tail_.load(std::memory_order_acquire);
238 if (tagged_index_t::full(head, tail))
239 {
240 return false;
241 }
242 void* p = value_allocator_.allocate();
243 try
244 {
245 ring_[head.index] = new (p) value_type(x);
246 }
247 catch (...)
248 {
249 value_allocator_.deallocate(p);
250 throw;
251 }
252 head_.store(next_index(head), std::memory_order_release);
253 return true;
254}
255
256
257
258/** Try to push a value x on the front.
259 * @return false if buffer was full and x could not be pushed.
260 */
261template <typename T, typename A>
262bool lock_free_spmc_object_ring_buffer<T, A>::try_push(value_type&& x)
263{
264 const tagged_index_t head = head_.load(std::memory_order_relaxed);
265 const tagged_index_t tail = tail_.load(std::memory_order_acquire);
266 if (tagged_index_t::full(head, tail))
267 {
268 return false;
269 }
270 void* p = value_allocator_.allocate();
271 try
272 {
273 ring_[head.index] = new (p) value_type(std::move(x));
274 }
275 catch (...)
276 {
277 value_allocator_.deallocate(p);
278 throw;
279 }
280 head_.store(next_index(head), std::memory_order_release);
281 return true;
282}
283
284
285
286/** Try to push a value x on the front.
287 * @return false if buffer was full and x could not be pushed.
288 */
289template <typename T, typename A>
290template <class... Args>
291bool lock_free_spmc_object_ring_buffer<T, A>::try_emplace(Args&&... args)
292{
293 const tagged_index_t head = head_.load(std::memory_order_relaxed);
294 const tagged_index_t tail = tail_.load(std::memory_order_acquire);
295 if (tagged_index_t::full(head, tail))
296 {
297 return false;
298 }
299 void* p = value_allocator_.allocate();
300 try
301 {
302 ring_[head.index] = new (p) value_type(std::forward<Args...>(args...));
303 }
304 catch (...)
305 {
306 value_allocator_.deallocate(p);
307 throw;
308 }
309 head_.store(next_index(head), std::memory_order_release);
310 return true;
311}
312
313
314/** Try to pop a value from the back and store it in x.
315 * @return false if buffer was empty and no element could be popped.
316 */
317template <typename T, typename A>
318bool lock_free_spmc_object_ring_buffer<T, A>::try_pop(value_type& x)
319{
320 tagged_index_t tail = tail_.load(std::memory_order_acquire);
321 while (true)
322 {
323 const tagged_index_t head = head_.load(std::memory_order_acquire);
324 if (tagged_index_t::empty(head, tail))
325 {
326 return false;
327 }
328 value_type* p = ring_[tail.index];
329 if (tail_.compare_exchange_weak(tail, next_index(tail)))
330 {
331 x = std::move(*p);
332 p->~value_type();
333 value_allocator_.deallocate(p);
334 return true;
335 }
336 }
337}
338
339
340}
341}
342}
343
344// EOF
lass extensions to the standard library
Library for Assembled Shared Sources.
Definition config.h:53