Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / external_libraries / boost-lockfree / boost / lockfree / detail / freelist.hpp
blob6976dae881034cf63f2f39d9479a5b098db58b68
1 // lock-free freelist
2 //
3 // Copyright (C) 2008, 2009, 2011 Tim Blechmann
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See
6 // accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
9 #ifndef BOOST_LOCKFREE_FREELIST_HPP_INCLUDED
10 #define BOOST_LOCKFREE_FREELIST_HPP_INCLUDED
12 #include <memory>
14 #include <boost/array.hpp>
15 #include <boost/config.hpp>
16 #include <boost/noncopyable.hpp>
17 #include <boost/static_assert.hpp>
19 #include <boost/lockfree/detail/atomic.hpp>
20 #include <boost/lockfree/detail/parameter.hpp>
21 #include <boost/lockfree/detail/tagged_ptr.hpp>
23 namespace boost {
24 namespace lockfree {
25 namespace detail {
27 template <typename T,
28 typename Alloc = std::allocator<T>
30 class freelist_stack:
31 Alloc
33 struct freelist_node
35 tagged_ptr<freelist_node> next;
38 typedef tagged_ptr<freelist_node> tagged_node_ptr;
40 public:
41 typedef tagged_ptr<T> tagged_node_handle;
43 template <typename Allocator>
44 freelist_stack (Allocator const & alloc, std::size_t n = 0):
45 Alloc(alloc),
46 pool_(tagged_node_ptr(NULL))
48 for (std::size_t i = 0; i != n; ++i) {
49 T * node = Alloc::allocate(1);
50 #ifdef BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR
51 destruct<false>(node);
52 #else
53 deallocate<false>(node);
54 #endif
58 template <bool ThreadSafe>
59 void reserve (std::size_t count)
61 for (std::size_t i = 0; i != count; ++i) {
62 T * node = Alloc::allocate(1);
63 deallocate<ThreadSafe>(node);
67 template <bool ThreadSafe, bool Bounded>
68 T * construct (void)
70 T * node = allocate<ThreadSafe, Bounded>();
71 if (node)
72 new(node) T();
73 return node;
76 template <bool ThreadSafe, bool Bounded, typename ArgumentType>
77 T * construct (ArgumentType const & arg)
79 T * node = allocate<ThreadSafe, Bounded>();
80 if (node)
81 new(node) T(arg);
82 return node;
85 template <bool ThreadSafe, bool Bounded, typename ArgumentType1, typename ArgumentType2>
86 T * construct (ArgumentType1 const & arg1, ArgumentType2 const & arg2)
88 T * node = allocate<ThreadSafe, Bounded>();
89 if (node)
90 new(node) T(arg1, arg2);
91 return node;
94 template <bool ThreadSafe>
95 void destruct (tagged_node_handle tagged_ptr)
97 T * n = tagged_ptr.get_ptr();
98 n->~T();
99 deallocate<ThreadSafe>(n);
102 template <bool ThreadSafe>
103 void destruct (T * n)
105 n->~T();
106 deallocate<ThreadSafe>(n);
109 ~freelist_stack(void)
111 tagged_node_ptr current (pool_);
113 while (current) {
114 freelist_node * current_ptr = current.get_ptr();
115 if (current_ptr)
116 current = current_ptr->next;
117 Alloc::deallocate((T*)current_ptr, 1);
121 bool is_lock_free(void) const
123 return pool_.is_lock_free();
126 T * get_handle(T * pointer) const
128 return pointer;
131 T * get_handle(tagged_node_handle const & handle) const
133 return get_pointer(handle);
136 T * get_pointer(tagged_node_handle const & tptr) const
138 return tptr.get_ptr();
141 T * get_pointer(T * pointer) const
143 return pointer;
146 T * null_handle(void) const
148 return NULL;
151 private:
152 template <bool ThreadSafe, bool Bounded>
153 T * allocate (void)
155 if (ThreadSafe)
156 return allocate_impl<Bounded>();
157 else
158 return allocate_impl_unsafe<Bounded>();
161 template <bool Bounded>
162 T * allocate_impl (void)
164 tagged_node_ptr old_pool = pool_.load(memory_order_consume);
166 for(;;) {
167 if (!old_pool.get_ptr()) {
168 if (!Bounded)
169 return Alloc::allocate(1);
170 else
171 return 0;
174 freelist_node * new_pool_ptr = old_pool->next.get_ptr();
175 tagged_node_ptr new_pool (new_pool_ptr, old_pool.get_tag() + 1);
177 if (pool_.compare_exchange_weak(old_pool, new_pool)) {
178 void * ptr = old_pool.get_ptr();
179 return reinterpret_cast<T*>(ptr);
184 template <bool Bounded>
185 T * allocate_impl_unsafe (void)
187 tagged_node_ptr old_pool = pool_.load(memory_order_relaxed);
189 if (!old_pool.get_ptr()) {
190 if (!Bounded)
191 return Alloc::allocate(1);
192 else
193 return 0;
196 freelist_node * new_pool_ptr = old_pool->next.get_ptr();
197 tagged_node_ptr new_pool (new_pool_ptr, old_pool.get_tag() + 1);
199 pool_.store(new_pool, memory_order_relaxed);
200 void * ptr = old_pool.get_ptr();
201 return reinterpret_cast<T*>(ptr);
204 template <bool ThreadSafe>
205 void deallocate (T * n)
207 if (ThreadSafe)
208 deallocate_impl(n);
209 else
210 deallocate_impl_unsafe(n);
213 void deallocate_impl (T * n)
215 void * node = n;
216 tagged_node_ptr old_pool = pool_.load(memory_order_consume);
217 freelist_node * new_pool_ptr = reinterpret_cast<freelist_node*>(node);
219 for(;;) {
220 tagged_node_ptr new_pool (new_pool_ptr, old_pool.get_tag());
221 new_pool->next.set_ptr(old_pool.get_ptr());
223 if (pool_.compare_exchange_weak(old_pool, new_pool))
224 return;
228 void deallocate_impl_unsafe (T * n)
230 void * node = n;
231 tagged_node_ptr old_pool = pool_.load(memory_order_relaxed);
232 freelist_node * new_pool_ptr = reinterpret_cast<freelist_node*>(node);
234 tagged_node_ptr new_pool (new_pool_ptr, old_pool.get_tag());
235 new_pool->next.set_ptr(old_pool.get_ptr());
237 pool_.store(new_pool, memory_order_relaxed);
240 atomic<tagged_node_ptr> pool_;
243 class tagged_index
245 public:
246 typedef boost::uint16_t tag_t;
247 typedef boost::uint16_t index_t;
249 /** uninitialized constructor */
250 tagged_index(void) BOOST_NOEXCEPT //: index(0), tag(0)
253 /** copy constructor */
254 #ifdef BOOST_NO_DEFAULTED_FUNCTIONS
255 tagged_index(tagged_index const & rhs):
256 index(rhs.index), tag(rhs.tag)
258 #else
259 tagged_index(tagged_index const & rhs) = default;
260 #endif
262 explicit tagged_index(index_t i, tag_t t = 0):
263 index(i), tag(t)
266 /** index access */
267 /* @{ */
268 index_t get_index() const
270 return index;
273 void set_index(index_t i)
275 index = i;
277 /* @} */
279 /** tag access */
280 /* @{ */
281 tag_t get_tag() const
283 return tag;
286 void set_tag(tag_t t)
288 tag = t;
290 /* @} */
292 bool operator==(tagged_index const & rhs) const
294 return (index == rhs.index) && (tag == rhs.tag);
297 protected:
298 index_t index;
299 tag_t tag;
302 template <typename T,
303 std::size_t size>
304 struct compiletime_sized_freelist_storage
306 // array-based freelists only support a 16bit address space.
307 BOOST_STATIC_ASSERT(size < 65536);
309 boost::array<char, size * sizeof(T)> data;
311 // unused ... only for API purposes
312 template <typename Allocator>
313 compiletime_sized_freelist_storage(Allocator const & alloc, std::size_t count)
316 T * nodes(void) const
318 return reinterpret_cast<T*>(const_cast<char*>(data.data()));
321 std::size_t node_count(void) const
323 return size;
327 template <typename T,
328 typename Alloc = std::allocator<T> >
329 struct runtime_sized_freelist_storage:
330 Alloc
332 T * nodes_;
333 std::size_t node_count_;
335 template <typename Allocator>
336 runtime_sized_freelist_storage(Allocator const & alloc, std::size_t count):
337 Alloc(alloc), node_count_(count)
339 if (count > 65535)
340 boost::throw_exception(std::runtime_error("boost.lockfree: freelist size is limited to a maximum of 65535 objects"));
341 nodes_ = Alloc::allocate(count);
344 ~runtime_sized_freelist_storage(void)
346 Alloc::deallocate(nodes_, node_count_);
349 T * nodes(void) const
351 return nodes_;
354 std::size_t node_count(void) const
356 return node_count_;
361 template <typename T,
362 typename NodeStorage = runtime_sized_freelist_storage<T>
364 class fixed_size_freelist:
365 NodeStorage
367 struct freelist_node
369 tagged_index next;
372 typedef tagged_index::index_t index_t;
374 void initialize(void)
376 T * nodes = NodeStorage::nodes();
377 for (std::size_t i = 0; i != NodeStorage::node_count(); ++i) {
378 tagged_index * next_index = reinterpret_cast<tagged_index*>(nodes + i);
379 next_index->set_index(null_handle());
381 #ifdef BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR
382 destruct<false>(nodes + i);
383 #else
384 deallocate<false>(i);
385 #endif
389 public:
390 typedef tagged_index tagged_node_handle;
392 template <typename Allocator>
393 fixed_size_freelist (Allocator const & alloc, std::size_t count):
394 NodeStorage(alloc, count), pool_(tagged_index(count, 0))
396 initialize();
399 fixed_size_freelist (void):
400 pool_(tagged_index(NodeStorage::node_count(), 0))
402 initialize();
405 template <bool ThreadSafe, bool Bounded>
406 T * construct (void)
408 index_t node_index = allocate<ThreadSafe>();
409 if (node_index == null_handle())
410 return NULL;
412 T * node = NodeStorage::nodes() + node_index;
413 new(node) T();
414 return node;
417 template <bool ThreadSafe, bool Bounded, typename ArgumentType>
418 T * construct (ArgumentType const & arg)
420 index_t node_index = allocate<ThreadSafe>();
421 if (node_index == null_handle())
422 return NULL;
424 T * node = NodeStorage::nodes() + node_index;
425 new(node) T(arg);
426 return node;
429 template <bool ThreadSafe, bool Bounded, typename ArgumentType1, typename ArgumentType2>
430 T * construct (ArgumentType1 const & arg1, ArgumentType2 const & arg2)
432 index_t node_index = allocate<ThreadSafe>();
433 if (node_index == null_handle())
434 return NULL;
436 T * node = NodeStorage::nodes() + node_index;
437 new(node) T(arg1, arg2);
438 return node;
441 template <bool ThreadSafe>
442 void destruct (tagged_node_handle tagged_index)
444 index_t index = tagged_index.get_index();
445 T * n = NodeStorage::nodes() + index;
446 n->~T();
447 deallocate<ThreadSafe>(index);
450 template <bool ThreadSafe>
451 void destruct (T * n)
453 n->~T();
454 deallocate<ThreadSafe>(n - NodeStorage::nodes());
457 bool is_lock_free(void) const
459 return pool_.is_lock_free();
462 index_t null_handle(void) const
464 return NodeStorage::node_count();
467 index_t get_handle(T * pointer) const
469 if (pointer == NULL)
470 return null_handle();
471 else
472 return pointer - NodeStorage::nodes();
475 index_t get_handle(tagged_node_handle const & handle) const
477 return handle.get_index();
480 T * get_pointer(tagged_node_handle const & tptr) const
482 return get_pointer(tptr.get_index());
485 T * get_pointer(index_t index) const
487 if (index == null_handle())
488 return 0;
489 else
490 return NodeStorage::nodes() + index;
493 T * get_pointer(T * ptr) const
495 return ptr;
498 private:
499 template <bool ThreadSafe>
500 index_t allocate (void)
502 if (ThreadSafe)
503 return allocate_impl();
504 else
505 return allocate_impl_unsafe();
508 index_t allocate_impl (void)
510 tagged_index old_pool = pool_.load(memory_order_consume);
512 for(;;) {
513 index_t index = old_pool.get_index();
514 if (index == null_handle())
515 return index;
517 T * old_node = NodeStorage::nodes() + index;
518 tagged_index * next_index = reinterpret_cast<tagged_index*>(old_node);
520 tagged_index new_pool(next_index->get_index(), old_pool.get_tag() + 1);
522 if (pool_.compare_exchange_weak(old_pool, new_pool))
523 return old_pool.get_index();
527 index_t allocate_impl_unsafe (void)
529 tagged_index old_pool = pool_.load(memory_order_consume);
531 index_t index = old_pool.get_index();
532 if (index == null_handle())
533 return index;
535 T * old_node = NodeStorage::nodes() + index;
536 tagged_index * next_index = reinterpret_cast<tagged_index*>(old_node);
538 tagged_index new_pool(next_index->get_index(), old_pool.get_tag() + 1);
540 pool_.store(new_pool, memory_order_relaxed);
541 return old_pool.get_index();
544 template <bool ThreadSafe>
545 void deallocate (index_t index)
547 if (ThreadSafe)
548 deallocate_impl(index);
549 else
550 deallocate_impl_unsafe(index);
553 void deallocate_impl (index_t index)
555 freelist_node * new_pool_node = reinterpret_cast<freelist_node*>(NodeStorage::nodes() + index);
556 tagged_index old_pool = pool_.load(memory_order_consume);
558 for(;;) {
559 tagged_index new_pool (index, old_pool.get_tag());
560 new_pool_node->next.set_index(old_pool.get_index());
562 if (pool_.compare_exchange_weak(old_pool, new_pool))
563 return;
567 void deallocate_impl_unsafe (index_t index)
569 freelist_node * new_pool_node = reinterpret_cast<freelist_node*>(NodeStorage::nodes() + index);
570 tagged_index old_pool = pool_.load(memory_order_consume);
572 tagged_index new_pool (index, old_pool.get_tag());
573 new_pool_node->next.set_index(old_pool.get_index());
575 pool_.store(new_pool);
578 atomic<tagged_index> pool_;
581 template <typename T,
582 typename Alloc,
583 bool IsCompileTimeSized,
584 bool IsFixedSize,
585 std::size_t Capacity
587 struct select_freelist
589 typedef typename mpl::if_c<IsCompileTimeSized,
590 compiletime_sized_freelist_storage<T, Capacity>,
591 runtime_sized_freelist_storage<T, Alloc>
592 >::type fixed_sized_storage_type;
594 typedef typename mpl::if_c<IsCompileTimeSized || IsFixedSize,
595 fixed_size_freelist<T, fixed_sized_storage_type>,
596 freelist_stack<T, Alloc>
597 >::type type;
600 template <typename T, bool IsNodeBased>
601 struct select_tagged_handle
603 typedef typename mpl::if_c<IsNodeBased,
604 tagged_ptr<T>,
605 tagged_index
606 >::type tagged_handle_type;
608 typedef typename mpl::if_c<IsNodeBased,
610 typename tagged_index::index_t
611 >::type handle_type;
615 } /* namespace detail */
616 } /* namespace lockfree */
617 } /* namespace boost */
619 #endif /* BOOST_LOCKFREE_FREELIST_HPP_INCLUDED */