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
dev/timer
Joakim Gebart 8 years ago
parent 988ae54e4f
commit 215ccc1213

@ -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 <kaspar@schleiser.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Joakim Gebart <joakim.gebart@eistec.se>
*
* @}
*/
#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

@ -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 <kaspar@schleiser.de>
* @author Joakim Gebart <joakim.gebart@eistec.se>
*/
#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_ */
/** @} */

@ -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 <stefan.pfeiffer@fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Hinnerk van Bruinehsen <h.v.bruinehsen@fu-berlin.de>
*
* @}
*/
#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;
}

@ -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 <stefan.pfeiffer@fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#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;
}

@ -20,11 +20,17 @@
*
* @author Stefan Pfeiffer <stefan.pfeiffer@fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Joakim Gebart <joakim.gebart@eistec.se>
*/
#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"
/**

@ -20,11 +20,17 @@
*
* @author Stefan Pfeiffer <stefan.pfeiffer@fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Joakim Gebart <joakim.gebart@eistec.se>
*/
#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"
/**

@ -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 <kaspar@schleiser.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
*
* @}
*/
#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;
}

@ -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");

Loading…
Cancel
Save