lockfree: fifo - node->next uses boost.atomic
[boost_lockfree.git] / boost / lockfree / fifo.hpp
blob035cf47049a9c194b1ad068762cec26bfcd07a15
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"
4 //
5 // implementation for c++
6 //
7 // Copyright (C) 2008 Tim Blechmann
8 //
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 <boost/atomic.hpp>
19 #include <boost/lockfree/detail/tagged_ptr.hpp>
20 #include <boost/lockfree/detail/freelist.hpp>
22 #include <boost/static_assert.hpp>
23 #include <boost/type_traits/is_pod.hpp>
25 #include <memory> /* std::auto_ptr */
26 #include <boost/scoped_ptr.hpp>
27 #include <boost/shared_ptr.hpp>
28 #include <boost/noncopyable.hpp>
30 namespace boost
32 namespace lockfree
35 namespace detail
38 template <typename T, typename freelist_t, typename Alloc>
39 class fifo:
40 boost::noncopyable
42 BOOST_STATIC_ASSERT(boost::is_pod<T>::value);
44 struct BOOST_LOCKFREE_CACHELINE_ALIGNMENT node
46 typedef tagged_ptr<node> tagged_ptr_t;
48 node(T const & v):
49 data(v)
51 /* increment tag to avoid ABA problem */
52 tagged_ptr_t old_next = next.load(memory_order_acquire);
53 tagged_ptr_t new_next (NULL, old_next.get_tag()+1);
54 next.store(new_next, memory_order_release);
57 node (void):
58 next(tagged_ptr_t(NULL, 0))
61 atomic<tagged_ptr_t> next;
62 T data;
65 typedef tagged_ptr<node> atomic_node_ptr;
67 typedef typename Alloc::template rebind<node>::other node_allocator;
68 /* typedef typename select_freelist<node, node_allocator, freelist_t>::type pool_t; */
70 typedef typename boost::mpl::if_<boost::is_same<freelist_t, caching_freelist_t>,
71 caching_freelist<node, node_allocator>,
72 static_freelist<node, node_allocator>
73 >::type pool_t;
75 public:
76 static const bool is_lockfree = node::tagged_ptr_t::is_lockfree;
78 fifo(void):
79 pool(128)
81 node * n = alloc_node();
82 head_.set_ptr(n);
83 tail_.set_ptr(n);
86 explicit fifo(std::size_t initial_nodes):
87 pool(initial_nodes)
89 node * n = alloc_node();
90 head_.set_ptr(n);
91 tail_.set_ptr(n);
94 ~fifo(void)
96 if (!empty())
98 T dummy;
99 for(;;)
101 if (!dequeue(&dummy))
102 break;
105 dealloc_node(head_.get_ptr());
108 bool empty(void)
110 return head_.get_ptr() == tail_.get_ptr();
113 bool enqueue(T const & t)
115 node * n = alloc_node(t);
117 if (n == NULL)
118 return false;
120 for (;;)
122 atomic_node_ptr tail (tail_);
123 read_memory_barrier();
124 atomic_node_ptr next (tail->next);
126 if (likely(tail == tail_))
128 if (next.get_ptr() == 0)
130 if ( tail->next.compare_exchange_strong(next, tagged_ptr<node>(n, next.get_tag() + 1)) )
132 tail_.cas(tail, n);
133 return true;
136 else
137 tail_.cas(tail, next.get_ptr());
142 bool dequeue (T * ret)
144 for (;;)
146 atomic_node_ptr head (head_);
147 read_memory_barrier();
149 atomic_node_ptr tail(tail_);
150 node * next = head->next.load(memory_order_acquire).get_ptr();
151 read_memory_barrier();
153 if (likely(head == head_))
155 if (head.get_ptr() == tail.get_ptr())
157 if (next == 0)
158 return false;
160 tail_.cas(tail, next);
162 else
164 *ret = next->data;
165 if (head_.cas(head, next))
167 dealloc_node(head.get_ptr());
169 return true;
176 private:
177 node * alloc_node(void)
179 node * chunk = pool.allocate();
180 new(chunk) node();
181 return chunk;
184 node * alloc_node(T const & t)
186 node * chunk = pool.allocate();
187 new(chunk) node(t);
188 return chunk;
191 void dealloc_node(node * n)
193 n->~node();
194 pool.deallocate(n);
197 volatile atomic_node_ptr head_;
198 static const int padding_size = 64 - sizeof(atomic_node_ptr); /* cache lines on current cpus seem to
199 * be 64 byte */
200 char padding1[padding_size];
201 volatile atomic_node_ptr tail_;
202 char padding2[padding_size];
204 pool_t pool;
207 } /* namespace detail */
209 /** lockfree fifo
211 * - wrapper for detail::fifo
212 * */
213 template <typename T,
214 typename freelist_t = caching_freelist_t,
215 typename Alloc = std::allocator<T>
217 class fifo:
218 public detail::fifo<T, freelist_t, Alloc>
220 public:
221 fifo(void)
224 explicit fifo(std::size_t initial_nodes):
225 detail::fifo<T, freelist_t, Alloc>(initial_nodes)
230 /** lockfree fifo, template specialization for pointer-types
232 * - wrapper for detail::fifo
233 * - overload dequeue to support smart pointers
234 * */
235 template <typename T, typename freelist_t, typename Alloc>
236 class fifo<T*, freelist_t, Alloc>:
237 public detail::fifo<T*, freelist_t, Alloc>
239 typedef detail::fifo<T*, freelist_t, Alloc> fifo_t;
241 template <typename smart_ptr>
242 bool dequeue_smart_ptr(smart_ptr & ptr)
244 T * result = 0;
245 bool success = fifo_t::dequeue(&result);
247 if (success)
248 ptr.reset(result);
249 return success;
252 public:
253 fifo(void)
256 explicit fifo(std::size_t initial_nodes):
257 fifo_t(initial_nodes)
260 bool enqueue(T * t)
262 return fifo_t::enqueue(t);
265 bool dequeue (T ** ret)
267 return fifo_t::dequeue(ret);
270 bool dequeue (std::auto_ptr<T> & ret)
272 return dequeue_smart_ptr(ret);
275 bool dequeue (boost::scoped_ptr<T> & ret)
277 BOOST_STATIC_ASSERT(sizeof(boost::scoped_ptr<T>) == sizeof(T*));
278 return dequeue(reinterpret_cast<T**>((void*)&ret));
281 bool dequeue (boost::shared_ptr<T> & ret)
283 return dequeue_smart_ptr(ret);
287 } /* namespace lockfree */
288 } /* namespace boost */
291 #endif /* BOOST_LOCKFREE_FIFO_HPP_INCLUDED */