123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- [/
- / 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<T>] 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&<T,S=sizeof(T),I=is_integral_type<T> >]:
- 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<T,S=sizeof(T)>]
- or [^boost::detail::atomic::platform_atomic_integral<T,S=sizeof(T)>].
- 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<T,S=sizeof(T)>]
- -- the rationale is that platform implementors only need to provide
- integer-type operations.
- * [^boost::detail::atomic::platform_atomic_integral<T,S=sizeof(T)>]
- 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<T,S=sizeof(T)>]
- 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<T>]
- 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<T,S=sizeof(T)>]:
- [c++]
- template<typename T>
- class platform_atomic_integral<T, 4>
- {
- 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<typename T>
- 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<typename T>
- class platform_atomic_integral<T, 4>
- : public boost::detail::atomic::build_atomic_from_minimal<my_atomic_32<T> >
- {
- public:
- typedef build_atomic_from_minimal<my_atomic_32<T> > 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] (<I>supported on gcc only</I>) 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<typename T>
- 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<typename T>
- class platform_atomic_integral<T, 1> :
- public build_atomic_from_larger_type<my_atomic_32<uint32_t>, T>
- {
- public:
- typedef build_atomic_from_larger_type<my_atomic_32<uint32_t>, 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<T,S=sizeof(T)>]
- 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]
|