BDE 4.14.0 Production release
|
Provide default implementation for atomic operations.
[PRIVATE] This component provides classes having default implementations of atomic operations independent of a any specific platform. The purpose of these classes is for them to be combined with platform-specific implementations of atomic operations to form a full set of atomic operations.
[WARNING] This component should not be used directly by client code. It is subject to change without warning.
The implementation of atomic operations splits the set of operations into two groups: essential (core) operations and non-essential operations. The essential core operations, such as getInt
, setInt
, swapInt
, testAndSwapInt
, must be implemented on every target platform. The non-essential operations, such as incrementInt
, decrementInt
, have default implementations written in terms of the core operations (though the default implementation may be overridden for a target platform).
This component provides the default implementations for non-essential atomic operations. For the essential core operations, it provides only function prototypes without any concrete implementation. The concrete implementation for core operations on each platform are provided by a platform-specific implementation component.
There are about 70 different atomic operations we provide in this atomics library. Only a fraction of these operations (about 10-15) have implementations specific to a target platform. These are the core atomic operations. Other operations have implementations that are independent of the platform, and are defined in terms of those core operations.
A primary design objective for this implementation of atomic operations is to provide a single common implementation for the platform-independent, non-essential, atomic operations. This way, each platform-specific implementation of atomics needs to provide only the 10-15 core operations, and may obtain the remaining 60 atomic operations automatically from this default implementation component.
The implementation technique used to achieve this goal is called the Curiously Recurring Template Pattern, or CRTP.
In the CRTP a derived class inherits from the base class, but the base class is parameterized with the derived class template parameter. In the context of atomic operations, the CRTP allows the non-essential base class methods to be accessed through the platform specific derived class, while the template parameterization of the base class allows the implementations for platform-independent (non-essential) operations in the base class to be implemented in terms of the core operations implemented in the platform-specific derived class.
Let's illustrate this idea with an example. The base class, AtomicsBase
, will provide an implementation for a single non-essential atomic operation, getIntAcquire
, whose implementation delegates to the platform-specific implementation of another, (core) atomic operation, getInt
:
Now we define a platform-specific implementation of the atomics class for the x86 platform providing the core operation getInt
:
To get an idea what the effective interface and the implementation of the AtomicsX86
class looks like, we can flatten the hierarchy of AtomicsX86
and remove the AtomicsBase
class from the picture, as if we implemented AtomicsX86
without the help of AtomicsBase
. The effective interface and the implementation of AtomicsX86
is as follows:
The above code shows that the design goal is achieved: Platform-independent atomic operations are factored out into easily reusable AtomicsBase
which, when combined with platform-specific atomic operations, produces the complete set of atomic operations for a given target-platform.
This section describes the hierarchy of components and types that implement atomic operations. Below is the list of base classes providing default implementations of platform-independent, non-essential atomic operations. Atomic operations for different data types are factored into their own base classes:
The platform-specific core atomic operations are left unimplemented in these default implementation classes. The implementations for those operations have to be provided by a platform-specific derived classes.
The above default implementation classes are combined (using inheritance) into more specialized base classes that provide nearly complete set of atomic operations for a generic platform:
This is how the generic platform base classes are composed:
A typical derived class implementing platform-specific atomic operations needs to derive from either bsls::AtomicOperations_Default32
(if the platform is 32-bit) or bsls::AtomicOperations_Default64
(if the platform is 64-bit).
For example, let's take the X86_64 platform with GCC compiler. The derived class for this platform, bsls::AtomicOperations_X64_ALL_GCC
, inherits from bsls::AtomicOperations_Default64
and implements all platform-specific atomic operations:
A derived class can also override some default atomic implementations from a base class. For example, bsls::AtomicOperations_X64_ALL_GCC
can provide a better implementation for getIntAcquire
than the one provided by the bsls::AtomicOperations_DefaultInt
base class. So bsls::AtomicOperations_X64_ALL_GCC
implements its own getIntAcquire
with the same signature as in the base class:
Technically speaking, this is not overriding, but hiding the respective methods of the base class, but for our purpose, it works as if the methods were overridden.
Platform-specific atomic operations for other platforms are implemented in a similar manner. Here is a conceptual diagram of relationships between classes that provide the default atomic operations and platform-specific atomic operations (the bsls::AtomicOperations_ prefix is omitted for brevity):
The last part of the implementation is the atomic type traits class. We need a special representation for atomic primitive types to ensure conformance to size and alignment requirements for each platform. For example, the atomic primitive type for int
is usually a 4-byte aligned volatile int
. Since the alignment is specified differently for different platforms and compilers, the atomic type traits class allows us to abstract the rest of the implementation of atomics from those differences.
This default implementation component provides only a declaration of a generic atomic type traits class:
Each platform-specific implementation has to provide its own specialization of the type traits class. For example, the specialization for the x86_64 platform defined by the bsls::AtomicOperations_X64_ALL_GCC
class might look like:
This component is a private implementation type of bsls_atomicoperations ; see bsls_atomicoperations for a usage example.