mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Merge branch 'ble_align' into integration
This commit is contained in:
		| @@ -56,7 +56,7 @@ enum IoCapability { | ||||
|   IO_CAP_KBDISP = ESP_IO_CAP_KBDISP, | ||||
| }; | ||||
|  | ||||
| enum BLEComponentState { | ||||
| enum BLEComponentState : uint8_t { | ||||
|   /** Nothing has been initialized yet. */ | ||||
|   BLE_COMPONENT_STATE_OFF = 0, | ||||
|   /** BLE should be disabled on next loop. */ | ||||
| @@ -146,21 +146,31 @@ class ESP32BLE : public Component { | ||||
|  private: | ||||
|   template<typename... Args> friend void enqueue_ble_event(Args... args); | ||||
|  | ||||
|   // Vectors (12 bytes each on 32-bit, naturally aligned to 4 bytes) | ||||
|   std::vector<GAPEventHandler *> gap_event_handlers_; | ||||
|   std::vector<GAPScanEventHandler *> gap_scan_event_handlers_; | ||||
|   std::vector<GATTcEventHandler *> gattc_event_handlers_; | ||||
|   std::vector<GATTsEventHandler *> gatts_event_handlers_; | ||||
|   std::vector<BLEStatusEventHandler *> ble_status_event_handlers_; | ||||
|   BLEComponentState state_{BLE_COMPONENT_STATE_OFF}; | ||||
|  | ||||
|   // Large objects (size depends on template parameters, but typically aligned to 4 bytes) | ||||
|   esphome::LockFreeQueue<BLEEvent, MAX_BLE_QUEUE_SIZE> ble_events_; | ||||
|   esphome::EventPool<BLEEvent, MAX_BLE_QUEUE_SIZE> ble_event_pool_; | ||||
|   BLEAdvertising *advertising_{}; | ||||
|   esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; | ||||
|   uint32_t advertising_cycle_time_{}; | ||||
|   bool enable_on_boot_{}; | ||||
|  | ||||
|   // optional<string> (typically 16+ bytes on 32-bit, aligned to 4 bytes) | ||||
|   optional<std::string> name_; | ||||
|   uint16_t appearance_{0}; | ||||
|  | ||||
|   // 4-byte aligned members | ||||
|   BLEAdvertising *advertising_{};             // 4 bytes (pointer) | ||||
|   esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};  // 4 bytes (enum) | ||||
|   uint32_t advertising_cycle_time_{};         // 4 bytes | ||||
|  | ||||
|   // 2-byte aligned members | ||||
|   uint16_t appearance_{0};  // 2 bytes | ||||
|  | ||||
|   // 1-byte aligned members (grouped together to minimize padding) | ||||
|   BLEComponentState state_{BLE_COMPONENT_STATE_OFF};  // 1 byte (uint8_t enum) | ||||
|   bool enable_on_boot_{};                             // 1 byte | ||||
| }; | ||||
|  | ||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|   | ||||
| @@ -252,7 +252,7 @@ class MQTTBackendESP32 final : public MQTTBackend { | ||||
| #if defined(USE_MQTT_IDF_ENQUEUE) | ||||
|   static void esphome_mqtt_task(void *params); | ||||
|   EventPool<struct QueueElement, MQTT_QUEUE_LENGTH> mqtt_event_pool_; | ||||
|   LockFreeQueue<struct QueueElement, MQTT_QUEUE_LENGTH> mqtt_queue_; | ||||
|   NotifyingLockFreeQueue<struct QueueElement, MQTT_QUEUE_LENGTH> mqtt_queue_; | ||||
|   TaskHandle_t task_handle_{nullptr}; | ||||
|   bool enqueue_(MqttQueueTypeT type, const char *topic, int qos = 0, bool retain = false, const char *payload = NULL, | ||||
|                 size_t len = 0); | ||||
|   | ||||
| @@ -31,11 +31,20 @@ | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| // Base lock-free queue without task notification | ||||
| template<class T, uint8_t SIZE> class LockFreeQueue { | ||||
|  public: | ||||
|   LockFreeQueue() : head_(0), tail_(0), dropped_count_(0), task_to_notify_(nullptr) {} | ||||
|   LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {} | ||||
|  | ||||
|   bool push(T *element) { | ||||
|     bool was_empty; | ||||
|     uint8_t old_tail; | ||||
|     return push_internal_(element, was_empty, old_tail); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   // Internal push that reports queue state - for use by derived classes | ||||
|   bool push_internal_(T *element, bool &was_empty, uint8_t &old_tail) { | ||||
|     if (element == nullptr) | ||||
|       return false; | ||||
|  | ||||
| @@ -51,34 +60,16 @@ template<class T, uint8_t SIZE> class LockFreeQueue { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     // Check if queue was empty before push | ||||
|     bool was_empty = (current_tail == head_before); | ||||
|     was_empty = (current_tail == head_before); | ||||
|     old_tail = current_tail; | ||||
|  | ||||
|     buffer_[current_tail] = element; | ||||
|     tail_.store(next_tail, std::memory_order_release); | ||||
|  | ||||
|     // Notify optimization: only notify if we need to | ||||
|     if (task_to_notify_ != nullptr) { | ||||
|       if (was_empty) { | ||||
|         // Queue was empty - consumer might be going to sleep, must notify | ||||
|         xTaskNotifyGive(task_to_notify_); | ||||
|       } else { | ||||
|         // Queue wasn't empty - check if consumer has caught up to previous tail | ||||
|         uint8_t head_after = head_.load(std::memory_order_acquire); | ||||
|         if (head_after == current_tail) { | ||||
|           // Consumer just caught up to where tail was - might go to sleep, must notify | ||||
|           // Note: There's a benign race here - between reading head_after and calling | ||||
|           // xTaskNotifyGive(), the consumer could advance further. This would result | ||||
|           // in an unnecessary wake-up, but is harmless and extremely rare in practice. | ||||
|           xTaskNotifyGive(task_to_notify_); | ||||
|         } | ||||
|         // Otherwise: consumer is still behind, no need to notify | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   T *pop() { | ||||
|     uint8_t current_head = head_.load(std::memory_order_relaxed); | ||||
|  | ||||
| @@ -108,11 +99,6 @@ template<class T, uint8_t SIZE> class LockFreeQueue { | ||||
|     return next_tail == head_.load(std::memory_order_acquire); | ||||
|   } | ||||
|  | ||||
|   // Set the FreeRTOS task handle to notify when items are pushed to the queue | ||||
|   // This enables efficient wake-up of a consumer task that's waiting for data | ||||
|   // @param task The FreeRTOS task handle to notify, or nullptr to disable notifications | ||||
|   void set_task_to_notify(TaskHandle_t task) { task_to_notify_ = task; } | ||||
|  | ||||
|  protected: | ||||
|   T *buffer_[SIZE]; | ||||
|   // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset) | ||||
| @@ -123,7 +109,42 @@ template<class T, uint8_t SIZE> class LockFreeQueue { | ||||
|   std::atomic<uint8_t> head_; | ||||
|   // Atomic: written by producer (push), read by consumer (pop) to check if empty | ||||
|   std::atomic<uint8_t> tail_; | ||||
|   // Task handle for notification (optional) | ||||
| }; | ||||
|  | ||||
| // Extended queue with task notification support | ||||
| template<class T, uint8_t SIZE> class NotifyingLockFreeQueue : public LockFreeQueue<T, SIZE> { | ||||
|  public: | ||||
|   NotifyingLockFreeQueue() : LockFreeQueue<T, SIZE>(), task_to_notify_(nullptr) {} | ||||
|  | ||||
|   bool push(T *element) { | ||||
|     bool was_empty; | ||||
|     uint8_t old_tail; | ||||
|     bool result = this->push_internal_(element, was_empty, old_tail); | ||||
|  | ||||
|     // Notify optimization: only notify if we need to | ||||
|     if (result && task_to_notify_ != nullptr) { | ||||
|       if (was_empty) { | ||||
|         // Queue was empty - consumer might be going to sleep, must notify | ||||
|         xTaskNotifyGive(task_to_notify_); | ||||
|       } else if (this->head_.load(std::memory_order_acquire) == old_tail) { | ||||
|         // Consumer just caught up to where tail was - might go to sleep, must notify | ||||
|         // Note: There's a benign race here - between reading head and calling | ||||
|         // xTaskNotifyGive(), the consumer could advance further. This would result | ||||
|         // in an unnecessary wake-up, but is harmless and extremely rare in practice. | ||||
|         xTaskNotifyGive(task_to_notify_); | ||||
|       } | ||||
|       // Otherwise: consumer is still behind, no need to notify | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   // Set the FreeRTOS task handle to notify when items are pushed to the queue | ||||
|   // This enables efficient wake-up of a consumer task that's waiting for data | ||||
|   // @param task The FreeRTOS task handle to notify, or nullptr to disable notifications | ||||
|   void set_task_to_notify(TaskHandle_t task) { task_to_notify_ = task; } | ||||
|  | ||||
|  private: | ||||
|   TaskHandle_t task_to_notify_; | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user