]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | / Copyright (c) 2009 Helge Bahmann | |
3 | / | |
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) | |
6 | /] | |
7 | ||
8 | [section:template_organization Organization of class template layers] | |
9 | ||
10 | The implementation uses multiple layers of template classes that | |
11 | inherit from the next lower level each and refine or adapt the respective | |
12 | underlying class: | |
13 | ||
14 | * [^boost::atomic<T>] is the topmost-level, providing | |
15 | the external interface. Implementation-wise, it does not add anything | |
16 | (except for hiding copy constructor and assignment operator). | |
17 | ||
18 | * [^boost::detail::atomic::internal_atomic&<T,S=sizeof(T),I=is_integral_type<T> >]: | |
19 | This layer is mainly responsible for providing the overloaded operators | |
20 | mapping to API member functions (e.g. [^+=] to [^fetch_add]). | |
21 | The defaulted template parameter [^I] allows | |
22 | to expose the correct API functions (via partial template | |
23 | specialization): For non-integral types, it only | |
24 | publishes the various [^exchange] functions | |
25 | as well as load and store, for integral types it | |
26 | additionally exports arithmetic and logic operations. | |
27 | [br] | |
28 | Depending on whether the given type is integral, it | |
29 | inherits from either [^boost::detail::atomic::platform_atomic<T,S=sizeof(T)>] | |
30 | or [^boost::detail::atomic::platform_atomic_integral<T,S=sizeof(T)>]. | |
31 | There is however some special-casing: for non-integral types | |
32 | of size 1, 2, 4 or 8, it will coerce the datatype into an integer representation | |
33 | and delegate to [^boost::detail::atomic::platform_atomic_integral<T,S=sizeof(T)>] | |
34 | -- the rationale is that platform implementors only need to provide | |
35 | integer-type operations. | |
36 | ||
37 | * [^boost::detail::atomic::platform_atomic_integral<T,S=sizeof(T)>] | |
38 | must provide the full set of operations for an integral type T | |
39 | (i.e. [^load], [^store], [^exchange], | |
40 | [^compare_exchange_weak], [^compare_exchange_strong], | |
41 | [^fetch_add], [^fetch_sub], [^fetch_and], | |
42 | [^fetch_or], [^fetch_xor], [^is_lock_free]). | |
43 | The default implementation uses locking to emulate atomic operations, so | |
44 | this is the level at which implementors should provide template specializations | |
45 | to add support for platform-specific atomic operations. | |
46 | [br] | |
47 | The two separate template parameters allow separate specialization | |
48 | on size and type (which, with fixed size, cannot | |
49 | specify more than signedness/unsignedness). The rationale is that | |
50 | most platform-specific atomic operations usually depend only on the | |
51 | operand size, so that common implementations for signed/unsigned | |
52 | types are possible. Signedness allows to properly to choose sign-extending | |
53 | instructions for the [^load] operation, avoiding later | |
54 | conversion. The expectation is that in most implementations this will | |
55 | be a normal assignment in C, possibly accompanied by memory | |
56 | fences, so that the compiler can automatically choose the correct | |
57 | instruction. | |
58 | ||
59 | * At the lowest level, [^boost::detail::atomic::platform_atomic<T,S=sizeof(T)>] | |
60 | provides the most basic atomic operations ([^load], [^store], | |
61 | [^exchange], [^compare_exchange_weak], | |
62 | [^compare_exchange_strong]) for arbitrarily generic data types. | |
63 | The default implementation uses locking as a fallback mechanism. | |
64 | Implementors generally do not have to specialize at this level | |
65 | (since these will not be used for the common integral type sizes | |
66 | of 1, 2, 4 and 8 bytes), but if s/he can if s/he so wishes to | |
67 | provide truly atomic operations for "odd" data type sizes. | |
68 | Some amount of care must be taken as the "raw" data type | |
69 | passed in from the user through [^boost::atomic<T>] | |
70 | is visible here -- it thus needs to be type-punned or otherwise | |
71 | manipulated byte-by-byte to avoid using overloaded assignment, | |
72 | comparison operators and copy constructors. | |
73 | ||
74 | [endsect] | |
75 | ||
76 | ||
77 | [section:platform_atomic_implementation Implementing platform-specific atomic operations] | |
78 | ||
79 | In principle implementors are responsible for providing the | |
80 | full range of named member functions of an atomic object | |
81 | (i.e. [^load], [^store], [^exchange], | |
82 | [^compare_exchange_weak], [^compare_exchange_strong], | |
83 | [^fetch_add], [^fetch_sub], [^fetch_and], | |
84 | [^fetch_or], [^fetch_xor], [^is_lock_free]). | |
85 | These must be implemented as partial template specializations for | |
86 | [^boost::detail::atomic::platform_atomic_integral<T,S=sizeof(T)>]: | |
87 | ||
88 | [c++] | |
89 | ||
90 | template<typename T> | |
91 | class platform_atomic_integral<T, 4> | |
92 | { | |
93 | public: | |
94 | explicit platform_atomic_integral(T v) : i(v) {} | |
95 | platform_atomic_integral(void) {} | |
96 | ||
97 | T load(memory_order order=memory_order_seq_cst) const volatile | |
98 | { | |
99 | // platform-specific code | |
100 | } | |
101 | void store(T v, memory_order order=memory_order_seq_cst) volatile | |
102 | { | |
103 | // platform-specific code | |
104 | } | |
105 | ||
106 | private: | |
107 | volatile T i; | |
108 | }; | |
109 | ||
110 | As noted above, it will usually suffice to specialize on the second | |
111 | template argument, indicating the size of the data type in bytes. | |
112 | ||
113 | [section:automatic_buildup Templates for automatic build-up] | |
114 | ||
115 | Often only a portion of the required operations can be | |
116 | usefully mapped to machine instructions. Several helper template | |
117 | classes are provided that can automatically synthesize missing methods to | |
118 | complete an implementation. | |
119 | ||
120 | At the minimum, an implementor must provide the | |
121 | [^load], [^store], | |
122 | [^compare_exchange_weak] and | |
123 | [^is_lock_free] methods: | |
124 | ||
125 | [c++] | |
126 | ||
127 | template<typename T> | |
128 | class my_atomic_32 { | |
129 | public: | |
130 | my_atomic_32() {} | |
131 | my_atomic_32(T initial_value) : value(initial_value) {} | |
132 | ||
133 | T load(memory_order order=memory_order_seq_cst) volatile const | |
134 | { | |
135 | // platform-specific code | |
136 | } | |
137 | void store(T new_value, memory_order order=memory_order_seq_cst) volatile | |
138 | { | |
139 | // platform-specific code | |
140 | } | |
141 | bool compare_exchange_weak(T &expected, T desired, | |
142 | memory_order success_order, | |
143 | memory_order_failure_order) volatile | |
144 | { | |
145 | // platform-specific code | |
146 | } | |
147 | bool is_lock_free() const volatile {return true;} | |
148 | protected: | |
149 | // typedef is required for classes inheriting from this | |
150 | typedef T integral_type; | |
151 | private: | |
152 | T value; | |
153 | }; | |
154 | ||
155 | The template [^boost::detail::atomic::build_atomic_from_minimal] | |
156 | can then take care of the rest: | |
157 | ||
158 | [c++] | |
159 | ||
160 | template<typename T> | |
161 | class platform_atomic_integral<T, 4> | |
162 | : public boost::detail::atomic::build_atomic_from_minimal<my_atomic_32<T> > | |
163 | { | |
164 | public: | |
165 | typedef build_atomic_from_minimal<my_atomic_32<T> > super; | |
166 | ||
167 | explicit platform_atomic_integral(T v) : super(v) {} | |
168 | platform_atomic_integral(void) {} | |
169 | }; | |
170 | ||
171 | There are several helper classes to assist in building "complete" | |
172 | atomic implementations from different starting points: | |
173 | ||
174 | * [^build_atomic_from_minimal] requires | |
175 | * [^load] | |
176 | * [^store] | |
177 | * [^compare_exchange_weak] (4-operand version) | |
178 | ||
179 | * [^build_atomic_from_exchange] requires | |
180 | * [^load] | |
181 | * [^store] | |
182 | * [^compare_exchange_weak] (4-operand version) | |
183 | * [^compare_exchange_strong] (4-operand version) | |
184 | * [^exchange] | |
185 | ||
186 | * [^build_atomic_from_add] requires | |
187 | * [^load] | |
188 | * [^store] | |
189 | * [^compare_exchange_weak] (4-operand version) | |
190 | * [^compare_exchange_strong] (4-operand version) | |
191 | * [^exchange] | |
192 | * [^fetch_add] | |
193 | ||
194 | * [^build_atomic_from_typical] (<I>supported on gcc only</I>) requires | |
195 | * [^load] | |
196 | * [^store] | |
197 | * [^compare_exchange_weak] (4-operand version) | |
198 | * [^compare_exchange_strong] (4-operand version) | |
199 | * [^exchange] | |
200 | * [^fetch_add_var] (protected method) | |
201 | * [^fetch_inc] (protected method) | |
202 | * [^fetch_dec] (protected method) | |
203 | ||
204 | This will generate a [^fetch_add] method | |
205 | that calls [^fetch_inc]/[^fetch_dec] | |
206 | when the given parameter is a compile-time constant | |
207 | equal to +1 or -1 respectively, and [^fetch_add_var] | |
208 | in all other cases. This provides a mechanism for | |
209 | optimizing the extremely common case of an atomic | |
210 | variable being used as a counter. | |
211 | ||
212 | The prototypes for these methods to be implemented is: | |
213 | [c++] | |
214 | ||
215 | template<typename T> | |
216 | class my_atomic { | |
217 | public: | |
218 | T fetch_inc(memory_order order) volatile; | |
219 | T fetch_dec(memory_order order) volatile; | |
220 | T fetch_add_var(T counter, memory_order order) volatile; | |
221 | }; | |
222 | ||
223 | These helper templates are defined in [^boost/atomic/detail/builder.hpp]. | |
224 | ||
225 | [endsect] | |
226 | ||
227 | [section:automatic_buildup_small Build sub-word-sized atomic data types] | |
228 | ||
229 | There is one other helper template that can build sub-word-sized | |
230 | atomic data types even though the underlying architecture allows | |
231 | only word-sized atomic operations: | |
232 | ||
233 | [c++] | |
234 | ||
235 | template<typename T> | |
236 | class platform_atomic_integral<T, 1> : | |
237 | public build_atomic_from_larger_type<my_atomic_32<uint32_t>, T> | |
238 | { | |
239 | public: | |
240 | typedef build_atomic_from_larger_type<my_atomic_32<uint32_t>, T> super; | |
241 | ||
242 | explicit platform_atomic_integral(T v) : super(v) {} | |
243 | platform_atomic_integral(void) {} | |
244 | }; | |
245 | ||
246 | The above would create an atomic data type of 1 byte size, and | |
247 | use masking and shifts to map it to 32-bit atomic operations. | |
248 | The base type must implement [^load], [^store] | |
249 | and [^compare_exchange_weak] for this to work. | |
250 | ||
251 | [endsect] | |
252 | ||
253 | [section:other_sizes Atomic data types for unusual object sizes] | |
254 | ||
255 | In unusual circumstances, an implementor may also opt to specialize | |
256 | [^public boost::detail::atomic::platform_atomic<T,S=sizeof(T)>] | |
257 | to provide support for atomic objects not fitting an integral size. | |
258 | If you do that, keep the following things in mind: | |
259 | ||
260 | * There is no reason to ever do this for object sizes | |
261 | of 1, 2, 4 and 8 | |
262 | * Only the following methods need to be implemented: | |
263 | * [^load] | |
264 | * [^store] | |
265 | * [^compare_exchange_weak] (4-operand version) | |
266 | * [^compare_exchange_strong] (4-operand version) | |
267 | * [^exchange] | |
268 | ||
269 | The type of the data to be stored in the atomic | |
270 | variable (template parameter [^T]) | |
271 | is exposed to this class, and the type may have | |
272 | overloaded assignment and comparison operators -- | |
273 | using these overloaded operators however will result | |
274 | in an error. The implementor is responsible for | |
275 | accessing the objects in a way that does not | |
276 | invoke either of these operators (using e.g. | |
277 | [^memcpy] or type-casts). | |
278 | ||
279 | [endsect] | |
280 | ||
281 | [endsect] | |
282 | ||
283 | [section:platform_atomic_fences Fences] | |
284 | ||
285 | Platform implementors need to provide a function performing | |
286 | the action required for [funcref boost::atomic_thread_fence atomic_thread_fence] | |
287 | (the fallback implementation will just perform an atomic operation | |
288 | on an integer object). This is achieved by specializing the | |
289 | [^boost::detail::atomic::platform_atomic_thread_fence] template | |
290 | function in the following way: | |
291 | ||
292 | [c++] | |
293 | ||
294 | template<> | |
295 | void platform_atomic_thread_fence(memory_order order) | |
296 | { | |
297 | // platform-specific code here | |
298 | } | |
299 | ||
300 | [endsect] | |
301 | ||
302 | [section:platform_atomic_puttogether Putting it altogether] | |
303 | ||
304 | The template specializations should be put into a header file | |
305 | in the [^boost/atomic/detail] directory, preferably | |
306 | specifying supported compiler and architecture in its name. | |
307 | ||
308 | The file [^boost/atomic/detail/platform.hpp] must | |
309 | subsequently be modified to conditionally include the new | |
310 | header. | |
311 | ||
312 | [endsect] |