From c32984c3a8d066ba9abc9e0f7218c6d71d1745dd Mon Sep 17 00:00:00 2001 From: Kevin Bracey Date: Fri, 5 Oct 2018 15:55:29 +0300 Subject: [PATCH] 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. --- platform/mbed_critical.c | 23 +++++++++++++++++++++++ platform/mbed_critical.h | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/platform/mbed_critical.c b/platform/mbed_critical.c index 59876db431..320319ba63 100644 --- a/platform/mbed_critical.c +++ b/platform/mbed_critical.c @@ -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; diff --git a/platform/mbed_critical.h b/platform/mbed_critical.h index 2c3a2d6099..c4a510ca8e 100644 --- a/platform/mbed_critical.h +++ b/platform/mbed_critical.h @@ -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