mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add heap scheduler tests
This commit is contained in:
		| @@ -0,0 +1,21 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| scheduler_rapid_cancellation_component_ns = cg.esphome_ns.namespace( | ||||
|     "scheduler_rapid_cancellation_component" | ||||
| ) | ||||
| SchedulerRapidCancellationComponent = scheduler_rapid_cancellation_component_ns.class_( | ||||
|     "SchedulerRapidCancellationComponent", cg.Component | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(SchedulerRapidCancellationComponent), | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
| @@ -0,0 +1,77 @@ | ||||
| #include "rapid_cancellation_component.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include <thread> | ||||
| #include <vector> | ||||
| #include <chrono> | ||||
| #include <random> | ||||
| #include <sstream> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace scheduler_rapid_cancellation_component { | ||||
|  | ||||
| static const char *const TAG = "scheduler_rapid_cancellation"; | ||||
|  | ||||
| void SchedulerRapidCancellationComponent::setup() { ESP_LOGCONFIG(TAG, "SchedulerRapidCancellationComponent setup"); } | ||||
|  | ||||
| void SchedulerRapidCancellationComponent::run_rapid_cancellation_test() { | ||||
|   ESP_LOGI(TAG, "Starting rapid cancellation test - multiple threads racing on same timeout names"); | ||||
|  | ||||
|   // Reset counters | ||||
|   this->total_scheduled_ = 0; | ||||
|   this->total_executed_ = 0; | ||||
|  | ||||
|   static constexpr int NUM_THREADS = 4;              // Number of threads to create | ||||
|   static constexpr int NUM_NAMES = 10;               // Only 10 unique names | ||||
|   static constexpr int OPERATIONS_PER_THREAD = 100;  // Each thread does 100 operations | ||||
|  | ||||
|   // Create threads that will all fight over the same timeout names | ||||
|   std::vector<std::thread> threads; | ||||
|   threads.reserve(NUM_THREADS); | ||||
|  | ||||
|   for (int thread_id = 0; thread_id < NUM_THREADS; thread_id++) { | ||||
|     threads.emplace_back([this, thread_id]() { | ||||
|       for (int i = 0; i < OPERATIONS_PER_THREAD; i++) { | ||||
|         // Use modulo to ensure multiple threads use the same names | ||||
|         int name_index = i % NUM_NAMES; | ||||
|         std::stringstream ss; | ||||
|         ss << "shared_timeout_" << name_index; | ||||
|         std::string name = ss.str(); | ||||
|  | ||||
|         // All threads schedule timeouts - this will implicitly cancel existing ones | ||||
|         this->set_timeout(name, 100, [this, name]() { | ||||
|           this->total_executed_.fetch_add(1); | ||||
|           ESP_LOGI(TAG, "Executed callback '%s'", name.c_str()); | ||||
|         }); | ||||
|         this->total_scheduled_.fetch_add(1); | ||||
|  | ||||
|         // Small delay to increase chance of race conditions | ||||
|         if (i % 10 == 0) { | ||||
|           std::this_thread::sleep_for(std::chrono::microseconds(100)); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   // Wait for all threads to complete | ||||
|   for (auto &t : threads) { | ||||
|     t.join(); | ||||
|   } | ||||
|  | ||||
|   ESP_LOGI(TAG, "All threads completed. Scheduled: %d", this->total_scheduled_.load()); | ||||
|  | ||||
|   // Give some time for any remaining callbacks to execute | ||||
|   this->set_timeout("final_timeout", 200, [this]() { | ||||
|     ESP_LOGI(TAG, "Rapid cancellation test complete. Final stats:"); | ||||
|     ESP_LOGI(TAG, "  Total scheduled: %d", this->total_scheduled_.load()); | ||||
|     ESP_LOGI(TAG, "  Total executed: %d", this->total_executed_.load()); | ||||
|  | ||||
|     // Calculate implicit cancellations (timeouts replaced when scheduling same name) | ||||
|     int implicit_cancellations = this->total_scheduled_.load() - this->total_executed_.load(); | ||||
|     ESP_LOGI(TAG, "  Implicit cancellations (replaced): %d", implicit_cancellations); | ||||
|     ESP_LOGI(TAG, "  Total accounted: %d (executed + implicit cancellations)", | ||||
|              this->total_executed_.load() + implicit_cancellations); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| }  // namespace scheduler_rapid_cancellation_component | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,22 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include <atomic> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace scheduler_rapid_cancellation_component { | ||||
|  | ||||
| class SchedulerRapidCancellationComponent : public Component { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   float get_setup_priority() const override { return setup_priority::LATE; } | ||||
|  | ||||
|   void run_rapid_cancellation_test(); | ||||
|  | ||||
|  private: | ||||
|   std::atomic<int> total_scheduled_{0}; | ||||
|   std::atomic<int> total_executed_{0}; | ||||
| }; | ||||
|  | ||||
| }  // namespace scheduler_rapid_cancellation_component | ||||
| }  // namespace esphome | ||||
		Reference in New Issue
	
	Block a user