macro LFDS711_PAL_ATOMIC_CAS

From liblfds.org
Jump to: navigation, search

Source File

└───liblfds711
    └───inc
        └───liblfds711
                lfds711_misc.h
                lfds711_porting_abstraction_layer_compiler.h

Enums

enum lfds711_misc_cas_strength
{
  LFDS711_MISC_CAS_WEAK,
  LFDS711_MISC_CAS_STRONG
};

Macro

#define LFDS711_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare, new_destination, cas_strength, result )  [compiler atomic CAS instrinsic]

Parameters

pointer_to_destination

A pointer to a lfds711_pal_uint_t volatile. The value of *pointer_to_destination is compared to the value of *pointer_to_compare and if they are equal, new_value is written into *pointer_to_destination.

pointer_to_compare

A pointer to a lfds711_pal_uint_t. Whether or not the compare is successful (and so whether or not new_value is written into *pointer_to_destination), the original value of *pointer_to_destination is written into *pointer_to_compare, i.e. the compare value is always lost.

new_destination

A lfds711_pal_uint_t. This value is written into *pointer_to_destination if *pointer_to_destination and *pointer_to_compare are equal.

cas_strength

This argument is only meaningful on LL/SC platforms, and if set to LFDS711_MISC_CAS_STRONG indicates the macro should internally retry if the LL/SC operation aborted. See Notes.

result

A char unsigned, which is set to 1 if the swap occurred, 0 if not.

Return Value

No return value.

Example

#define LFDS711_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare, new_destination, cas_strength, result )                                                                                          \
{                                                                                                                                                                                                            \
  lfds711_pal_uint_t                                                                                                                                                                                         \
    original_compare;                                                                                                                                                                                        \
                                                                                                                                                                                                             \
  /* LFDS711_PAL_ASSERT( (pointer_to_destination) != NULL ); */                                                                                                                                              \
  /* LFDS711_PAL_ASSERT( (pointer_to_compare) != NULL ); */                                                                                                                                                  \
  /* TRD : new_destination can be any value in its range */                                                                                                                                                  \
  /* TRD : cas_strength can be any value in its range */                                                                                                                                                     \
  /* TRD : result can be any value in its range */                                                                                                                                                           \
                                                                                                                                                                                                             \
  original_compare = (lfds711_pal_uint_t) *(pointer_to_compare);                                                                                                                                             \
                                                                                                                                                                                                             \
  LFDS711_PAL_BARRIER_COMPILER_FULL;                                                                                                                                                                         \
  *(lfds711_pal_uint_t *) (pointer_to_compare) = (lfds711_pal_uint_t) _InterlockedCompareExchange_nf( (long volatile *) (pointer_to_destination), (long) (new_destination), (long) *(pointer_to_compare) );  \
  LFDS711_PAL_BARRIER_COMPILER_FULL;                                                                                                                                                                         \
                                                                                                                                                                                                             \
  result = (char unsigned) ( original_compare == (lfds711_pal_uint_t) *(pointer_to_compare) );                                                                                                               \
}

Optionality

This macro is optional. If it is not given, the macro must be absent, rather than empty.

Notes

All of the atomic operation macros open and close with curley braces as some of them need to declare variables on the stack, so that they can operate in ways which match their 'prototype' (i.e. they may need a bit of temporary storage, as the way in which the macro is arranged to work doesn't map directly to the atomic intrinsic prototype for that platform). We see this here in the example.

The actual atomic intrinsic if it does not inherently provide compiler barriers itself MUST be immediately preceeded and followed by LFDS711_PAL_BARRIER_COMPILER_FULL. This is to prevent compiler re-ordering.

Finally, we get to the actual atomic operation itself. The liblfds711_pal_uint_t types need to be cast to the types the intrinsic expects, and to the maximum extent possible eschew any memory barriers. On ARM, for example, memory barriers and atomic operations are wholly seperated and on that platform, the operation is and is only an atomic operation. The data structures themselves issue memory barriers as and when they must, and any additional barriers issued within the atomic macros are only overhead. On x86 and x64, sadly, memory barriers are built into the atomic operations and cannot be removed. On Itanium, it looks like atomic operations must occur with a barrier, but it is possible to choose a load, store or full barrier, and as such on that platform, the load barrier is always used, as it is the lowest cost of the three.

In this particular example, which is for GCC versions equal to or greater than 4.1.2 and less than 4.7.3, we see the compiler intrinsic returns the original value of the destination, but the macro requires this value to be written into the compare variable. It seems strange that the macro is using a pointer to compare - it could just be using compare directly. The reason for this is to be similar in style to the LFDS711_PAL_ATOMIC_DWCAS macro; some DWCAS implementations, where they take an array of two atomic variables, take a pointer for their compare argument and write the original value of destination back to the compare argument, as it cannot (by being an array of two variables) be returned.

If this atomic operaton is not available, the macro must be left undefined, which will lead to a placeholder version automatically being used. This placeholder version if called first calls LFDS711_PAL_ASSERT and then, assuming execution has continued (i.e. LFDS711_PAL_ASSERT is not defined, or is defined but this is a release user-mode build and so asserts are not being checked) will attempt to write 0 into memory location 0, to deliberately crash.

See Also