mirror of
https://github.com/esphome/esphome.git
synced 2026-06-01 09:25:09 +08:00
[api] Extract cold code from APIConnection::loop() hot path (#13901)
This commit is contained in:
@@ -219,35 +219,8 @@ void APIConnection::loop() {
|
|||||||
this->process_batch_();
|
this->process_batch_();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this->active_iterator_) {
|
if (this->active_iterator_ != ActiveIterator::NONE) {
|
||||||
case ActiveIterator::LIST_ENTITIES:
|
this->process_active_iterator_();
|
||||||
if (this->iterator_storage_.list_entities.completed()) {
|
|
||||||
this->destroy_active_iterator_();
|
|
||||||
if (this->flags_.state_subscription) {
|
|
||||||
this->begin_iterator_(ActiveIterator::INITIAL_STATE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->process_iterator_batch_(this->iterator_storage_.list_entities);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ActiveIterator::INITIAL_STATE:
|
|
||||||
if (this->iterator_storage_.initial_state.completed()) {
|
|
||||||
this->destroy_active_iterator_();
|
|
||||||
// Process any remaining batched messages immediately
|
|
||||||
if (!this->deferred_batch_.empty()) {
|
|
||||||
this->process_batch_();
|
|
||||||
}
|
|
||||||
// Now that everything is sent, enable immediate sending for future state changes
|
|
||||||
this->flags_.should_try_send_immediately = true;
|
|
||||||
// Release excess memory from buffers that grew during initial sync
|
|
||||||
this->deferred_batch_.release_buffer();
|
|
||||||
this->helper_->release_buffers();
|
|
||||||
} else {
|
|
||||||
this->process_iterator_batch_(this->iterator_storage_.initial_state);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ActiveIterator::NONE:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->flags_.sent_ping) {
|
if (this->flags_.sent_ping) {
|
||||||
@@ -283,6 +256,49 @@ void APIConnection::loop() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APIConnection::process_active_iterator_() {
|
||||||
|
// Caller ensures active_iterator_ != NONE
|
||||||
|
if (this->active_iterator_ == ActiveIterator::LIST_ENTITIES) {
|
||||||
|
if (this->iterator_storage_.list_entities.completed()) {
|
||||||
|
this->destroy_active_iterator_();
|
||||||
|
if (this->flags_.state_subscription) {
|
||||||
|
this->begin_iterator_(ActiveIterator::INITIAL_STATE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->process_iterator_batch_(this->iterator_storage_.list_entities);
|
||||||
|
}
|
||||||
|
} else { // INITIAL_STATE
|
||||||
|
if (this->iterator_storage_.initial_state.completed()) {
|
||||||
|
this->destroy_active_iterator_();
|
||||||
|
// Process any remaining batched messages immediately
|
||||||
|
if (!this->deferred_batch_.empty()) {
|
||||||
|
this->process_batch_();
|
||||||
|
}
|
||||||
|
// Now that everything is sent, enable immediate sending for future state changes
|
||||||
|
this->flags_.should_try_send_immediately = true;
|
||||||
|
// Release excess memory from buffers that grew during initial sync
|
||||||
|
this->deferred_batch_.release_buffer();
|
||||||
|
this->helper_->release_buffers();
|
||||||
|
} else {
|
||||||
|
this->process_iterator_batch_(this->iterator_storage_.initial_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIConnection::process_iterator_batch_(ComponentIterator &iterator) {
|
||||||
|
size_t initial_size = this->deferred_batch_.size();
|
||||||
|
size_t max_batch = this->get_max_batch_size_();
|
||||||
|
while (!iterator.completed() && (this->deferred_batch_.size() - initial_size) < max_batch) {
|
||||||
|
iterator.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the batch is full, process it immediately
|
||||||
|
// Note: iterator.advance() already calls schedule_batch_() via schedule_message_()
|
||||||
|
if (this->deferred_batch_.size() >= max_batch) {
|
||||||
|
this->process_batch_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool APIConnection::send_disconnect_response_() {
|
bool APIConnection::send_disconnect_response_() {
|
||||||
// remote initiated disconnect_client
|
// remote initiated disconnect_client
|
||||||
// don't close yet, we still need to send the disconnect response
|
// don't close yet, we still need to send the disconnect response
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
class ComponentIterator;
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
namespace esphome::api {
|
namespace esphome::api {
|
||||||
|
|
||||||
// Keepalive timeout in milliseconds
|
// Keepalive timeout in milliseconds
|
||||||
@@ -366,20 +370,13 @@ class APIConnection final : public APIServerConnectionBase {
|
|||||||
return this->client_supports_api_version(1, 14) ? MAX_INITIAL_PER_BATCH : MAX_INITIAL_PER_BATCH_LEGACY;
|
return this->client_supports_api_version(1, 14) ? MAX_INITIAL_PER_BATCH : MAX_INITIAL_PER_BATCH_LEGACY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to process multiple entities from an iterator in a batch
|
// Process active iterator (list_entities/initial_state) during connection setup.
|
||||||
template<typename Iterator> void process_iterator_batch_(Iterator &iterator) {
|
// Extracted from loop() — only runs during initial handshake, NONE in steady state.
|
||||||
size_t initial_size = this->deferred_batch_.size();
|
void __attribute__((noinline)) process_active_iterator_();
|
||||||
size_t max_batch = this->get_max_batch_size_();
|
|
||||||
while (!iterator.completed() && (this->deferred_batch_.size() - initial_size) < max_batch) {
|
|
||||||
iterator.advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the batch is full, process it immediately
|
// Helper method to process multiple entities from an iterator in a batch.
|
||||||
// Note: iterator.advance() already calls schedule_batch_() via schedule_message_()
|
// Takes ComponentIterator base class reference to avoid duplicate template instantiations.
|
||||||
if (this->deferred_batch_.size() >= max_batch) {
|
void process_iterator_batch_(ComponentIterator &iterator);
|
||||||
this->process_batch_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ class ListEntitiesIterator : public ComponentIterator {
|
|||||||
bool on_update(update::UpdateEntity *entity) override;
|
bool on_update(update::UpdateEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
bool on_end() override;
|
bool on_end() override;
|
||||||
bool completed() { return this->state_ == IteratorState::NONE; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIConnection *client_;
|
APIConnection *client_;
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ class InitialStateIterator : public ComponentIterator {
|
|||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool on_update(update::UpdateEntity *entity) override;
|
bool on_update(update::UpdateEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
bool completed() { return this->state_ == IteratorState::NONE; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIConnection *client_;
|
APIConnection *client_;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class ComponentIterator {
|
|||||||
public:
|
public:
|
||||||
void begin(bool include_internal = false);
|
void begin(bool include_internal = false);
|
||||||
void advance();
|
void advance();
|
||||||
|
bool completed() const { return this->state_ == IteratorState::NONE; }
|
||||||
virtual bool on_begin();
|
virtual bool on_begin();
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
virtual bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) = 0;
|
virtual bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user