Merge pull request #2321 from gebart/pr/atomic-counters
core: Atomic counters
This commit is contained in:
commit
021226fcbe
59
core/atomic.c
Normal file
59
core/atomic.c
Normal file
@ -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_ */
|
||||
/** @} */
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#ifndef _ARM_CPU_H
|
||||
#define _ARM_CPU_H
|
||||
#ifndef ARM_CPU_H_
|
||||
#define ARM_CPU_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "VIC.h"
|
||||
@ -17,6 +17,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ARM has architecture specific atomic_set_return in atomic.s
|
||||
*/
|
||||
#define ARCH_HAS_ATOMIC_SET_RETURN 1
|
||||
|
||||
#define NEW_TASK_CPSR 0x1F
|
||||
#define WORDSIZE 32
|
||||
|
||||
@ -44,4 +49,4 @@ int uart0_puts(char *astring, int length);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ARM_CPU_H
|
||||
#endif /* ARM_CPU_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;
|
||||
}
|
||||
@ -7,26 +7,40 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_cortex-m3
|
||||
* @ingroup cpu_cortexm3_common
|
||||
* @{
|
||||
*
|
||||
* @file atomic_arch.c
|
||||
* @file
|
||||
* @brief Implementation of the kernels atomic interface
|
||||
*
|
||||
* @author Stefan Pfeiffer <stefan.pfeiffer@fu-berlin.de>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Joakim Gebart <joakim.gebart@eistec.se>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "arch/atomic_arch.h"
|
||||
#include <stdint.h>
|
||||
#include "atomic.h"
|
||||
#include "irq.h"
|
||||
#include "cpu.h"
|
||||
|
||||
unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value)
|
||||
int atomic_cas(atomic_int_t *var, int old, int now)
|
||||
{
|
||||
disableIRQ();
|
||||
unsigned int old = *to_set;
|
||||
*to_set = value;
|
||||
enableIRQ();
|
||||
return old;
|
||||
int tmp;
|
||||
int status;
|
||||
|
||||
/* Load exclusive */
|
||||
tmp = __LDREXW((volatile uint32_t *)(&ATOMIC_VALUE(*var)));
|
||||
|
||||
if (tmp != old) {
|
||||
/* Clear memory exclusivity */
|
||||
__CLREX();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to write the new value */
|
||||
status = __STREXW(now, (volatile uint32_t *)(&ATOMIC_VALUE(*var)));
|
||||
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
/**
|
||||
* @defgroup cpu_cortex-m3 Cortex-M3 common
|
||||
* @brief ARM Cortex-M specific code
|
||||
* @ingroup cpu
|
||||
*/
|
||||
@ -7,7 +7,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup cpu_cortex-m3
|
||||
* @defgroup cpu_cortexm3_common ARM Cortex-M3 common
|
||||
* @ingroup cpu
|
||||
* @brief Common implementations and headers for Cortex-M3 family based micro-controllers
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
@ -18,10 +20,16 @@
|
||||
*
|
||||
* @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
|
||||
#ifndef CORTEXM_COMMON_H_
|
||||
#define CORTEXM_COMMON_H_
|
||||
|
||||
/**
|
||||
* @brief Cortex-M3 has architecture specific atomic operations in atomic_arch.c.
|
||||
*/
|
||||
#define ARCH_HAS_ATOMIC_COMPARE_AND_SWAP 1
|
||||
|
||||
#include "cpu-conf.h"
|
||||
|
||||
@ -47,5 +55,5 @@ void cpu_init(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CORTEXM_COMMON_H */
|
||||
#endif /* CORTEXM_COMMON_H_ */
|
||||
/** @} */
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_cortex-m3
|
||||
* @ingroup cpu_cortexm3_common
|
||||
* @{
|
||||
*
|
||||
* @file irq_arch.c
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_cortex-m3
|
||||
* @ingroup cpu_cortexm3_common
|
||||
* @{
|
||||
*
|
||||
* @file thread_arch.c
|
||||
|
||||
@ -15,18 +15,32 @@
|
||||
*
|
||||
* @author Stefan Pfeiffer <stefan.pfeiffer@fu-berlin.de>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Joakim Gebart <joakim.gebart@eistec.se>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "arch/atomic_arch.h"
|
||||
#include <stdint.h>
|
||||
#include "atomic.h"
|
||||
#include "irq.h"
|
||||
#include "cpu.h"
|
||||
|
||||
unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value)
|
||||
int atomic_cas(atomic_int_t *var, int old, int now)
|
||||
{
|
||||
disableIRQ();
|
||||
unsigned int old = *to_set;
|
||||
*to_set = value;
|
||||
enableIRQ();
|
||||
return old;
|
||||
int tmp;
|
||||
int status;
|
||||
|
||||
/* Load exclusive */
|
||||
tmp = __LDREXW((volatile uint32_t *)(&ATOMIC_VALUE(*var)));
|
||||
|
||||
if (tmp != old) {
|
||||
/* Clear memory exclusivity */
|
||||
__CLREX();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to write the new value */
|
||||
status = __STREXW(now, (volatile uint32_t *)(&ATOMIC_VALUE(*var)));
|
||||
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
@ -20,10 +20,16 @@
|
||||
*
|
||||
* @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
|
||||
#ifndef CORTEXM_COMMON_H_
|
||||
#define CORTEXM_COMMON_H_
|
||||
|
||||
/**
|
||||
* @brief Cortex-M4 has architecture specific atomic operations in atomic_arch.c.
|
||||
*/
|
||||
#define ARCH_HAS_ATOMIC_COMPARE_AND_SWAP 1
|
||||
|
||||
#include "cpu-conf.h"
|
||||
|
||||
@ -49,5 +55,5 @@ void cpu_init(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CORTEXM_COMMON_H */
|
||||
#endif /* CORTEXM_COMMON_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;
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Native CPU atomic.h implementation
|
||||
*
|
||||
* Copyright (C) 2013 Ludwig Ortmann <ludwig.ortmann@fu-berlin.de>
|
||||
*
|
||||
* 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 arch
|
||||
* @{
|
||||
* @file
|
||||
* @author Ludwig Ortmann <ludwig.ortmann@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "atomic.h"
|
||||
#include "irq.h"
|
||||
#include "debug.h"
|
||||
|
||||
unsigned int atomic_set_return(unsigned int *val, unsigned int set)
|
||||
{
|
||||
unsigned int old_val;
|
||||
unsigned int old_state;
|
||||
|
||||
DEBUG("atomic_set_return\n");
|
||||
|
||||
old_state = disableIRQ();
|
||||
|
||||
old_val = *val;
|
||||
*val = set;
|
||||
|
||||
restoreIRQ(old_state);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
@ -26,8 +26,8 @@
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__CPU__H__
|
||||
#define CPU__X86__CPU__H__
|
||||
#ifndef CPU_X86_CPU_H_
|
||||
#define CPU_X86_CPU_H_
|
||||
|
||||
#include "attributes.h"
|
||||
#include "irq.h"
|
||||
@ -43,6 +43,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief x86 has architecture specific atomic operations in x86_atomic.c.
|
||||
*/
|
||||
#define ARCH_HAS_ATOMIC_SET_RETURN 1
|
||||
#define ARCH_HAS_ATOMIC_COMPARE_AND_SWAP 1
|
||||
|
||||
static inline void __attribute__((always_inline)) dINT(void)
|
||||
{
|
||||
asm volatile ("cli");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
* Copyright (C) 2014-2015 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -28,6 +28,7 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "atomic.h"
|
||||
|
||||
unsigned int atomic_set_return(unsigned int *val, unsigned int set)
|
||||
@ -35,3 +36,14 @@ unsigned int atomic_set_return(unsigned int *val, unsigned int set)
|
||||
asm volatile ("lock xchg %0, %1" : "+m"(*val), "+r"(set));
|
||||
return set;
|
||||
}
|
||||
|
||||
int atomic_cas(atomic_int_t *dest, int known_value, int new_value)
|
||||
{
|
||||
uint8_t successful;
|
||||
asm volatile ("lock cmpxchgl %2, %0\n"
|
||||
"seteb %1"
|
||||
: "+m"(ATOMIC_VALUE(*dest)), "=g"(successful)
|
||||
: "r"(new_value), "a"(known_value)
|
||||
: "flags");
|
||||
return successful;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* 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
|
||||
@ -63,6 +64,196 @@ static void test_atomic_set_return_null_random(void)
|
||||
TEST_ASSERT_EQUAL_INT(r, res);
|
||||
}
|
||||
|
||||
/* Test atomic_set_to_one on a variable set to 0 */
|
||||
static void test_atomic_set_to_one_zero(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_set_to_one(&res));
|
||||
TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_set_to_one on a variable set to 1 */
|
||||
static void test_atomic_set_to_one_one(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(1);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_set_to_one(&res));
|
||||
TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_set_to_one twice */
|
||||
static void test_atomic_set_to_one_twice(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_set_to_one(&res));
|
||||
TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_set_to_one(&res));
|
||||
TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_set_to_zero on a variable set to 0 */
|
||||
static void test_atomic_set_to_zero_zero(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_set_to_zero(&res));
|
||||
TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_set_to_zero on a variable set to 1 */
|
||||
static void test_atomic_set_to_zero_one(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(1);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_set_to_zero(&res));
|
||||
TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_set_to_zero twice */
|
||||
static void test_atomic_set_to_zero_twice(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(1);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_set_to_zero(&res));
|
||||
TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_set_to_zero(&res));
|
||||
TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_inc */
|
||||
static void test_atomic_inc_positive(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(2, ATOMIC_VALUE(res));
|
||||
ATOMIC_VALUE(res) = 0;
|
||||
for (int i = 0; i < 512; ++i) {
|
||||
TEST_ASSERT_EQUAL_INT(i, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(i + 1, ATOMIC_VALUE(res));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_atomic_inc_negative(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(-99);
|
||||
|
||||
for (int i = -99; i < 123; ++i) {
|
||||
TEST_ASSERT_EQUAL_INT(i, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(i + 1, ATOMIC_VALUE(res));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_atomic_inc_rollover(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(INT_MAX - 30);
|
||||
|
||||
for (int i = 0; i < 30; ++i) {
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX - 30 + i, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX - 30 + i + 1, ATOMIC_VALUE(res));
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN + 1, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN + 1, atomic_inc(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN + 2, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_dec */
|
||||
static void test_atomic_dec_negative(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(-1, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(-1, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(-2, ATOMIC_VALUE(res));
|
||||
ATOMIC_VALUE(res) = 0;
|
||||
for (int i = 0; i < 512; ++i) {
|
||||
TEST_ASSERT_EQUAL_INT(-i, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(-i - 1, ATOMIC_VALUE(res));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_atomic_dec_positive(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(99);
|
||||
|
||||
for (int i = 99; i < -123; --i) {
|
||||
TEST_ASSERT_EQUAL_INT(i, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(i - 1, ATOMIC_VALUE(res));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_atomic_dec_rollover(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(INT_MIN + 30);
|
||||
|
||||
for (int i = 0; i < 30; ++i) {
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN + 30 - i, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN + 30 - i - 1, ATOMIC_VALUE(res));
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(INT_MIN, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX - 1, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX - 1, atomic_dec(&res));
|
||||
TEST_ASSERT_EQUAL_INT(INT_MAX - 2, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_cas with a correct old value */
|
||||
static void test_atomic_cas_same(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, 0, 1234567));
|
||||
TEST_ASSERT_EQUAL_INT(1234567, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, 1234567, -987654321));
|
||||
TEST_ASSERT_EQUAL_INT(-987654321, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, -987654321, -987654321));
|
||||
TEST_ASSERT_EQUAL_INT(-987654321, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, -987654321, 0));
|
||||
TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test atomic_cas with a non-matching old value */
|
||||
static void test_atomic_cas_diff(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(32767);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 65535, 12345));
|
||||
TEST_ASSERT_EQUAL_INT(32767, ATOMIC_VALUE(res));
|
||||
ATOMIC_VALUE(res) = -12345687;
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 12345687, 123456789));
|
||||
TEST_ASSERT_EQUAL_INT(-12345687, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 12345687, 12345687));
|
||||
TEST_ASSERT_EQUAL_INT(-12345687, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 12345687, -12345687));
|
||||
TEST_ASSERT_EQUAL_INT(-12345687, ATOMIC_VALUE(res));
|
||||
}
|
||||
|
||||
/* Test ATOMIC_VALUE */
|
||||
static void test_atomic_value(void)
|
||||
{
|
||||
atomic_int_t res = ATOMIC_INIT(12345);
|
||||
atomic_int_t *ptr = &res;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(12345, ATOMIC_VALUE(res));
|
||||
ATOMIC_VALUE(res) = 54332;
|
||||
TEST_ASSERT_EQUAL_INT(54332, ATOMIC_VALUE(res));
|
||||
TEST_ASSERT_EQUAL_INT(54332, res.value);
|
||||
res.value = 1232342131;
|
||||
TEST_ASSERT_EQUAL_INT(ATOMIC_VALUE(res), res.value);
|
||||
TEST_ASSERT_EQUAL_INT(ATOMIC_VALUE(*ptr), res.value);
|
||||
}
|
||||
|
||||
/* ATOMIC_INIT is implicitly tested by the other tests */
|
||||
|
||||
Test *tests_core_atomic_tests(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
@ -72,6 +263,21 @@ Test *tests_core_atomic_tests(void)
|
||||
new_TestFixture(test_atomic_set_return_limit_null),
|
||||
new_TestFixture(test_atomic_set_return_null_limit),
|
||||
new_TestFixture(test_atomic_set_return_null_random),
|
||||
new_TestFixture(test_atomic_set_to_one_one),
|
||||
new_TestFixture(test_atomic_set_to_one_zero),
|
||||
new_TestFixture(test_atomic_set_to_one_twice),
|
||||
new_TestFixture(test_atomic_set_to_zero_one),
|
||||
new_TestFixture(test_atomic_set_to_zero_zero),
|
||||
new_TestFixture(test_atomic_set_to_zero_twice),
|
||||
new_TestFixture(test_atomic_inc_positive),
|
||||
new_TestFixture(test_atomic_inc_negative),
|
||||
new_TestFixture(test_atomic_inc_rollover),
|
||||
new_TestFixture(test_atomic_dec_positive),
|
||||
new_TestFixture(test_atomic_dec_negative),
|
||||
new_TestFixture(test_atomic_dec_rollover),
|
||||
new_TestFixture(test_atomic_cas_same),
|
||||
new_TestFixture(test_atomic_cas_diff),
|
||||
new_TestFixture(test_atomic_value),
|
||||
};
|
||||
|
||||
EMB_UNIT_TESTCALLER(core_atomic_tests, NULL, NULL,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user