3 // Copyright (C) 2008, 2009, 2011 Tim Blechmann
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
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>
28 typename Alloc
= std::allocator
<T
>
35 tagged_ptr
<freelist_node
> next
;
38 typedef tagged_ptr
<freelist_node
> tagged_node_ptr
;
41 typedef tagged_ptr
<T
> tagged_node_handle
;
43 template <typename Allocator
>
44 freelist_stack (Allocator
const & alloc
, std::size_t n
= 0):
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
);
53 deallocate
<false>(node
);
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
>
70 T
* node
= allocate
<ThreadSafe
, Bounded
>();
76 template <bool ThreadSafe
, bool Bounded
, typename ArgumentType
>
77 T
* construct (ArgumentType
const & arg
)
79 T
* node
= allocate
<ThreadSafe
, Bounded
>();
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
>();
90 new(node
) T(arg1
, arg2
);
94 template <bool ThreadSafe
>
95 void destruct (tagged_node_handle tagged_ptr
)
97 T
* n
= tagged_ptr
.get_ptr();
99 deallocate
<ThreadSafe
>(n
);
102 template <bool ThreadSafe
>
103 void destruct (T
* n
)
106 deallocate
<ThreadSafe
>(n
);
109 ~freelist_stack(void)
111 tagged_node_ptr
current (pool_
);
114 freelist_node
* current_ptr
= current
.get_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
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
146 T
* null_handle(void) const
152 template <bool ThreadSafe
, bool Bounded
>
156 return allocate_impl
<Bounded
>();
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
);
167 if (!old_pool
.get_ptr()) {
169 return Alloc::allocate(1);
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()) {
191 return Alloc::allocate(1);
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
)
210 deallocate_impl_unsafe(n
);
213 void deallocate_impl (T
* n
)
216 tagged_node_ptr old_pool
= pool_
.load(memory_order_consume
);
217 freelist_node
* new_pool_ptr
= reinterpret_cast<freelist_node
*>(node
);
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
))
228 void deallocate_impl_unsafe (T
* 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_
;
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
)
259 tagged_index(tagged_index
const & rhs
) = default;
262 explicit tagged_index(index_t i
, tag_t t
= 0):
268 index_t
get_index() const
273 void set_index(index_t i
)
281 tag_t
get_tag() const
286 void set_tag(tag_t t
)
292 bool operator==(tagged_index
const & rhs
) const
294 return (index
== rhs
.index
) && (tag
== rhs
.tag
);
302 template <typename T
,
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
327 template <typename T
,
328 typename Alloc
= std::allocator
<T
> >
329 struct runtime_sized_freelist_storage
:
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
)
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
354 std::size_t node_count(void) const
361 template <typename T
,
362 typename NodeStorage
= runtime_sized_freelist_storage
<T
>
364 class fixed_size_freelist
:
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
);
384 deallocate
<false>(i
);
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))
399 fixed_size_freelist (void):
400 pool_(tagged_index(NodeStorage::node_count(), 0))
405 template <bool ThreadSafe
, bool Bounded
>
408 index_t node_index
= allocate
<ThreadSafe
>();
409 if (node_index
== null_handle())
412 T
* node
= NodeStorage::nodes() + node_index
;
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())
424 T
* node
= NodeStorage::nodes() + node_index
;
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())
436 T
* node
= NodeStorage::nodes() + node_index
;
437 new(node
) T(arg1
, arg2
);
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
;
447 deallocate
<ThreadSafe
>(index
);
450 template <bool ThreadSafe
>
451 void destruct (T
* n
)
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
470 return null_handle();
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())
490 return NodeStorage::nodes() + index
;
493 T
* get_pointer(T
* ptr
) const
499 template <bool ThreadSafe
>
500 index_t
allocate (void)
503 return allocate_impl();
505 return allocate_impl_unsafe();
508 index_t
allocate_impl (void)
510 tagged_index old_pool
= pool_
.load(memory_order_consume
);
513 index_t index
= old_pool
.get_index();
514 if (index
== null_handle())
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())
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
)
548 deallocate_impl(index
);
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
);
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
))
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
,
583 bool IsCompileTimeSized
,
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
>
600 template <typename T
, bool IsNodeBased
>
601 struct select_tagged_handle
603 typedef typename
mpl::if_c
<IsNodeBased
,
606 >::type tagged_handle_type
;
608 typedef typename
mpl::if_c
<IsNodeBased
,
610 typename
tagged_index::index_t
615 } /* namespace detail */
616 } /* namespace lockfree */
617 } /* namespace boost */
619 #endif /* BOOST_LOCKFREE_FREELIST_HPP_INCLUDED */