libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
intrusive_impl.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2011 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace intrusive {
20 
21 template<class V, class LkT>
22 inline bool
23 stack<V, LkT>::empty() const noexcept(true) {
24 // TODO assert((!prefront_.next) || (prefront_.next.get()));
25  return size_ctr==0;
26 }
27 
28 template<class V, class LkT>
29 inline typename stack<V, LkT>::size_type
30 stack<V, LkT>::size() const noexcept(true) {
31  return size_ctr.get();
32 }
33 
34 template<class V, class LkT>
35 inline typename stack<V, LkT>::size_type
36 stack<V, LkT>::size_n() const noexcept(true) {
37  size_type ret=0;
38  for ([[maybe_unused]] auto const &i: *this) {
39  ++ret;
40  }
41  return ret;
42 }
43 
44 template<class V, class LkT>
45 inline
46 stack<V, LkT>::stack() noexcept(true)
47 : prefront_(), size_ctr() {
48  // Make sure iterators to begin() don't accidentally attempt to delete prefront_! Don't need to be atomic about this.
49  prefront_.sp_acquire();
50  assert(prefront_.next.get()==nullptr || prefront_.next->sp_count());
51 }
52 
53 template<class V, class LkT>
54 inline
55 stack<V, LkT>::stack(stack &&s) noexcept(true)
56 : prefront_(), size_ctr() {
57  prefront_.next.swap(s.prefront_.next);
58  size_ctr.swap(s.size_ctr);
59 }
60 
61 template<class V, class LkT>
62 inline typename stack<V, LkT>::value_type
63 stack<V, LkT>::top() noexcept(true) {
64  assert(prefront_.next.get()==nullptr || prefront_.next->sp_count());
65  assert(prefront_.next->sp_count()>0);
66  return value_type(prefront_.next.get());
67 }
68 
69 template<class V, class LkT>
70 inline typename stack<V, LkT>::value_type
71 stack<V, LkT>::top() const noexcept(true) {
72  assert(prefront_.next.get()==nullptr || prefront_.next->sp_count());
73  assert(prefront_.next->sp_count()>0);
74  return value_type(prefront_.next.get());
75 }
76 
77 template<class V, class LkT>
78 inline typename stack<V, LkT>::iterator
79 stack<V, LkT>::begin() noexcept(true) {
80  return iterator(typename iterator::value_type(dynamic_cast<typename iterator::node_ptr_t::value_type *>(&prefront_)));
81 }
82 
83 template<class V, class LkT>
84 inline typename stack<V, LkT>::const_iterator
85 stack<V, LkT>::begin() const noexcept(true) {
86  return const_iterator(typename const_iterator::value_type(dynamic_cast<typename iterator::node_ptr_t::value_type *>(&prefront_)));
87 }
88 
89 template<class V, class LkT>
90 inline typename stack<V, LkT>::iterator
91 stack<V, LkT>::end() noexcept(true) {
92  return private_::end<V, LkT>();
93 }
94 
95 template<class V, class LkT>
96 inline typename stack<V, LkT>::const_iterator
97 stack<V, LkT>::end() const noexcept(true) {
98  return private_::end<V const, LkT>();
99 }
100 
101 template<class V, class LkT>
102 inline typename stack<V, LkT>::node_details_t::base_t::atomic_ptr_t
103 stack<V, LkT>::unlink_node(typename node_details_t::base_t::atomic_ptr_t &node) noexcept(true) {
104  using ptr_t=typename node_details_t::base_t::atomic_ptr_t;
105 // TODO return node.assign([](ptr_t const &ptr) {return ptr.get() ? ptr->next : ptr;});
106  return node.assign([](ptr_t const &ptr) {return ptr->next;});
107 }
108 
109 template<class V, class LkT>
110 inline void
111 stack<V, LkT>::pop() noexcept(true) {
112  if (!this->empty()) {
113  --this->size_ctr;
114  {
115  // Unlink the node to be deleted first, to ensure only one thread at a time reduces the reference count.
116  value_type retain_next(prefront_.next.get());
117  assert(retain_next->sp_count()>=1);
118  value_type unlinked_node(unlink_node(prefront_.next).get());
119  // Remove the list ownership and function ownership.
120  unlinked_node->sp_release();
121  assert(unlinked_node->sp_count()>=1);
122  }
123  assert(prefront_.next.get()==nullptr || prefront_.next->sp_count()>=1);
124  }
125 }
126 
127 template<class V, class LkT>
128 inline void
129 stack<V, LkT>::erase(iterator v) noexcept(true) {
130  if (!this->empty()) {
131  if (v==this->begin()) {
132  this->pop();
133  } else if (v!=this->end()) {
134  --size_ctr;
135  value_type del(*v);
136  assert(del->sp_count()>0);
137  unlink_node(v.node->next);
138  del->sp_release();
139  assert(del->sp_count()>0);
140  }
141  }
142 }
143 
144 template<class V, class LkT>
145 inline typename stack<V, LkT>::size_type
146 stack<V, LkT>::erase(const_reference v) noexcept(true) {
147  iterator start=begin();
148  while (start!=end() && *start!=v) {
149  ++start;
150  }
151  if (start!=end()) {
152  erase(start);
153  return 1;
154  } else {
155  return 0;
156  }
157 }
158 
159 template<class V, class LkT>
160 inline void
161 stack<V, LkT>::pop_front() noexcept(true) {
162  this->pop();
163 }
164 
165 template<class V, class LkT>
166 inline void
167 stack<V, LkT>::clear() noexcept(true) {
168  while (!this->empty()) {
169  this->erase(this->begin());
170  }
171  assert(this->empty());
172 }
173 
174 template<class V, class LkT>
175 inline
176 stack<V, LkT>::~stack() noexcept(true) {
177  this->clear();
178 }
179 
180 template<class V, class LkT>
181 inline void
182 stack<V, LkT>::insert(typename node_details_t::base_t::atomic_ptr_t i, value_type v) noexcept(true) {
183  assert(v->sp_count()>=0);
184  // Acquire a reference count for the list, which will now own it also.
185  v->sp_acquire();
186  assert(v->sp_count()>=1);
187  assert(dynamic_cast<typename node_details_t::base_t::atomic_ptr_t::value_type>(i.get()));
188  typename node_details_t::base_t::atomic_ptr_t expected;
189  do {
190  // Linking v->next to the tail does not need to be atomic, because no-one else should be mucking around with v->next, otherwise the slist is broken.
191  assert(static_cast<bool>(i));
192  expected=i->next;
193  assert(dynamic_cast<typename node_details_t::base_t::atomic_ptr_t::value_type>(v.get().get()));
194  v->next=expected;
195  } while (!i->next.compare_exchange_strong(expected.get(), v.get().get()));
196  assert(i->next->sp_count()>=1);
197  assert(v->sp_count()>=1);
198 }
199 
200 template<class V, class LkT>
201 inline void
202 stack<V, LkT>::insert(value_type v) noexcept(true) {
203  assert(v->sp_count()>=1);
204  // Acquire a reference count for the list, which will now own it also.
205  v->sp_acquire();
206  assert(v->sp_count()>=1);
207  typename node_details_t::base_t::atomic_ptr_t expected;
208  do {
209  // Linking v->next to the tail does not need to be atomic, because no-one else should be mucking around with v->next, otherwise the slist is broken.
210  expected=prefront_.next;
211  assert(dynamic_cast<typename node_details_t::base_t::atomic_ptr_t::value_type>(v.get().get()));
212  v->next=expected;
213  } while (!prefront_.next.compare_exchange_strong(expected.get(), v.get().get()));
214  assert(dynamic_cast<typename node_details_t::base_t::atomic_ptr_t::value_type>(prefront_.next.get()));
215  assert(prefront_.next->sp_count()>=1);
216  assert(v->sp_count()>=1);
217  ++size_ctr;
218 }
219 
220 template<class V, class LkT>
221 inline void
222 stack<V, LkT>::push(value_type const &v) noexcept(true) {
223  this->insert(v);
224 }
225 
226 template<class V, class LkT>
227 inline void
228 stack<V, LkT>::push(value_type &&v) noexcept(true) {
229  this->insert(v);
230 }
231 
232 template<class V, class LkT>
233 inline void
234 stack<V, LkT>::push_front(value_type const &v) noexcept(true) {
235  this->push(std::move(v));
236 }
237 
238 template<class V, class LkT>
239 inline void
240 stack<V, LkT>::push_front(value_type &&v) noexcept(true) {
241  this->push(std::forward<value_type>(v));
242 }
243 
244 template<class V, class LkT>
245 inline typename stack<V, LkT>::value_type
246 stack<V, LkT>::pop_top_nochk() noexcept(true) {
247  assert(!empty());
248  --this->size_ctr;
249  // Unlink the node to be deleted first, to ensure only one thread at a time reduces the reference count.
250  value_type ret(unlink_node(prefront_.next).get());
251  // Remove the list ownership and function ownership.
252  assert(ret->sp_count()>=1);
253  ret->sp_release();
254  return ret;
255 }
256 
257 template<class V, class LkT>
258 inline
259 slist<V, LkT>::slist() noexcept(true)
260 : base_t(), penultimate_() {
261 }
262 
263 template<class V, class LkT>
264 inline
265 slist<V, LkT>::slist(slist &&s) noexcept(true)
266 : base_t(), penultimate_() {
267  s.prefront_.sp_acquire();
268  this->prefront_.swap(s.prefront_);
269  this->prefront_.sp_release();
270  penultimate_.swap(s.penultimate_);
271  this->size_ctr.swap(s.size_ctr);
272 }
273 
274 template<class V, class LkT>
275 inline typename slist<V, LkT>::value_type
276 slist<V, LkT>::front() noexcept(true) {
277  return this->top();
278 }
279 
280 template<class V, class LkT>
281 inline typename slist<V, LkT>::value_type
282 slist<V, LkT>::front() const noexcept(true) {
283  return this->top();
284 }
285 
286 template<class V, class LkT>
287 inline typename slist<V, LkT>::iterator
288 slist<V, LkT>::find(const_reference v) noexcept(true) {
289  if (!this->empty()) {
290  return std::find(this->begin(), this->end(), v);
291  } else {
292  return this->end();
293  }
294 }
295 
296 template<class V, class LkT>
297 inline typename slist<V, LkT>::const_iterator
298 slist<V, LkT>::find(const_reference v) const noexcept(true) {
299  if (!this->empty()) {
300  return std::find(this->begin(), this->end(), v);
301  } else {
302  return this->end();
303  }
304 }
305 
306 template<class V, class LkT>
307 inline void
308 slist<V, LkT>::pop_front() noexcept(true) {
309  base_t::pop_front();
310  if (!this->prefront_.next.get()) {
311  penultimate_=typename node_details_t::base_t::atomic_ptr_t();
312  }
313 }
314 
315 template<class V, class LkT>
316 inline typename slist<V, LkT>::size_type
317 slist<V, LkT>::erase(const_reference v) noexcept(true) {
318  size_type num_erased=0;
319  iterator iter_to_erase(this->begin());
320  while ((iter_to_erase=std::find(iter_to_erase, this->end(), v))!=this->end()) {
321  this->erase(iter_to_erase++);
322  ++num_erased;
323  }
324  return num_erased;
325 }
326 
327 template<class V, class LkT>
328 inline
329 slist<V, LkT>::~slist() noexcept(true) {
330 }
331 
332 template<class V, class LkT>
333 inline typename slist<V, LkT>::value_type
334 slist<V, LkT>::back() noexcept(true) {
335  assert(penultimate_->next->sp_count()>0);
336  return value_type(static_cast<typename value_type::value_type *>(penultimate_->next));
337 }
338 
339 template<class V, class LkT>
340 inline typename slist<V, LkT>::value_type
341 slist<V, LkT>::back() const noexcept(true) {
342  assert(penultimate_->next->sp_count()>0);
343  return value_type(static_cast<typename value_type::value_type const *>(penultimate_->next));
344 }
345 
346 template<class V, class LkT>
347 inline void
348 slist<V, LkT>::push_front(value_type const &v) noexcept(true) {
349  typename node_details_t::base_t::atomic_ptr_t const was_empty=this->prefront_.next;
350  this->insert(typename node_details_t::base_t::atomic_ptr_t(&this->prefront_), v);
351  if (!was_empty.get()) {
352  penultimate_=&this->prefront_;
353  } else if (!v->next->next) {
354  penultimate_=v.get().get();
355  }
356  ++this->size_ctr;
357 }
358 
359 template<class V, class LkT>
360 inline void
361 slist<V, LkT>::push_front(value_type &&v) noexcept(true) {
362  typename node_details_t::base_t::atomic_ptr_t const was_empty=this->prefront_.next;
363  this->insert(typename node_details_t::base_t::atomic_ptr_t(&this->prefront_), v);
364  if (!was_empty.get()) {
365  penultimate_=&this->prefront_;
366  } else if (!v->next->next) {
367  penultimate_=v.get();
368  }
369  ++this->size_ctr;
370 }
371 
372 template<class V, class LkT>
373 inline void
374 slist<V, LkT>::move_penultimate(typename node_details_t::base_t::atomic_ptr_t &node) noexcept(true) {
375  using ptr_t=typename node_details_t::base_t::atomic_ptr_t;
376  assert(dynamic_cast<typename node_details_t::base_t::atomic_ptr_t::value_type>(node->next.get()));
377  node.assign([&node](ptr_t const &) {return node->next;});
378  assert(dynamic_cast<typename node_details_t::base_t::atomic_ptr_t::value_type>(node->next.get()));
379 }
380 
381 template<class V, class LkT>
382 inline void
383 slist<V, LkT>::push_back(value_type const &v) noexcept(true) {
384  if (!this->empty()) {
385  // Note how penultimate_ is the penultimate member of the slist, not the last, which allows us to do this in constant time.
386  this->insert(penultimate_->next, v);
387  move_penultimate(penultimate_);
388  ++this->size_ctr;
389  } else {
390  push_front(v);
391  }
392 }
393 
394 template<class V, class LkT>
395 inline void
396 slist<V, LkT>::push_back(value_type &&v) noexcept(true) {
397  if (!this->empty()) {
398  // Note how penultimate_ is the penultimate member of the slist, not the last, which allows us to do this in constant time.
399  this->insert(penultimate_->next, v);
400  move_penultimate(penultimate_);
401  ++this->size_ctr;
402  } else {
403  push_front(std::forward<value_type>(v));
404  }
405 }
406 
407 template<class V, class LkT>
408 inline bool
409 slist<V, LkT>::penultimate_reachable_from_prefront() const noexcept(true) {
410  bool ret=this->empty();
411  for (const_iterator i(this->begin()); i!=this->end(); ++i) {
412  if (i.real_node.get()==penultimate_->next) {
413  ret=true;
414  break;
415  }
416  }
417  return ret;
418 }
419 
420 } } }