[host] Use native clock_gettime for millis_64()

The host platform already uses clock_gettime(CLOCK_MONOTONIC) for
millis(). Use it directly for millis_64() instead of delegating to
the Scheduler's rollover tracking.

This eliminates the MULTI_ATOMICS rollover code path (CAS/atomic)
on host builds and removes the dependency on App.scheduler for
millis_64() to work.
This commit is contained in:
J. Nick Koston
2026-02-26 23:36:22 -10:00
parent 0e05ae56e5
commit c7182404a3
3 changed files with 17 additions and 15 deletions

View File

@@ -1,6 +1,5 @@
#ifdef USE_HOST
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "preferences.h"
@@ -20,7 +19,11 @@ uint32_t IRAM_ATTR HOT millis() {
uint32_t ms = round(spec.tv_nsec / 1e6);
return ((uint32_t) seconds) * 1000U + ms;
}
uint64_t millis_64() { return App.scheduler.millis_64_impl_(millis()); }
uint64_t millis_64() {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return static_cast<uint64_t>(spec.tv_sec) * 1000ULL + static_cast<uint64_t>(spec.tv_nsec) / 1000000ULL;
}
void HOT delay(uint32_t ms) {
struct timespec ts;
ts.tv_sec = ms / 1000;

View File

@@ -28,7 +28,7 @@ static constexpr size_t MAX_POOL_SIZE = 5;
// Set to 5 to match the pool size - when we have as many cancelled items as our
// pool can hold, it's time to clean up and recycle them.
static constexpr uint32_t MAX_LOGICALLY_DELETED_ITEMS = 5;
#ifndef USE_ESP32
#if !defined(USE_ESP32) && !defined(USE_HOST)
// 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;
#endif
@@ -475,12 +475,12 @@ void HOT Scheduler::call(uint32_t now) {
if (now_64 - last_print > 2000) {
last_print = now_64;
std::vector<SchedulerItemPtr> old_items;
#if !defined(USE_ESP32) && defined(ESPHOME_THREAD_MULTI_ATOMICS)
#if !defined(USE_ESP32) && !defined(USE_HOST) && defined(ESPHOME_THREAD_MULTI_ATOMICS)
const auto last_dbg = this->last_millis_.load(std::memory_order_relaxed);
const auto major_dbg = this->millis_major_.load(std::memory_order_relaxed);
ESP_LOGD(TAG, "Items: count=%zu, pool=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(),
this->scheduler_item_pool_.size(), now_64, major_dbg, last_dbg);
#elif !defined(USE_ESP32)
#elif !defined(USE_ESP32) && !defined(USE_HOST)
ESP_LOGD(TAG, "Items: count=%zu, pool=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(),
this->scheduler_item_pool_.size(), now_64, this->millis_major_, this->last_millis_);
#else
@@ -714,7 +714,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, NameType name_type
return total_cancelled > 0;
}
#ifndef USE_ESP32
#if !defined(USE_ESP32) && !defined(USE_HOST)
uint64_t Scheduler::millis_64_impl_(uint32_t now) {
// THREAD SAFETY NOTE:
// This function has three implementations, based on the precompiler flags
@@ -872,7 +872,7 @@ uint64_t Scheduler::millis_64_impl_(uint32_t now) {
"No platform threading model defined. One of ESPHOME_THREAD_SINGLE, ESPHOME_THREAD_MULTI_NO_ATOMICS, or ESPHOME_THREAD_MULTI_ATOMICS must be defined."
#endif
}
#endif // not USE_ESP32
#endif // !USE_ESP32 && !USE_HOST
bool HOT Scheduler::SchedulerItem::cmp(const SchedulerItemPtr &a, const SchedulerItemPtr &b) {
// High bits are almost always equal (change only on 32-bit rollover ~49 days)

View File

@@ -287,7 +287,7 @@ class Scheduler {
// On ESP32, ignores now and uses esp_timer_get_time() directly (native 64-bit).
// On non-ESP32, extends now to 64-bit using rollover tracking.
uint64_t millis_64_from_(uint32_t now) {
#ifdef USE_ESP32
#if defined(USE_ESP32) || defined(USE_HOST)
(void) now;
return millis_64();
#else
@@ -295,10 +295,9 @@ class Scheduler {
#endif
}
#ifndef USE_ESP32
// On non-ESP32 platforms, millis_64() HAL function delegates to this method
// which tracks 32-bit millis() rollover using millis_major_ and last_millis_.
// On ESP32, millis_64() uses esp_timer_get_time() directly.
#if !defined(USE_ESP32) && !defined(USE_HOST)
// On platforms without native 64-bit time, millis_64() HAL function delegates to this
// method which tracks 32-bit millis() rollover using millis_major_ and last_millis_.
friend uint64_t millis_64();
uint64_t millis_64_impl_(uint32_t now);
#endif
@@ -568,8 +567,8 @@ class Scheduler {
// to synchronize between tasks (see https://github.com/esphome/backlog/issues/52)
std::vector<SchedulerItemPtr> scheduler_item_pool_;
#ifndef USE_ESP32
// On ESP32, millis_64() uses esp_timer_get_time() directly; no rollover tracking needed.
#if !defined(USE_ESP32) && !defined(USE_HOST)
// On platforms with native 64-bit time (ESP32, Host), no rollover tracking needed.
// On other platforms, these fields track 32-bit millis() rollover for millis_64_impl_().
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
/*
@@ -599,7 +598,7 @@ class Scheduler {
#else /* not ESPHOME_THREAD_MULTI_ATOMICS */
uint16_t millis_major_{0};
#endif /* else ESPHOME_THREAD_MULTI_ATOMICS */
#endif /* not USE_ESP32 */
#endif /* !USE_ESP32 && !USE_HOST */
};
} // namespace esphome