[/ / Copyright (c) 2009 Helge Bahmann / / Distributed under the Boost Software License, Version 1.0. (See accompanying / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /] [section:template_organization Organization of class template layers] The implementation uses multiple layers of template classes that inherit from the next lower level each and refine or adapt the respective underlying class: * [^boost::atomic] is the topmost-level, providing the external interface. Implementation-wise, it does not add anything (except for hiding copy constructor and assignment operator). * [^boost::detail::atomic::internal_atomic& >]: This layer is mainly responsible for providing the overloaded operators mapping to API member functions (e.g. [^+=] to [^fetch_add]). The defaulted template parameter [^I] allows to expose the correct API functions (via partial template specialization): For non-integral types, it only publishes the various [^exchange] functions as well as load and store, for integral types it additionally exports arithmetic and logic operations. [br] Depending on whether the given type is integral, it inherits from either [^boost::detail::atomic::platform_atomic] or [^boost::detail::atomic::platform_atomic_integral]. There is however some special-casing: for non-integral types of size 1, 2, 4 or 8, it will coerce the datatype into an integer representation and delegate to [^boost::detail::atomic::platform_atomic_integral] -- the rationale is that platform implementors only need to provide integer-type operations. * [^boost::detail::atomic::platform_atomic_integral] must provide the full set of operations for an integral type T (i.e. [^load], [^store], [^exchange], [^compare_exchange_weak], [^compare_exchange_strong], [^fetch_add], [^fetch_sub], [^fetch_and], [^fetch_or], [^fetch_xor], [^is_lock_free]). The default implementation uses locking to emulate atomic operations, so this is the level at which implementors should provide template specializations to add support for platform-specific atomic operations. [br] The two separate template parameters allow separate specialization on size and type (which, with fixed size, cannot specify more than signedness/unsignedness). The rationale is that most platform-specific atomic operations usually depend only on the operand size, so that common implementations for signed/unsigned types are possible. Signedness allows to properly to choose sign-extending instructions for the [^load] operation, avoiding later conversion. The expectation is that in most implementations this will be a normal assignment in C, possibly accompanied by memory fences, so that the compiler can automatically choose the correct instruction. * At the lowest level, [^boost::detail::atomic::platform_atomic] provides the most basic atomic operations ([^load], [^store], [^exchange], [^compare_exchange_weak], [^compare_exchange_strong]) for arbitrarily generic data types. The default implementation uses locking as a fallback mechanism. Implementors generally do not have to specialize at this level (since these will not be used for the common integral type sizes of 1, 2, 4 and 8 bytes), but if s/he can if s/he so wishes to provide truly atomic operations for "odd" data type sizes. Some amount of care must be taken as the "raw" data type passed in from the user through [^boost::atomic] is visible here -- it thus needs to be type-punned or otherwise manipulated byte-by-byte to avoid using overloaded assignment, comparison operators and copy constructors. [endsect] [section:platform_atomic_implementation Implementing platform-specific atomic operations] In principle implementors are responsible for providing the full range of named member functions of an atomic object (i.e. [^load], [^store], [^exchange], [^compare_exchange_weak], [^compare_exchange_strong], [^fetch_add], [^fetch_sub], [^fetch_and], [^fetch_or], [^fetch_xor], [^is_lock_free]). These must be implemented as partial template specializations for [^boost::detail::atomic::platform_atomic_integral]: [c++] template class platform_atomic_integral { public: explicit platform_atomic_integral(T v) : i(v) {} platform_atomic_integral(void) {} T load(memory_order order=memory_order_seq_cst) const volatile { // platform-specific code } void store(T v, memory_order order=memory_order_seq_cst) volatile { // platform-specific code } private: volatile T i; }; As noted above, it will usually suffice to specialize on the second template argument, indicating the size of the data type in bytes. [section:automatic_buildup Templates for automatic build-up] Often only a portion of the required operations can be usefully mapped to machine instructions. Several helper template classes are provided that can automatically synthesize missing methods to complete an implementation. At the minimum, an implementor must provide the [^load], [^store], [^compare_exchange_weak] and [^is_lock_free] methods: [c++] template class my_atomic_32 { public: my_atomic_32() {} my_atomic_32(T initial_value) : value(initial_value) {} T load(memory_order order=memory_order_seq_cst) volatile const { // platform-specific code } void store(T new_value, memory_order order=memory_order_seq_cst) volatile { // platform-specific code } bool compare_exchange_weak(T &expected, T desired, memory_order success_order, memory_order_failure_order) volatile { // platform-specific code } bool is_lock_free() const volatile {return true;} protected: // typedef is required for classes inheriting from this typedef T integral_type; private: T value; }; The template [^boost::detail::atomic::build_atomic_from_minimal] can then take care of the rest: [c++] template class platform_atomic_integral : public boost::detail::atomic::build_atomic_from_minimal > { public: typedef build_atomic_from_minimal > super; explicit platform_atomic_integral(T v) : super(v) {} platform_atomic_integral(void) {} }; There are several helper classes to assist in building "complete" atomic implementations from different starting points: * [^build_atomic_from_minimal] requires * [^load] * [^store] * [^compare_exchange_weak] (4-operand version) * [^build_atomic_from_exchange] requires * [^load] * [^store] * [^compare_exchange_weak] (4-operand version) * [^compare_exchange_strong] (4-operand version) * [^exchange] * [^build_atomic_from_add] requires * [^load] * [^store] * [^compare_exchange_weak] (4-operand version) * [^compare_exchange_strong] (4-operand version) * [^exchange] * [^fetch_add] * [^build_atomic_from_typical] (supported on gcc only) requires * [^load] * [^store] * [^compare_exchange_weak] (4-operand version) * [^compare_exchange_strong] (4-operand version) * [^exchange] * [^fetch_add_var] (protected method) * [^fetch_inc] (protected method) * [^fetch_dec] (protected method) This will generate a [^fetch_add] method that calls [^fetch_inc]/[^fetch_dec] when the given parameter is a compile-time constant equal to +1 or -1 respectively, and [^fetch_add_var] in all other cases. This provides a mechanism for optimizing the extremely common case of an atomic variable being used as a counter. The prototypes for these methods to be implemented is: [c++] template class my_atomic { public: T fetch_inc(memory_order order) volatile; T fetch_dec(memory_order order) volatile; T fetch_add_var(T counter, memory_order order) volatile; }; These helper templates are defined in [^boost/atomic/detail/builder.hpp]. [endsect] [section:automatic_buildup_small Build sub-word-sized atomic data types] There is one other helper template that can build sub-word-sized atomic data types even though the underlying architecture allows only word-sized atomic operations: [c++] template class platform_atomic_integral : public build_atomic_from_larger_type, T> { public: typedef build_atomic_from_larger_type, T> super; explicit platform_atomic_integral(T v) : super(v) {} platform_atomic_integral(void) {} }; The above would create an atomic data type of 1 byte size, and use masking and shifts to map it to 32-bit atomic operations. The base type must implement [^load], [^store] and [^compare_exchange_weak] for this to work. [endsect] [section:other_sizes Atomic data types for unusual object sizes] In unusual circumstances, an implementor may also opt to specialize [^public boost::detail::atomic::platform_atomic] to provide support for atomic objects not fitting an integral size. If you do that, keep the following things in mind: * There is no reason to ever do this for object sizes of 1, 2, 4 and 8 * Only the following methods need to be implemented: * [^load] * [^store] * [^compare_exchange_weak] (4-operand version) * [^compare_exchange_strong] (4-operand version) * [^exchange] The type of the data to be stored in the atomic variable (template parameter [^T]) is exposed to this class, and the type may have overloaded assignment and comparison operators -- using these overloaded operators however will result in an error. The implementor is responsible for accessing the objects in a way that does not invoke either of these operators (using e.g. [^memcpy] or type-casts). [endsect] [endsect] [section:platform_atomic_fences Fences] Platform implementors need to provide a function performing the action required for [funcref boost::atomic_thread_fence atomic_thread_fence] (the fallback implementation will just perform an atomic operation on an integer object). This is achieved by specializing the [^boost::detail::atomic::platform_atomic_thread_fence] template function in the following way: [c++] template<> void platform_atomic_thread_fence(memory_order order) { // platform-specific code here } [endsect] [section:platform_atomic_puttogether Putting it altogether] The template specializations should be put into a header file in the [^boost/atomic/detail] directory, preferably specifying supported compiler and architecture in its name. The file [^boost/atomic/detail/platform.hpp] must subsequently be modified to conditionally include the new header. [endsect]