Library of Assembled Shared Sources
lock_free_stack.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-2022 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
43namespace lass
44{
45namespace stde
46{
47
48// --- public --------------------------------------------------------------------------------------
49
50template <typename T, typename A>
51lock_free_stack<T, A>::lock_free_stack():
52 util::AllocatorConcurrentFreeList<A>(sizeof(node_t)),
53 top_()
54{
55#if defined(__cpp_lib_atomic_is_always_lock_free)
56 static_assert(std::atomic<pointer_t>::is_always_lock_free);
57#else
58 LASS_ENFORCE(top_.is_lock_free());
59#endif
60}
61
62
63
64template <typename T, typename A>
65lock_free_stack<T, A>::~lock_free_stack()
66{
67 node_t* top = top_.load(std::memory_order_acquire).get();
68 while (top)
69 {
70 node_t* const next = top->next;
71 free_node(top);
72 top = next;
73 }
74}
75
76
77
78/** push value on stack.
79 * @arg exception safe: if no node of could be allocatoed, or if copy constructor of x throws,
80 * it fails gracefully.
81 */
82template <typename T, typename A>
83void lock_free_stack<T, A>::push(const value_type& x)
84{
85 emplace(x);
86}
87
88
89
90/** push value on stack.
91 */
92template <typename T, typename A>
93void lock_free_stack<T, A>::push(value_type&& x)
94{
95 emplace(std::move(x));
96}
97
98
99
100/** push value on stack.
101 */
102template <typename T, typename A>
103template <class... Args>
104void lock_free_stack<T, A>::emplace(Args&&... args)
105{
106 node_t* node = make_node();
107 try
108 {
109 new (&node->value) value_type(std::forward<Args>(args)...);
110 }
111 catch (...)
112 {
113 this->deallocate(node);
114 throw;
115 }
116 push_node(node);
117}
118
119
120
121/** Try to pop a value and copy it in @a x .
122 * @return false if stack is empty
123 * @arg strong exception safety: if copy-constructor of x throws,
124 * node is put back on top and exception is propagated
125 */
126template <typename T, typename A>
127bool lock_free_stack<T, A>::pop(value_type& x)
128{
129 node_t* const node = pop_node();
130 if (!node)
131 {
132 return false;
133 }
134 try
135 {
136 x = std::move(node->value);
137 }
138 catch (...)
139 {
140 // put it back!
141 push_node(node);
142 throw;
143 }
144 free_node(node);
145 return true;
146}
147
148
149
150/** Try to pop a value and swap it in @a x .
151 * @return false if stack is empty
152 * @arg condition on @a value_type: x.swap(y) must be a valid, non throwing operation.
153 * @arg exception safety: no-throw guarantee (if x.swap(y) plays by the rules)
154 */
155template <typename T, typename A>
156bool lock_free_stack<T, A>::pop_swap(value_type& x)
157{
158 node_t* const node = pop_node();
159 if (!node)
160 {
161 return false;
162 }
163 x.swap(node->value);
164 free_node(node);
165 return true;
166}
167
168
169
170// --- private -------------------------------------------------------------------------------------
171
172template <typename T, typename A>
173typename lock_free_stack<T, A>::node_t*
174lock_free_stack<T, A>::make_node()
175{
176 node_t* node = static_cast<node_t*>(this->allocate());
177 node->next = nullptr;
178 return node;
179}
180
181
182
183template <typename T, typename A>
184void lock_free_stack<T, A>::free_node(node_t* node)
185{
186 node->value.~value_type();
187 this->deallocate(node);
188}
189
190
191
192template <typename T, typename A>
193void lock_free_stack<T, A>::push_node(node_t* node)
194{
195 pointer_t top = top_.load(std::memory_order_acquire);
196 pointer_t new_top;
197 do
198 {
199 node->next = top.get();
200 new_top = pointer_t(node, top.nextTag());
201 }
202 while (!top_.compare_exchange_weak(top, new_top));
203}
204
205
206
207template <typename T, typename A>
208typename lock_free_stack<T, A>::node_t*
209lock_free_stack<T, A>::pop_node()
210{
211 pointer_t top = top_.load(std::memory_order_acquire);
212 pointer_t next;
213 do
214 {
215 if (!top)
216 {
217 return 0;
218 }
219
220 // This is the tricky part ... does top still exist?
221 // In theory, it can be freed by now. But by using AllocatorConcurrentFreeList,
222 // it's guaranteed that at least its memory is not reclaimed by the OS. It's
223 // either sitting unallocated in the free-list and has its memory preserved, or
224 // it's already being reallocated for a new node.
225 // In both cases, it should be safe to read top->next.
226 //
227 next = pointer_t(top->next, top.nextTag());
228 }
229 while (!top_.compare_exchange_weak(top, next));
230 return top.get();
231}
232
233}
234
235}
236
237// EOF
const lass::python::impl::IterNextSlot next("__next__", Py_tp_iternext)
__next__ method (iterator next)
lass extensions to the standard library
Library for Assembled Shared Sources.
Definition config.h:53