mirror of
https://github.com/esphome/esphome.git
synced 2026-07-10 17:05:36 +00:00
Merge branch 'millis64-inline-single-thread' into integration
This commit is contained in:
@@ -290,7 +290,7 @@ class Scheduler {
|
||||
// because scheduling only compares millis_64 against millis_64 — never against millis().
|
||||
// On other platforms (ESP8266), extends now to 64-bit using rollover tracking, so both
|
||||
// millis() and scheduling use the same clock.
|
||||
uint64_t millis_64_from_(uint32_t now) {
|
||||
uint64_t ESPHOME_ALWAYS_INLINE millis_64_from_(uint32_t now) {
|
||||
#ifdef USE_NATIVE_64BIT_TIME
|
||||
(void) now;
|
||||
return millis_64();
|
||||
|
||||
+12
-30
@@ -20,6 +20,12 @@ namespace esphome {
|
||||
static const char *const TAG = "time_64";
|
||||
#endif
|
||||
|
||||
#ifdef ESPHOME_THREAD_SINGLE
|
||||
// Storage for Millis64Impl inline compute() — defined here so all TUs share one copy.
|
||||
uint32_t Millis64Impl::last_millis_{0};
|
||||
uint16_t Millis64Impl::millis_major_{0};
|
||||
#else
|
||||
|
||||
uint64_t Millis64Impl::compute(uint32_t now) {
|
||||
// Half the 32-bit range - used to detect rollovers vs normal time progression
|
||||
static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max() / 2;
|
||||
@@ -44,51 +50,25 @@ uint64_t Millis64Impl::compute(uint32_t now) {
|
||||
* to last_millis is provided by its release store and the corresponding acquire loads.
|
||||
*/
|
||||
static std::atomic<uint16_t> millis_major{0};
|
||||
#elif !defined(ESPHOME_THREAD_SINGLE) /* ESPHOME_THREAD_MULTI_NO_ATOMICS */
|
||||
#else /* ESPHOME_THREAD_MULTI_NO_ATOMICS */
|
||||
static Mutex lock;
|
||||
static uint32_t last_millis{0};
|
||||
static uint16_t millis_major{0};
|
||||
#else /* ESPHOME_THREAD_SINGLE */
|
||||
static uint32_t last_millis{0};
|
||||
static uint16_t millis_major{0};
|
||||
#endif
|
||||
|
||||
// THREAD SAFETY NOTE:
|
||||
// This function has three implementations, based on the precompiler flags
|
||||
// - ESPHOME_THREAD_SINGLE - Runs on single-threaded platforms (ESP8266, etc.)
|
||||
// This function has two out-of-line implementations, based on the precompiler flags:
|
||||
// - ESPHOME_THREAD_MULTI_NO_ATOMICS - Runs on multi-threaded platforms without atomics (LibreTiny BK72xx)
|
||||
// - ESPHOME_THREAD_MULTI_ATOMICS - Runs on multi-threaded platforms with atomics (LibreTiny RTL87xx/LN882x, etc.)
|
||||
//
|
||||
// The ESPHOME_THREAD_SINGLE path is inlined in time_64.h.
|
||||
// Make sure all changes are synchronized if you edit this function.
|
||||
//
|
||||
// IMPORTANT: Always pass fresh millis() values to this function. The implementation
|
||||
// handles out-of-order timestamps between threads, but minimizing time differences
|
||||
// helps maintain accuracy.
|
||||
|
||||
#ifdef ESPHOME_THREAD_SINGLE
|
||||
// Single-core platforms have no concurrency, so this is a simple implementation
|
||||
// that just tracks 32-bit rollover (every 49.7 days) without any locking or atomics.
|
||||
|
||||
uint16_t major = millis_major;
|
||||
uint32_t last = last_millis;
|
||||
|
||||
// Check for rollover
|
||||
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
||||
millis_major++;
|
||||
major++;
|
||||
last_millis = now;
|
||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||
ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
|
||||
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
||||
} else if (now > last) {
|
||||
// Only update if time moved forward
|
||||
last_millis = now;
|
||||
}
|
||||
|
||||
// Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
|
||||
return now + (static_cast<uint64_t>(major) << 32);
|
||||
|
||||
#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
|
||||
#if defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
|
||||
// Without atomics, this implementation uses locks more aggressively:
|
||||
// 1. Always locks when near the rollover boundary (within 10 seconds)
|
||||
// 2. Always locks when detecting a large backwards jump
|
||||
@@ -202,6 +182,8 @@ uint64_t Millis64Impl::compute(uint32_t now) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // !ESPHOME_THREAD_SINGLE
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // !USE_NATIVE_64BIT_TIME
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#ifndef USE_NATIVE_64BIT_TIME
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@@ -16,7 +19,36 @@ class Millis64Impl {
|
||||
friend uint64_t millis_64();
|
||||
friend class Scheduler;
|
||||
|
||||
#ifdef ESPHOME_THREAD_SINGLE
|
||||
// Storage defined in time_64.cpp — declared here so the inline body can access them.
|
||||
static uint32_t last_millis_;
|
||||
static uint16_t millis_major_;
|
||||
|
||||
static inline uint64_t ESPHOME_ALWAYS_INLINE compute(uint32_t now) {
|
||||
// Half the 32-bit range - used to detect rollovers vs normal time progression
|
||||
static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max() / 2;
|
||||
|
||||
// Single-core platforms have no concurrency, so this is a simple implementation
|
||||
// that just tracks 32-bit rollover (every 49.7 days) without any locking or atomics.
|
||||
uint16_t major = millis_major_;
|
||||
uint32_t last = last_millis_;
|
||||
|
||||
// Check for rollover
|
||||
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
||||
millis_major_++;
|
||||
major++;
|
||||
last_millis_ = now;
|
||||
} else if (now > last) {
|
||||
// Only update if time moved forward
|
||||
last_millis_ = now;
|
||||
}
|
||||
|
||||
// Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
|
||||
return now + (static_cast<uint64_t>(major) << 32);
|
||||
}
|
||||
#else
|
||||
static uint64_t compute(uint32_t now);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
Reference in New Issue
Block a user