2 / Copyright (c) 2009 Helge Bahmann
4 / Distributed under the Boost Software License, Version 1.0. (See accompanying
5 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 [section:example_reference_counters Reference counting]
10 The purpose of a ['reference counter] is to count the number
11 of pointers to an object. The object can be destroyed as
12 soon as the reference counter reaches zero.
14 [section Implementation]
18 #include <boost/intrusive_ptr.hpp>
19 #include <boost/atomic.hpp>
23 typedef boost::intrusive_ptr<X> pointer;
27 mutable boost::atomic<int> refcount_;
28 friend void intrusive_ptr_add_ref(const X * x)
30 x->refcount_.fetch_add(1, boost::memory_order_relaxed);
32 friend void intrusive_ptr_release(const X * x)
34 if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) {
35 boost::atomic_thread_fence(boost::memory_order_acquire);
53 Increasing the reference counter can always be done with
54 [^memory_order_relaxed]: New references to an object can only
55 be formed from an existing reference, and passing an existing
56 reference from one thread to another must already provide any
57 required synchronization.
59 It is important to enforce any possible access to the object in
60 one thread (through an existing reference) to ['happen before]
61 deleting the object in a different thread. This is achieved
62 by a "release" operation after dropping a reference (any
63 access to the object through this reference must obviously
64 happened before), and an "acquire" operation before
67 It would be possible to use [^memory_order_acq_rel] for the
68 [^fetch_sub] operation, but this results in unneeded "acquire"
69 operations when the reference counter does not yet reach zero
70 and may impose a performance penalty.
76 [section:example_spinlock Spinlock]
78 The purpose of a ['spin lock] is to prevent multiple threads
79 from concurrently accessing a shared data structure. In contrast
80 to a mutex, threads will busy-wait and waste CPU cycles instead
81 of yielding the CPU to another thread. ['Do not use spinlocks
82 unless you are certain that you understand the consequences.]
84 [section Implementation]
88 #include <boost/atomic.hpp>
92 typedef enum {Locked, Unlocked} LockState;
93 boost::atomic<LockState> state_;
96 spinlock() : state_(Unlocked) {}
100 while (state_.exchange(Locked, boost::memory_order_acquire) == Locked) {
106 state_.store(Unlocked, boost::memory_order_release);
119 // access data structure here
126 The purpose of the spinlock is to make sure that one access
127 to the shared data structure always strictly "happens before"
128 another. The usage of acquire/release in lock/unlock is required
129 and sufficient to guarantee this ordering.
131 It would be correct to write the "lock" operation in the following
138 while (state_.exchange(Locked, boost::memory_order_relaxed) == Locked) {
141 atomic_thread_fence(boost::memory_order_acquire);
144 This "optimization" is however a) useless and b) may in fact hurt:
145 a) Since the thread will be busily spinning on a blocked spinlock,
146 it does not matter if it will waste the CPU cycles with just
147 "exchange" operations or with both useless "exchange" and "acquire"
148 operations. b) A tight "exchange" loop without any
149 memory-synchronizing instruction introduced through an "acquire"
150 operation will on some systems monopolize the memory subsystem
151 and degrade the performance of other system components.
157 [section:singleton Singleton with double-checked locking pattern]
159 The purpose of the ['Singleton with double-checked locking pattern] is to ensure
160 that at most one instance of a particular object is created.
161 If one instance has been created already, access to the existing
162 object should be as light-weight as possible.
164 [section Implementation]
168 #include <boost/atomic.hpp>
169 #include <boost/thread/mutex.hpp>
173 static X * instance()
175 X * tmp = instance_.load(boost::memory_order_consume);
177 boost::mutex::scoped_lock guard(instantiation_mutex);
178 tmp = instance_.load(boost::memory_order_consume);
181 instance_.store(tmp, boost::memory_order_release);
187 static boost::atomic<X *> instance_;
188 static boost::mutex instantiation_mutex;
191 boost::atomic<X *> X::instance_(0);
199 X * x = X::instance();
206 The mutex makes sure that only one instance of the object is
207 ever created. The [^instance] method must make sure that any
208 dereference of the object strictly "happens after" creating
209 the instance in another thread. The use of [^memory_order_release]
210 after creating and initializing the object and [^memory_order_consume]
211 before dereferencing the object provides this guarantee.
213 It would be permissible to use [^memory_order_acquire] instead of
214 [^memory_order_consume], but this provides a stronger guarantee
215 than is required since only operations depending on the value of
216 the pointer need to be ordered.
222 [section:example_ringbuffer Wait-free ring buffer]
224 A ['wait-free ring buffer] provides a mechanism for relaying objects
225 from one single "producer" thread to one single "consumer" thread without
226 any locks. The operations on this data structure are "wait-free" which
227 means that each operation finishes within a constant number of steps.
228 This makes this data structure suitable for use in hard real-time systems
229 or for communication with interrupt/signal handlers.
231 [section Implementation]
235 #include <boost/atomic.hpp>
237 template<typename T, size_t Size>
240 ringbuffer() : head_(0), tail_(0) {}
242 bool push(const T & value)
244 size_t head = head_.load(boost::memory_order_relaxed);
245 size_t next_head = next(head);
246 if (next_head == tail_.load(boost::memory_order_acquire))
249 head_.store(next_head, boost::memory_order_release);
254 size_t tail = tail_.load(boost::memory_order_relaxed);
255 if (tail == head_.load(boost::memory_order_acquire))
258 tail_.store(next(tail), boost::memory_order_release);
262 size_t next(size_t current)
264 return (current + 1) % Size;
267 boost::atomic<size_t> head_, tail_;
276 ringbuffer<int, 32> r;
278 // try to insert an element
279 if (r.push(42)) { /* succeeded */ }
280 else { /* buffer full */ }
282 // try to retrieve an element
284 if (r.pop(value)) { /* succeeded */ }
285 else { /* buffer empty */ }
291 The implementation makes sure that the ring indices do
292 not "lap-around" each other to ensure that no elements
293 are either lost or read twice.
295 Furthermore it must guarantee that read-access to a
296 particular object in [^pop] "happens after" it has been
297 written in [^push]. This is achieved by writing [^head_ ]
298 with "release" and reading it with "acquire". Conversely
299 the implementation also ensures that read access to
300 a particular ring element "happens before" before
301 rewriting this element with a new value by accessing [^tail_]
302 with appropriate ordering constraints.
308 [section:mp_queue Wait-free multi-producer queue]
310 The purpose of the ['wait-free multi-producer queue] is to allow
311 an arbitrary number of producers to enqueue objects which are
312 retrieved and processed in FIFO order by a single consumer.
314 [section Implementation]
319 class waitfree_queue {
325 void push(const T &data)
329 node * stale_head = head_.load(boost::memory_order_relaxed);
331 n->next = stale_head;
332 } while (!head_.compare_exchange_weak(stale_head, n, boost::memory_order_release));
337 T * last = pop_all_reverse(), * first = 0;
347 waitfree_queue() : head_(0) {}
349 // alternative interface if ordering is of no importance
350 node * pop_all_reverse(void)
352 return head_.exchange(0, boost::memory_order_consume);
355 boost::atomic<node *> head_;
364 waitfree_queue<int> q;
371 waitfree_queue<int>::node * x = q.pop_all()
375 // process tmp->data, probably delete it afterwards
383 The implementation guarantees that all objects enqueued are
384 processed in the order they were enqueued by building a singly-linked
385 list of object in reverse processing order. The queue is atomically
386 emptied by the consumer and brought into correct order.
388 It must be guaranteed that any access to an object to be enqueued
389 by the producer "happens before" any access by the consumer. This
390 is assured by inserting objects into the list with ['release] and
391 dequeuing them with ['consume] memory order. It is not
392 necessary to use ['acquire] memory order in [^waitfree_queue::pop_all]
393 because all operations involved depend on the value of
394 the atomic pointer through dereference