Add atomic_flag utility

An atomic flag primitive is sometimes wanted, and it is cumbersome to
create it from the compare-and-swap operation - cumbersome enough that
people often don't bother.

Put in a core_util_atomic_flag that follows the C11/C++11 atomic_flag
API, such that it could be mapped to it with #define later.
pull/8328/head
Kevin Bracey 2018-10-05 15:55:29 +03:00
parent 0b27736536
commit c32984c3a8
2 changed files with 60 additions and 0 deletions

View File

@ -100,6 +100,11 @@ void core_util_critical_section_exit(void)
}
}
void core_util_atomic_flag_clear(volatile core_util_atomic_flag *flagPtr)
{
flagPtr->_flag = false;
}
#if MBED_EXCLUSIVE_ACCESS
/* Supress __ldrex and __strex deprecated warnings - "#3731-D: intrinsic is deprecated" */
@ -107,6 +112,15 @@ void core_util_critical_section_exit(void)
#pragma diag_suppress 3731
#endif
bool core_util_atomic_flag_test_and_set(volatile core_util_atomic_flag *flagPtr)
{
uint8_t currentValue;
do {
currentValue = __LDREXB(&flagPtr->_flag);
} while (__STREXB(true, &flagPtr->_flag));
return currentValue;
}
bool core_util_atomic_cas_u8(volatile uint8_t *ptr, uint8_t *expectedCurrentValue, uint8_t desiredValue)
{
do {
@ -204,6 +218,15 @@ uint32_t core_util_atomic_decr_u32(volatile uint32_t *valuePtr, uint32_t delta)
#else
bool core_util_atomic_flag_test_and_set(volatile core_util_atomic_flag *flagPtr)
{
core_util_critical_section_enter();
uint8_t currentValue = flagPtr->_flag;
flagPtr->_flag = true;
core_util_critical_section_exit();
return currentValue;
}
bool core_util_atomic_cas_u8(volatile uint8_t *ptr, uint8_t *expectedCurrentValue, uint8_t desiredValue)
{
bool success;

View File

@ -89,6 +89,43 @@ void core_util_critical_section_exit(void);
*/
bool core_util_in_critical_section(void);
/**
* A lock-free, primitive atomic flag.
*
* Emulate C11's atomic_flag. The flag is initially in an indeterminate state
* unless explicitly initialised with CORE_UTIL_ATOMIC_FLAG_INIT.
*/
typedef struct core_util_atomic_flag {
uint8_t _flag;
} core_util_atomic_flag;
/**
* Initialiser for a core_util_atomic_flag.
*
* Example:
* ~~~
* core_util_atomic_flag in_progress = CORE_UTIL_ATOMIC_FLAG_INIT;
* ~~~
*/
#define CORE_UTIL_ATOMIC_FLAG_INIT { 0 }
/**
* Atomic test and set.
*
* Atomically tests then sets the flag to true, returning the previous value.
*
* @param flagPtr Target flag being tested and set.
* @return The previous value.
*/
bool core_util_atomic_flag_test_and_set(volatile core_util_atomic_flag *flagPtr);
/**
* Atomic clear.
*
* @param flagPtr Target flag being cleared.
*/
void core_util_atomic_flag_clear(volatile core_util_atomic_flag *flagPtr);
/**
* Atomic compare and set. It compares the contents of a memory location to a
* given value and, only if they are the same, modifies the contents of that