From 215ccc1213d7e53592766b1cb4e0c50592580c54 Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Tue, 28 Apr 2015 22:57:56 +0200 Subject: [PATCH] core: Introduce atomic counters - Move generic implementation of atomic_set_return to core/atomic.c - Generic implementation of atomic compare and swap in core/atomic.c - atomic_cas is used to implement atomic counters in core/include/atomic.h - atomic_int_t is an atomic integer type - ATOMIC_INIT can be used as an initializer for atomic_int_t - ATOMIC_VALUE gets a reference to the value of an atomic integer --- core/atomic.c | 59 +++++++++++++ core/include/atomic.h | 133 +++++++++++++++++++++++++++-- cpu/atmega_common/atomic_arch.c | 33 ------- cpu/cortex-m0_common/atomic_arch.c | 33 ------- cpu/cortex-m3_common/include/cpu.h | 6 ++ cpu/cortex-m4_common/include/cpu.h | 6 ++ cpu/msp430-common/atomic.c | 32 ------- cpu/x86/include/cpu.h | 5 ++ 8 files changed, 203 insertions(+), 104 deletions(-) create mode 100644 core/atomic.c delete mode 100644 cpu/atmega_common/atomic_arch.c delete mode 100644 cpu/cortex-m0_common/atomic_arch.c delete mode 100644 cpu/msp430-common/atomic.c diff --git a/core/atomic.c b/core/atomic.c new file mode 100644 index 000000000..c267a23cb --- /dev/null +++ b/core/atomic.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2015 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + + +/** + * @ingroup core_util + * @{ + * + * @file + * @brief Generic implementation of the kernel's atomic interface + * + * @author Kaspar Schleiser + * @author Oliver Hahm + * @author Joakim Gebart + * + * @} + */ + +#include "irq.h" +#include "cpu.h" +#include "atomic.h" + +#if (ARCH_HAS_ATOMIC_SET_RETURN == 0) + +unsigned int atomic_set_return(unsigned int *val, unsigned int set) +{ + unsigned int mask = disableIRQ(); + unsigned int old_val = *val; + *val = set; + restoreIRQ(mask); + return old_val; +} + +#endif + +/* Set ARCH_HAS_ATOMIC_COMPARE_AND_SWAP within cpu.h to override this function */ +#if (ARCH_HAS_ATOMIC_COMPARE_AND_SWAP == 0) + +int atomic_cas(atomic_int_t *var, int old, int now) +{ + unsigned int mask = disableIRQ(); + + if (ATOMIC_VALUE(*var) != old) { + restoreIRQ(mask); + return 0; + } + + ATOMIC_VALUE(*var) = now; + restoreIRQ(mask); + return 1; +} + +#endif diff --git a/core/include/atomic.h b/core/include/atomic.h index 0d6b64a32..e53a80eb3 100644 --- a/core/include/atomic.h +++ b/core/include/atomic.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2015 Eistec AB * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -10,21 +11,31 @@ * @addtogroup core_util * @{ * - * @file atomic.h - * @brief Atomic getter and setter functions + * @file + * @brief Functions for atomic handling of variables * * @author Kaspar Schleiser + * @author Joakim Gebart */ -#ifndef _ATOMIC_H -#define _ATOMIC_H +#ifndef ATOMIC_H_ +#define ATOMIC_H_ #include "arch/atomic_arch.h" #ifdef __cplusplus - extern "C" { +extern "C" { #endif +/** + * @brief Integer variable for use in atomic counters. + */ +typedef struct atomic_int { + volatile int value; +} atomic_int_t; + + + /** * @brief Sets a new and returns the old value of a variable atomically * @@ -35,9 +46,119 @@ */ unsigned int atomic_set_return(unsigned int *val, unsigned int set); +/** + * @brief Initializer for atomic variables + * + * @param[in] val initial value for the atomic integer + */ +#define ATOMIC_INIT(val) {(val)} + +/** + * @brief Atomic Compare and Swap + * + * Updates the value in var iff the current value of var is equal to old. + * + * @param[inout] var Atomic variable to update + * @param[in] old The old value to compare against + * @param[in] now The new value to write to var + * + * @return 1 if the write completed successfully + * @return 0 if the write failed. + */ +int atomic_cas(atomic_int_t *var, int old, int now); + +/** + * @brief Increment a counter variable by one atomically and return the old value. + * + * @param[inout] var Pointer to a counter variable. + * + * @return The value of *val* before the increment. + */ +static inline int atomic_inc(atomic_int_t *var) +{ + int old; + + do { + old = var->value; + } while (!atomic_cas(var, old, old + 1)); + + return old; +} + +/** + * @brief Decrement a counter variable by one atomically and return the old value. + * + * @param[inout] var Pointer to a counter variable. + * + * @return The value of *val* before the decrement. + */ +static inline int atomic_dec(atomic_int_t *var) +{ + int old; + + do { + old = var->value; + } while (!atomic_cas(var, old, old - 1)); + + return old; +} + +/** + * @brief Set an atomic variable to 1. + * + * @param[inout] var Pointer to an atomic variable to update + * + * @return 1 if the old value was 0 and the variable was successfully updated + * @return 0 if the variable was already set + */ +static inline int atomic_set_to_one(atomic_int_t *var) +{ + do { + if (var->value != 0) { + return 0; + } + } while (!atomic_cas(var, 0, 1)); + + return 1; +} + +/** + * @brief Set an atomic variable to 0. + * + * @param[inout] var Pointer to an atomic variable to update + * + * @return 1 if the old value was not 0 and the variable was successfully updated + * @return 0 if the variable was already cleared + */ +static inline int atomic_set_to_zero(atomic_int_t *var) +{ + int old; + + do { + old = var->value; + + if (old == 0) { + return 0; + } + } while (!atomic_cas(var, old, 0)); + + return 1; +} + +/** + * @brief Get the value of an atomic int + * + * @param[in] var Atomic variable + * + * @return the value of the atomic integer + * + * @note This can be used for non-thread-safe assignment of the atomic integer + */ +#define ATOMIC_VALUE(var) ((var).value) + #ifdef __cplusplus } #endif -#endif /* _ATOMIC_H */ +#endif /* ATOMIC_H_ */ /** @} */ diff --git a/cpu/atmega_common/atomic_arch.c b/cpu/atmega_common/atomic_arch.c deleted file mode 100644 index e945cb337..000000000 --- a/cpu/atmega_common/atomic_arch.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup cpu_atmega_common - * @{ - * - * @file atomic_arch.c - * @brief Implementation of the kernels atomic interface - * - * @author Stefan Pfeiffer - * @author Hauke Petersen - * @author Hinnerk van Bruinehsen - * - * @} - */ - -#include "arch/atomic_arch.h" -#include "irq.h" - -unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value) -{ - disableIRQ(); - unsigned int old = *to_set; - *to_set = value; - enableIRQ(); - return old; -} diff --git a/cpu/cortex-m0_common/atomic_arch.c b/cpu/cortex-m0_common/atomic_arch.c deleted file mode 100644 index bbb1e57f8..000000000 --- a/cpu/cortex-m0_common/atomic_arch.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License v2.1. See the file LICENSE in the top level directory for more - * details. - */ - -/** - * @ingroup cpu_cortexm0_common - * @{ - * - * @file - * @brief Implementation of the kernels atomic interface - * - * @author Stefan Pfeiffer - * @author Hauke Petersen - * - * @} - */ - -#include "arch/atomic_arch.h" -#include "irq.h" - - -unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value) -{ - disableIRQ(); - unsigned int old = *to_set; - *to_set = value; - enableIRQ(); - return old; -} diff --git a/cpu/cortex-m3_common/include/cpu.h b/cpu/cortex-m3_common/include/cpu.h index 16b8728ba..6ace208af 100644 --- a/cpu/cortex-m3_common/include/cpu.h +++ b/cpu/cortex-m3_common/include/cpu.h @@ -20,11 +20,17 @@ * * @author Stefan Pfeiffer * @author Hauke Petersen + * @author Joakim Gebart */ #ifndef __CORTEXM_COMMON_H #define __CORTEXM_COMMON_H +/** + * @brief Cortex-M3 has architecture specific atomic operations in atomic_arch.c. + */ +#define ARCH_HAS_ATOMIC_INT 1 + #include "cpu-conf.h" /** diff --git a/cpu/cortex-m4_common/include/cpu.h b/cpu/cortex-m4_common/include/cpu.h index 031e900c3..6ec219508 100644 --- a/cpu/cortex-m4_common/include/cpu.h +++ b/cpu/cortex-m4_common/include/cpu.h @@ -20,11 +20,17 @@ * * @author Stefan Pfeiffer * @author Hauke Petersen + * @author Joakim Gebart */ #ifndef __CORTEXM_COMMON_H #define __CORTEXM_COMMON_H +/** + * @brief Cortex-M4 has architecture specific atomic operations in atomic_arch.c. + */ +#define ARCH_HAS_ATOMIC_INT 1 + #include "cpu-conf.h" /** diff --git a/cpu/msp430-common/atomic.c b/cpu/msp430-common/atomic.c deleted file mode 100644 index 08469c9eb..000000000 --- a/cpu/msp430-common/atomic.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup cpu - * @{ - * - * @file atomic.c - * @brief atomic set and return function - * - * @author Kaspar Schleiser - * @author Oliver Hahm - * - * @} - */ - -#include "atomic.h" -#include "cpu.h" - -unsigned int atomic_set_return(unsigned int *val, unsigned int set) -{ - dINT(); - unsigned int old_val = *val; - *val = set; - eINT(); - return old_val; -} diff --git a/cpu/x86/include/cpu.h b/cpu/x86/include/cpu.h index b82b14a8f..080bedec2 100644 --- a/cpu/x86/include/cpu.h +++ b/cpu/x86/include/cpu.h @@ -43,6 +43,11 @@ extern "C" { #endif +/** + * @brief x86 has architecture specific atomic operations in x86_atomic.c. + */ +#define ARCH_HAS_ATOMIC_INT 1 + static inline void __attribute__((always_inline)) dINT(void) { asm volatile ("cli");