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 10The implementation uses multiple layers of template classes that 11inherit from the next lower level each and refine or adapt the respective 12underlying 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 79In principle implementors are responsible for providing the 80full 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]). 85These 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 110As noted above, it will usually suffice to specialize on the second 111template argument, indicating the size of the data type in bytes. 112 113[section:automatic_buildup Templates for automatic build-up] 114 115Often only a portion of the required operations can be 116usefully mapped to machine instructions. Several helper template 117classes are provided that can automatically synthesize missing methods to 118complete an implementation. 119 120At 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 155The template [^boost::detail::atomic::build_atomic_from_minimal] 156can 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 171There are several helper classes to assist in building "complete" 172atomic 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 223These 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 229There is one other helper template that can build sub-word-sized 230atomic data types even though the underlying architecture allows 231only 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 246The above would create an atomic data type of 1 byte size, and 247use masking and shifts to map it to 32-bit atomic operations. 248The base type must implement [^load], [^store] 249and [^compare_exchange_weak] for this to work. 250 251[endsect] 252 253[section:other_sizes Atomic data types for unusual object sizes] 254 255In unusual circumstances, an implementor may also opt to specialize 256[^public boost::detail::atomic::platform_atomic<T,S=sizeof(T)>] 257to provide support for atomic objects not fitting an integral size. 258If 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 269The type of the data to be stored in the atomic 270variable (template parameter [^T]) 271is exposed to this class, and the type may have 272overloaded assignment and comparison operators -- 273using these overloaded operators however will result 274in an error. The implementor is responsible for 275accessing the objects in a way that does not 276invoke 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 285Platform implementors need to provide a function performing 286the action required for [funcref boost::atomic_thread_fence atomic_thread_fence] 287(the fallback implementation will just perform an atomic operation 288on an integer object). This is achieved by specializing the 289[^boost::detail::atomic::platform_atomic_thread_fence] template 290function 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 304The template specializations should be put into a header file 305in the [^boost/atomic/detail] directory, preferably 306specifying supported compiler and architecture in its name. 307 308The file [^boost/atomic/detail/platform.hpp] must 309subsequently be modified to conditionally include the new 310header. 311 312[endsect] 313