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