[mqtt] Fix outbound EventPool/LockFreeQueue sizing off-by-one

Size the outbound pool to N-1 to match queue capacity, same as the
inbound pool fix in the previous commit.
This commit is contained in:
J. Nick Koston
2026-03-17 11:52:31 -10:00
parent 95ff70eb48
commit da1ee1bf66
2 changed files with 6 additions and 5 deletions
@@ -198,8 +198,8 @@ void MQTTBackendESP32::mqtt_event_handler(void *handler_args, esp_event_base_t b
return;
}
event->populate(*static_cast<esp_mqtt_event_t *>(event_data));
// Push always succeeds: pool is sized to queue capacity (N-1), so if
// allocate() returned non-null, the queue is guaranteed to have room.
// Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
// allocate() returned non-null, the queue cannot be full.
instance->mqtt_event_queue_.push(event);
// Wake main loop immediately to process MQTT event instead of waiting for select() timeout
+4 -3
View File
@@ -258,7 +258,8 @@ class MQTTBackendESP32 final : public MQTTBackend {
bool skip_cert_cn_check_{false};
#if defined(USE_MQTT_IDF_ENQUEUE)
static void esphome_mqtt_task(void *params);
EventPool<struct QueueElement, MQTT_QUEUE_LENGTH> mqtt_outbound_pool_;
// Pool sized to queue capacity (SIZE-1) — see mqtt_event_pool_ comment.
EventPool<struct QueueElement, MQTT_QUEUE_LENGTH - 1> mqtt_outbound_pool_;
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,
@@ -277,8 +278,8 @@ class MQTTBackendESP32 final : public MQTTBackend {
// buffer that holds N-1 elements (one slot distinguishes full from empty).
// This guarantees allocate() returns nullptr before push() can fail, which:
// 1. Prevents leaking a pool slot (the Nth allocate succeeds but push fails)
// 2. Ensures only the main loop ever calls release(), preserving the SPSC
// contract on the pool's internal free list
// 2. Avoids needing release() on the producer path after a failed push(),
// preserving the SPSC contract on the pool's internal free list
EventPool<Event, MQTT_EVENT_QUEUE_LENGTH - 1> mqtt_event_pool_;
LockFreeQueue<Event, MQTT_EVENT_QUEUE_LENGTH> mqtt_event_queue_;