mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Merge branch 'empty_hidden_side_effects' into integration
This commit is contained in:
		| @@ -3,6 +3,7 @@ import esphome.codegen as cg | |||||||
| from esphome.components import display, spi | from esphome.components import display, spi | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|  |     CONF_FLIP_X, | ||||||
|     CONF_ID, |     CONF_ID, | ||||||
|     CONF_INTENSITY, |     CONF_INTENSITY, | ||||||
|     CONF_LAMBDA, |     CONF_LAMBDA, | ||||||
| @@ -14,7 +15,6 @@ CODEOWNERS = ["@rspaargaren"] | |||||||
| DEPENDENCIES = ["spi"] | DEPENDENCIES = ["spi"] | ||||||
|  |  | ||||||
| CONF_ROTATE_CHIP = "rotate_chip" | CONF_ROTATE_CHIP = "rotate_chip" | ||||||
| CONF_FLIP_X = "flip_x" |  | ||||||
| CONF_SCROLL_SPEED = "scroll_speed" | CONF_SCROLL_SPEED = "scroll_speed" | ||||||
| CONF_SCROLL_DWELL = "scroll_dwell" | CONF_SCROLL_DWELL = "scroll_dwell" | ||||||
| CONF_SCROLL_DELAY = "scroll_delay" | CONF_SCROLL_DELAY = "scroll_delay" | ||||||
|   | |||||||
| @@ -6,6 +6,8 @@ from esphome.const import ( | |||||||
|     CONF_BRIGHTNESS, |     CONF_BRIGHTNESS, | ||||||
|     CONF_CONTRAST, |     CONF_CONTRAST, | ||||||
|     CONF_EXTERNAL_VCC, |     CONF_EXTERNAL_VCC, | ||||||
|  |     CONF_FLIP_X, | ||||||
|  |     CONF_FLIP_Y, | ||||||
|     CONF_INVERT, |     CONF_INVERT, | ||||||
|     CONF_LAMBDA, |     CONF_LAMBDA, | ||||||
|     CONF_MODEL, |     CONF_MODEL, | ||||||
| @@ -18,9 +20,6 @@ ssd1306_base_ns = cg.esphome_ns.namespace("ssd1306_base") | |||||||
| SSD1306 = ssd1306_base_ns.class_("SSD1306", cg.PollingComponent, display.DisplayBuffer) | SSD1306 = ssd1306_base_ns.class_("SSD1306", cg.PollingComponent, display.DisplayBuffer) | ||||||
| SSD1306Model = ssd1306_base_ns.enum("SSD1306Model") | SSD1306Model = ssd1306_base_ns.enum("SSD1306Model") | ||||||
|  |  | ||||||
| CONF_FLIP_X = "flip_x" |  | ||||||
| CONF_FLIP_Y = "flip_y" |  | ||||||
|  |  | ||||||
| MODELS = { | MODELS = { | ||||||
|     "SSD1306_128X32": SSD1306Model.SSD1306_MODEL_128_32, |     "SSD1306_128X32": SSD1306Model.SSD1306_MODEL_128_32, | ||||||
|     "SSD1306_128X64": SSD1306Model.SSD1306_MODEL_128_64, |     "SSD1306_128X64": SSD1306Model.SSD1306_MODEL_128_64, | ||||||
|   | |||||||
| @@ -383,6 +383,8 @@ CONF_FINGER_ID = "finger_id" | |||||||
| CONF_FINGERPRINT_COUNT = "fingerprint_count" | CONF_FINGERPRINT_COUNT = "fingerprint_count" | ||||||
| CONF_FLASH_LENGTH = "flash_length" | CONF_FLASH_LENGTH = "flash_length" | ||||||
| CONF_FLASH_TRANSITION_LENGTH = "flash_transition_length" | CONF_FLASH_TRANSITION_LENGTH = "flash_transition_length" | ||||||
|  | CONF_FLIP_X = "flip_x" | ||||||
|  | CONF_FLIP_Y = "flip_y" | ||||||
| CONF_FLOW = "flow" | CONF_FLOW = "flow" | ||||||
| CONF_FLOW_CONTROL_PIN = "flow_control_pin" | CONF_FLOW_CONTROL_PIN = "flow_control_pin" | ||||||
| CONF_FONT = "font" | CONF_FONT = "font" | ||||||
|   | |||||||
| @@ -219,10 +219,13 @@ bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) | |||||||
|  |  | ||||||
| optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) { | optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) { | ||||||
|   // IMPORTANT: This method should only be called from the main thread (loop task). |   // IMPORTANT: This method should only be called from the main thread (loop task). | ||||||
|   // It calls empty_() and accesses items_[0] without holding a lock, which is only |   // It performs cleanup and accesses items_[0] without holding a lock, which is only | ||||||
|   // safe when called from the main thread. Other threads must not call this method. |   // safe when called from the main thread. Other threads must not call this method. | ||||||
|   if (this->empty_()) |  | ||||||
|  |   // If no items, return empty optional | ||||||
|  |   if (this->cleanup_() == 0) | ||||||
|     return {}; |     return {}; | ||||||
|  |  | ||||||
|   auto &item = this->items_[0]; |   auto &item = this->items_[0]; | ||||||
|   // Convert the fresh timestamp from caller (usually Application::loop()) to 64-bit |   // Convert the fresh timestamp from caller (usually Application::loop()) to 64-bit | ||||||
|   const auto now_64 = this->millis_64_(now);  // 'now' from parameter - fresh from caller |   const auto now_64 = this->millis_64_(now);  // 'now' from parameter - fresh from caller | ||||||
| @@ -282,7 +285,9 @@ void HOT Scheduler::call(uint32_t now) { | |||||||
|     ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64, |     ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64, | ||||||
|              this->millis_major_, this->last_millis_); |              this->millis_major_, this->last_millis_); | ||||||
| #endif /* else ESPHOME_CORES_MULTI_ATOMICS */ | #endif /* else ESPHOME_CORES_MULTI_ATOMICS */ | ||||||
|     while (!this->empty_()) { |     // Cleanup before debug output | ||||||
|  |     this->cleanup_(); | ||||||
|  |     while (!this->items_.empty()) { | ||||||
|       std::unique_ptr<SchedulerItem> item; |       std::unique_ptr<SchedulerItem> item; | ||||||
|       { |       { | ||||||
|         LockGuard guard{this->lock_}; |         LockGuard guard{this->lock_}; | ||||||
| @@ -333,7 +338,9 @@ void HOT Scheduler::call(uint32_t now) { | |||||||
|     this->to_remove_ = 0; |     this->to_remove_ = 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   while (!this->empty_()) { |   // Cleanup removed items before processing | ||||||
|  |   this->cleanup_(); | ||||||
|  |   while (!this->items_.empty()) { | ||||||
|     // use scoping to indicate visibility of `item` variable |     // use scoping to indicate visibility of `item` variable | ||||||
|     { |     { | ||||||
|       // Don't copy-by value yet |       // Don't copy-by value yet | ||||||
| @@ -399,8 +406,8 @@ void HOT Scheduler::process_to_add() { | |||||||
|   } |   } | ||||||
|   this->to_add_.clear(); |   this->to_add_.clear(); | ||||||
| } | } | ||||||
| void HOT Scheduler::cleanup_() { | size_t HOT Scheduler::cleanup_() { | ||||||
|   // Fast path: if nothing to remove, just return |   // Fast path: if nothing to remove, just return the current size | ||||||
|   // Reading to_remove_ without lock is safe because: |   // Reading to_remove_ without lock is safe because: | ||||||
|   // 1. We only call this from the main thread during call() |   // 1. We only call this from the main thread during call() | ||||||
|   // 2. If it's 0, there's definitely nothing to cleanup |   // 2. If it's 0, there's definitely nothing to cleanup | ||||||
| @@ -408,7 +415,7 @@ void HOT Scheduler::cleanup_() { | |||||||
|   // 4. Not all platforms support atomics, so we accept this race in favor of performance |   // 4. Not all platforms support atomics, so we accept this race in favor of performance | ||||||
|   // 5. The worst case is a one-loop-iteration delay in cleanup, which is harmless |   // 5. The worst case is a one-loop-iteration delay in cleanup, which is harmless | ||||||
|   if (this->to_remove_ == 0) |   if (this->to_remove_ == 0) | ||||||
|     return; |     return this->items_.size(); | ||||||
|  |  | ||||||
|   // We must hold the lock for the entire cleanup operation because: |   // We must hold the lock for the entire cleanup operation because: | ||||||
|   // 1. We're modifying items_ (via pop_raw_) which requires exclusive access |   // 1. We're modifying items_ (via pop_raw_) which requires exclusive access | ||||||
| @@ -422,10 +429,11 @@ void HOT Scheduler::cleanup_() { | |||||||
|   while (!this->items_.empty()) { |   while (!this->items_.empty()) { | ||||||
|     auto &item = this->items_[0]; |     auto &item = this->items_[0]; | ||||||
|     if (!item->remove) |     if (!item->remove) | ||||||
|       return; |       break; | ||||||
|     this->to_remove_--; |     this->to_remove_--; | ||||||
|     this->pop_raw_(); |     this->pop_raw_(); | ||||||
|   } |   } | ||||||
|  |   return this->items_.size(); | ||||||
| } | } | ||||||
| void HOT Scheduler::pop_raw_() { | void HOT Scheduler::pop_raw_() { | ||||||
|   std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp); |   std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp); | ||||||
|   | |||||||
| @@ -58,6 +58,9 @@ class Scheduler { | |||||||
|  |  | ||||||
|   // Calculate when the next scheduled item should run |   // Calculate when the next scheduled item should run | ||||||
|   // @param now Fresh timestamp from millis() - must not be stale/cached |   // @param now Fresh timestamp from millis() - must not be stale/cached | ||||||
|  |   // Returns the time in milliseconds until the next scheduled item, or nullopt if no items | ||||||
|  |   // This method performs cleanup of removed items before checking the schedule | ||||||
|  |   // IMPORTANT: This method should only be called from the main thread (loop task). | ||||||
|   optional<uint32_t> next_schedule_in(uint32_t now); |   optional<uint32_t> next_schedule_in(uint32_t now); | ||||||
|  |  | ||||||
|   // Execute all scheduled items that are ready |   // Execute all scheduled items that are ready | ||||||
| @@ -147,7 +150,10 @@ class Scheduler { | |||||||
|                          uint32_t delay, std::function<void()> func); |                          uint32_t delay, std::function<void()> func); | ||||||
|  |  | ||||||
|   uint64_t millis_64_(uint32_t now); |   uint64_t millis_64_(uint32_t now); | ||||||
|   void cleanup_(); |   // Cleanup logically deleted items from the scheduler | ||||||
|  |   // Returns the number of items remaining after cleanup | ||||||
|  |   // IMPORTANT: This method should only be called from the main thread (loop task). | ||||||
|  |   size_t cleanup_(); | ||||||
|   void pop_raw_(); |   void pop_raw_(); | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
| @@ -191,17 +197,6 @@ class Scheduler { | |||||||
|     return item->remove || (item->component != nullptr && item->component->is_failed()); |     return item->remove || (item->component != nullptr && item->component->is_failed()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Check if the scheduler has no items. |  | ||||||
|   // IMPORTANT: This method should only be called from the main thread (loop task). |  | ||||||
|   // It performs cleanup of removed items and checks if the queue is empty. |  | ||||||
|   // The items_.empty() check at the end is done without a lock for performance, |  | ||||||
|   // which is safe because this is only called from the main thread while other |  | ||||||
|   // threads only add items (never remove them). |  | ||||||
|   bool empty_() { |  | ||||||
|     this->cleanup_(); |  | ||||||
|     return this->items_.empty(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   Mutex lock_; |   Mutex lock_; | ||||||
|   std::vector<std::unique_ptr<SchedulerItem>> items_; |   std::vector<std::unique_ptr<SchedulerItem>> items_; | ||||||
|   std::vector<std::unique_ptr<SchedulerItem>> to_add_; |   std::vector<std::unique_ptr<SchedulerItem>> to_add_; | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ platformio==6.1.18  # When updating platformio, also update /docker/Dockerfile | |||||||
| esptool==4.9.0 | esptool==4.9.0 | ||||||
| click==8.1.7 | click==8.1.7 | ||||||
| esphome-dashboard==20250514.0 | esphome-dashboard==20250514.0 | ||||||
| aioesphomeapi==37.0.1 | aioesphomeapi==37.0.2 | ||||||
| zeroconf==0.147.0 | zeroconf==0.147.0 | ||||||
| puremagic==1.30 | puremagic==1.30 | ||||||
| ruamel.yaml==0.18.14 # dashboard_import | ruamel.yaml==0.18.14 # dashboard_import | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user