1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-02 11:22:24 +01:00

core/scheduler: Make millis_64_ rollover monotonic on SMP (#9716)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
@RubenKelevra
2025-07-20 23:57:52 +02:00
committed by GitHub
parent 7d30d1e987
commit 6e31fb181e
9 changed files with 175 additions and 87 deletions

View File

@@ -1,10 +1,11 @@
#pragma once
#include "esphome/core/defines.h"
#include <vector>
#include <memory>
#include <cstring>
#include <deque>
#if !defined(USE_ESP8266) && !defined(USE_RP2040) && !defined(USE_LIBRETINY)
#ifdef ESPHOME_CORES_MULTI_ATOMICS
#include <atomic>
#endif
@@ -204,23 +205,40 @@ class Scheduler {
Mutex lock_;
std::vector<std::unique_ptr<SchedulerItem>> items_;
std::vector<std::unique_ptr<SchedulerItem>> to_add_;
#if !defined(USE_ESP8266) && !defined(USE_RP2040)
// ESP8266 and RP2040 don't need the defer queue because:
// ESP8266: Single-core with no preemptive multitasking
// RP2040: Currently has empty mutex implementation in ESPHome
// Both platforms save 40 bytes of RAM by excluding this
#ifndef ESPHOME_CORES_SINGLE
// Single-core platforms don't need the defer queue and save 40 bytes of RAM
std::deque<std::unique_ptr<SchedulerItem>> defer_queue_; // FIFO queue for defer() calls
#endif
#if !defined(USE_ESP8266) && !defined(USE_RP2040) && !defined(USE_LIBRETINY)
// Multi-threaded platforms with atomic support: last_millis_ needs atomic for lock-free updates
#endif /* ESPHOME_CORES_SINGLE */
uint32_t to_remove_{0};
#ifdef ESPHOME_CORES_MULTI_ATOMICS
/*
* Multi-threaded platforms with atomic support: last_millis_ needs atomic for lock-free updates
*
* MEMORY-ORDERING NOTE
* --------------------
* `last_millis_` and `millis_major_` form a single 64-bit timestamp split in half.
* Writers publish `last_millis_` with memory_order_release and readers use
* memory_order_acquire. This ensures that once a reader sees the new low word,
* it also observes the corresponding increment of `millis_major_`.
*/
std::atomic<uint32_t> last_millis_{0};
#else
#else /* not ESPHOME_CORES_MULTI_ATOMICS */
// Platforms without atomic support or single-threaded platforms
uint32_t last_millis_{0};
#endif
// millis_major_ is protected by lock when incrementing
#endif /* else ESPHOME_CORES_MULTI_ATOMICS */
/*
* Upper 16 bits of the 64-bit millis counter. Incremented only while holding
* `lock_`; read concurrently. Atomic (relaxed) avoids a formal data race.
* Ordering relative to `last_millis_` is provided by its release store and the
* corresponding acquire loads.
*/
#ifdef ESPHOME_CORES_MULTI_ATOMICS
std::atomic<uint16_t> millis_major_{0};
#else /* not ESPHOME_CORES_MULTI_ATOMICS */
uint16_t millis_major_{0};
uint32_t to_remove_{0};
#endif /* else ESPHOME_CORES_MULTI_ATOMICS */
};
} // namespace esphome