1 // lock-free fifo queue from
2 // Michael, M. M. and Scott, M. L.,
3 // "simple, fast and practical non-blocking and blocking concurrent queue algorithms"
5 // implementation for c++
7 // Copyright (C) 2008, 2009, 2010, 2011 Tim Blechmann
9 // Distributed under the Boost Software License, Version 1.0. (See
10 // accompanying file LICENSE_1_0.txt or copy at
11 // http://www.boost.org/LICENSE_1_0.txt)
13 // Disclaimer: Not a Boost library.
15 #ifndef BOOST_LOCKFREE_FIFO_HPP_INCLUDED
16 #define BOOST_LOCKFREE_FIFO_HPP_INCLUDED
18 #include <memory> /* std::auto_ptr */
20 #include <boost/noncopyable.hpp>
21 #include <boost/scoped_ptr.hpp>
22 #include <boost/shared_ptr.hpp>
23 #include <boost/static_assert.hpp>
24 #include <boost/type_traits/has_trivial_assign.hpp>
26 #include <boost/lockfree/detail/atomic.hpp>
27 #include <boost/lockfree/detail/tagged_ptr.hpp>
28 #include <boost/lockfree/detail/freelist.hpp>
34 template <typename T
, typename freelist_t
, typename Alloc
>
39 #ifndef BOOST_DOXYGEN_INVOKED
40 BOOST_STATIC_ASSERT(boost::is_pod
<T
>::value
);
42 struct BOOST_LOCKFREE_CACHELINE_ALIGNMENT node
44 typedef tagged_ptr
<node
> tagged_node_ptr
;
49 /* increment tag to avoid ABA problem */
50 tagged_node_ptr old_next
= next
.load(memory_order_relaxed
);
51 tagged_node_ptr
new_next (NULL
, old_next
.get_tag()+1);
52 next
.store(new_next
, memory_order_release
);
56 next(tagged_node_ptr(NULL
, 0))
59 atomic
<tagged_node_ptr
> next
;
63 typedef tagged_ptr
<node
> tagged_node_ptr
;
65 typedef typename
Alloc::template rebind
<node
>::other node_allocator
;
67 typedef typename
boost::mpl::if_
<boost::is_same
<freelist_t
, caching_freelist_t
>,
68 detail::freelist_stack
<node
, true, node_allocator
>,
69 detail::freelist_stack
<node
, false, node_allocator
>
74 node
* n
= pool
.construct();
75 tagged_node_ptr
dummy_node(n
, 0);
76 head_
.store(dummy_node
, memory_order_relaxed
);
77 tail_
.store(dummy_node
, memory_order_release
);
83 * \return true, if implementation is lock-free.
85 * \warning \b Warning: It only checks, if the fifo head node is lockfree. On most platforms, the whole implementation is
86 * lockfree, if this is true. Using c++0x-style atomics, there is no possibility to provide a completely
87 * accurate implementation, because one would need to test every internal node, which is impossible
88 * if further nodes will be allocated from the operating system.
90 bool is_lock_free (void) const
92 return head_
.is_lock_free() && pool
.is_lock_free();
102 //! Construct fifo, allocate n nodes for the freelist.
103 explicit fifo(std::size_t n
)
109 //! Allocate n nodes for freelist.
110 void reserve(std::size_t n
)
115 /** Destroys fifo, free all nodes from freelist.
121 while(dequeue(dummy
))
124 pool
.destruct(head_
.load(memory_order_relaxed
).get_ptr());
127 /** Check if the ringbuffer is empty
129 * \warning Not thread-safe, use for debugging purposes only
133 return head_
.load().get_ptr() == tail_
.load().get_ptr();
136 /** Enqueues object t to the fifo. Enqueueing may fail, if the freelist is not able to allocate a new fifo node.
138 * \returns true, if the enqueue operation is successful.
140 * \note Thread-safe and non-blocking
141 * \warning \b Warning: May block if node needs to be allocated from the operating system
143 bool enqueue(T
const & t
)
145 node
* n
= pool
.construct(t
);
151 tagged_node_ptr tail
= tail_
.load(memory_order_acquire
);
152 tagged_node_ptr next
= tail
->next
.load(memory_order_acquire
);
153 node
* next_ptr
= next
.get_ptr();
155 tagged_node_ptr tail2
= tail_
.load(memory_order_acquire
);
156 if (likely(tail
== tail2
)) {
158 if ( tail
->next
.compare_exchange_weak(next
, tagged_node_ptr(n
, next
.get_tag() + 1)) ) {
159 tail_
.compare_exchange_strong(tail
, tagged_node_ptr(n
, tail
.get_tag() + 1));
164 tail_
.compare_exchange_strong(tail
, tagged_node_ptr(next_ptr
, tail
.get_tag() + 1));
169 /** Dequeue object from fifo.
171 * if dequeue operation is successful, object is written to memory location denoted by ret.
173 * \returns true, if the dequeue operation is successful, false if fifo was empty.
175 * \note Thread-safe and non-blocking
178 bool dequeue (T
& ret
)
181 tagged_node_ptr head
= head_
.load(memory_order_acquire
);
182 tagged_node_ptr tail
= tail_
.load(memory_order_acquire
);
183 tagged_node_ptr next
= head
->next
.load(memory_order_acquire
);
184 node
* next_ptr
= next
.get_ptr();
186 tagged_node_ptr head2
= head_
.load(memory_order_acquire
);
187 if (likely(head
== head2
)) {
188 if (head
.get_ptr() == tail
.get_ptr()) {
191 tail_
.compare_exchange_strong(tail
, tagged_node_ptr(next_ptr
, tail
.get_tag() + 1));
194 /* this check is not part of the original algorithm as published by michael and scott
196 * however we reuse the tagged_ptr part for the and clear the next part during node
197 * allocation. we can observe a null-pointer here.
200 ret
= next_ptr
->data
;
201 if (head_
.compare_exchange_weak(head
, tagged_node_ptr(next_ptr
, head
.get_tag() + 1))) {
202 pool
.destruct(head
.get_ptr());
211 #ifndef BOOST_DOXYGEN_INVOKED
212 atomic
<tagged_node_ptr
> head_
;
213 static const int padding_size
= BOOST_LOCKFREE_CACHELINE_BYTES
- sizeof(tagged_node_ptr
);
214 char padding1
[padding_size
];
215 atomic
<tagged_node_ptr
> tail_
;
216 char padding2
[padding_size
];
222 } /* namespace detail */
224 /** The fifo class provides a multi-writer/multi-reader fifo, enqueueing and dequeueing is lockfree,
225 * construction/destruction has to be synchronized. It uses a freelist for memory management,
226 * freed nodes are pushed to the freelist and not returned to the os before the fifo is destroyed.
228 * The memory management of the fifo can be controlled via its freelist_t template argument. Two different
229 * freelists can be used. struct caching_freelist_t selects a caching freelist, which can allocate more nodes
230 * from the operating system, and struct static_freelist_t uses a fixed-sized freelist. With a fixed-sized
231 * freelist, the enqueue operation may fail, while with a caching freelist, the enqueue operation may block.
233 * \b Limitation: The class T is required to have a trivial assignment operator.
236 template <typename T
,
237 typename freelist_t
= caching_freelist_t
,
238 typename Alloc
= std::allocator
<T
>
241 public detail::fifo
<T
, freelist_t
, Alloc
>
243 BOOST_STATIC_ASSERT(boost::has_trivial_assign
<T
>::value
);
250 //! Construct fifo, allocate n nodes for the freelist.
251 explicit fifo(std::size_t n
):
252 detail::fifo
<T
, freelist_t
, Alloc
>(n
)
257 /** Template specialization of the fifo class for pointer arguments, that supports dequeue operations to
258 * stl/boost-style smart pointers
261 template <typename T
,
265 class fifo
<T
*, freelist_t
, Alloc
>:
266 public detail::fifo
<T
*, freelist_t
, Alloc
>
268 #ifndef BOOST_DOXYGEN_INVOKED
269 typedef detail::fifo
<T
*, freelist_t
, Alloc
> fifo_t
;
271 template <typename smart_ptr
>
272 bool dequeue_smart_ptr(smart_ptr
& ptr
)
275 bool success
= fifo_t::dequeue(result
);
288 //! Construct fifo, allocate n nodes for the freelist.
289 explicit fifo(std::size_t n
):
293 //! \copydoc detail::fifo::dequeue
294 bool dequeue (T
* & ret
)
296 return fifo_t::dequeue(ret
);
299 /** Dequeue object from fifo to std::auto_ptr
301 * if dequeue operation is successful, object is written to memory location denoted by ret.
303 * \returns true, if the dequeue operation is successful, false if fifo was empty.
305 * \note Thread-safe and non-blocking
308 bool dequeue (std::auto_ptr
<T
> & ret
)
310 return dequeue_smart_ptr(ret
);
313 /** Dequeue object from fifo to boost::scoped_ptr
315 * if dequeue operation is successful, object is written to memory location denoted by ret.
317 * \returns true, if the dequeue operation is successful, false if fifo was empty.
319 * \note Thread-safe and non-blocking
322 bool dequeue (boost::scoped_ptr
<T
> & ret
)
324 BOOST_STATIC_ASSERT(sizeof(boost::scoped_ptr
<T
>) == sizeof(T
*));
325 return dequeue(reinterpret_cast<T
*&>(ret
));
328 /** Dequeue object from fifo to boost::shared_ptr
330 * if dequeue operation is successful, object is written to memory location denoted by ret.
332 * \returns true, if the dequeue operation is successful, false if fifo was empty.
334 * \note Thread-safe and non-blocking
337 bool dequeue (boost::shared_ptr
<T
> & ret
)
339 return dequeue_smart_ptr(ret
);
343 } /* namespace lockfree */
344 } /* namespace boost */
346 #endif /* BOOST_LOCKFREE_FIFO_HPP_INCLUDED */