mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-07-24 15:27:41 +08:00
feat(atomic): drop the dead dmb barrier on single-core targets
A seq_cst px4::atomic emits a hardware `dmb` on ARM. That barrier only orders accesses as seen by a second observer (another CPU core or DMA); for inter-thread synchronisation on a single-core (uniprocessor) target there is no second core, so the `dmb` is dead weight - only the compiler ordering is required. On a NuttX build without CONFIG_SMP, keep full seq_cst semantics but emit a compiler-only fence (__atomic_signal_fence, zero instructions) instead of the `dmb`. This mirrors Linux, where smp_mb()/smp_rmb()/smp_wmb() collapse to a compiler barrier on uniprocessor builds. SMP NuttX and POSIX are unchanged (real barriers, so SITL under ThreadSanitizer keeps full ordering). 64-bit types (not lock-free on a 32-bit core) keep the existing interrupts-off critical section, which already provides both atomicity and ordering. The public API is unchanged - no per-call ordering knob. Verified on arm-none-eabi Cortex-M7 that the single-core path emits no `dmb` while keeping atomicity, and that SMP/POSIX is unchanged: load (single-core): ldr (signal_fence: 0 instr) store (single-core): str fadd (single-core): ldrex/strex loop, no dmb load (seq_cst/SMP): dmb ish; ldr; dmb ish store (seq_cst/SMP): dmb ish; str; dmb ish fadd (seq_cst/SMP): dmb ish; ldrex/strex; dmb ish Shrinks fmu-v6x by ~2.8 KB (dead barriers removed from atomics already in use), with no behaviour change. Note: inter-thread ordering only - DMA/device sync still needs explicit barriers.
This commit is contained in:
@@ -34,17 +34,30 @@
|
||||
/**
|
||||
* @file atomic.h
|
||||
*
|
||||
* Provides atomic integers and counters. Each method is executed atomically and thus
|
||||
* can be used to prevent data races and add memory synchronization between threads.
|
||||
*
|
||||
* In addition to the atomicity, each method serves as a memory barrier (sequential
|
||||
* consistent ordering). This means all operations that happen before and could
|
||||
* potentially have visible side-effects in other threads will happen before
|
||||
* the method is executed.
|
||||
* Provides atomic integers and counters. Each method is executed atomically and
|
||||
* acts as a sequentially-consistent memory barrier (the same contract as before).
|
||||
*
|
||||
* The implementation uses the built-in methods from GCC (supported by Clang as well).
|
||||
* @see https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html.
|
||||
*
|
||||
* ## Single-core lowering
|
||||
*
|
||||
* A seq_cst atomic on ARM emits a hardware `dmb` barrier. That barrier only matters
|
||||
* for ordering as seen by a *second observer* - another CPU core or DMA. On a
|
||||
* single-core (uniprocessor) target there is no second core, so for inter-thread
|
||||
* synchronisation the `dmb` is dead weight; only the *compiler* ordering is actually
|
||||
* required (so the compiler doesn't reorder neighbouring accesses across the atomic).
|
||||
*
|
||||
* So on a NuttX build without CONFIG_SMP we keep full seq_cst semantics but emit a
|
||||
* compiler-only fence (__atomic_signal_fence, zero instructions) instead of the
|
||||
* `dmb`. This mirrors Linux, where smp_mb()/smp_rmb()/smp_wmb() collapse to a plain
|
||||
* compiler barrier on uniprocessor builds. The public contract is unchanged; only
|
||||
* the dead hardware barrier is removed on single-core targets.
|
||||
*
|
||||
* @note This class provides *inter-thread* ordering only. Synchronisation with DMA or
|
||||
* device memory needs explicit barriers (e.g. up_clean_dcache / __DMB) and must not
|
||||
* rely on the ordering here.
|
||||
*
|
||||
* @note: on ARM, the instructions LDREX and STREX might be emitted. To ensure correct
|
||||
* behavior, the exclusive monitor needs to be cleared on a task switch (via CLREX).
|
||||
* This happens automatically e.g. on ARMv7-M as part of an exception entry or exit
|
||||
@@ -59,9 +72,19 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__PX4_NUTTX)
|
||||
# include <nuttx/config.h>
|
||||
# include <nuttx/irq.h>
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
// On a single-core NuttX build the hardware memory barrier (`dmb`) a seq_cst atomic
|
||||
// emits is unnecessary for inter-thread ordering; a compiler-only fence is sufficient
|
||||
// and saves the barrier. SMP NuttX and POSIX keep the real barriers.
|
||||
#if defined(__PX4_NUTTX) && !defined(CONFIG_SMP)
|
||||
# define PX4_ATOMIC_SINGLE_CORE 1
|
||||
#else
|
||||
# define PX4_ATOMIC_SINGLE_CORE 0
|
||||
#endif
|
||||
|
||||
namespace px4
|
||||
{
|
||||
|
||||
@@ -87,16 +110,28 @@ public:
|
||||
#if defined(__PX4_NUTTX)
|
||||
|
||||
if (!__atomic_always_lock_free(sizeof(T), 0)) {
|
||||
// Not lock-free (e.g. 64-bit on a 32-bit core): a critical section
|
||||
// provides both atomicity and full ordering.
|
||||
irqstate_t flags = enter_critical_section();
|
||||
T val = _value;
|
||||
leave_critical_section(flags);
|
||||
return val;
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_load_n(&_value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
// Single core: a relaxed load (a plain, non-tearing ldr) plus a
|
||||
// compiler-only fence gives seq_cst inter-thread ordering without a `dmb`.
|
||||
T val;
|
||||
__atomic_load(&_value, &val, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return val;
|
||||
#else
|
||||
T val;
|
||||
__atomic_load(&_value, &val, __ATOMIC_SEQ_CST);
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,12 +145,17 @@ public:
|
||||
irqstate_t flags = enter_critical_section();
|
||||
_value = value;
|
||||
leave_critical_section(flags);
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
__atomic_store(&_value, &value, __ATOMIC_SEQ_CST);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
__atomic_store(&_value, &value, __ATOMIC_RELAXED);
|
||||
#else
|
||||
__atomic_store(&_value, &value, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,12 +172,18 @@ public:
|
||||
_value += num;
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_fetch_add(&_value, num, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
T ret = __atomic_fetch_add(&_value, num, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return ret;
|
||||
#else
|
||||
return __atomic_fetch_add(&_value, num, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,12 +200,18 @@ public:
|
||||
_value -= num;
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_fetch_sub(&_value, num, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
T ret = __atomic_fetch_sub(&_value, num, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return ret;
|
||||
#else
|
||||
return __atomic_fetch_sub(&_value, num, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,12 +228,18 @@ public:
|
||||
_value &= num;
|
||||
leave_critical_section(flags);
|
||||
return val;
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_fetch_and(&_value, num, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
T ret = __atomic_fetch_and(&_value, num, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return ret;
|
||||
#else
|
||||
return __atomic_fetch_and(&_value, num, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,12 +256,18 @@ public:
|
||||
_value ^= num;
|
||||
leave_critical_section(flags);
|
||||
return val;
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_fetch_xor(&_value, num, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
T ret = __atomic_fetch_xor(&_value, num, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return ret;
|
||||
#else
|
||||
return __atomic_fetch_xor(&_value, num, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,12 +284,18 @@ public:
|
||||
_value |= num;
|
||||
leave_critical_section(flags);
|
||||
return val;
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_fetch_or(&_value, num, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
T ret = __atomic_fetch_or(&_value, num, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return ret;
|
||||
#else
|
||||
return __atomic_fetch_or(&_value, num, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,12 +312,18 @@ public:
|
||||
_value = ~(_value & num);
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_fetch_nand(&_value, num, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
T ret = __atomic_fetch_nand(&_value, num, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return ret;
|
||||
#else
|
||||
return __atomic_fetch_nand(&_value, num, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,15 +351,22 @@ public:
|
||||
leave_critical_section(flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else
|
||||
#endif // __PX4_NUTTX
|
||||
{
|
||||
return __atomic_compare_exchange(&_value, expected, &desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif // __PX4_NUTTX
|
||||
|
||||
#if PX4_ATOMIC_SINGLE_CORE
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
bool ret = __atomic_compare_exchange(&_value, expected, &desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
|
||||
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||
return ret;
|
||||
#else
|
||||
return __atomic_compare_exchange(&_value, expected, &desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T _value {};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user