Merge branch 'fix-stop-during-reconnect' into 'master'

MR: Fix stop during reconnect
Closes IDFGH-18203
See merge request espressif/esp-mqtt!346
This commit is contained in:
Euripedes Rocha
2026-09-29 14:43:46 +02:00
4 changed files with 74 additions and 3 deletions
+4 -2
View File
@@ -34,6 +34,7 @@ ESP_EVENT_DEFINE_BASE(MQTT_EVENTS);
static const int STOPPED_BIT = (1 << 0);
static const int RECONNECT_BIT = (1 << 1);
static const int DISCONNECT_BIT = (1 << 2);
static const int STOP_REQ_BIT = (1 << 3);
static esp_err_t esp_mqtt_dispatch_event(esp_mqtt_client_handle_t client);
static esp_err_t esp_mqtt_dispatch_event_with_msgid(esp_mqtt_client_handle_t client);
@@ -2013,7 +2014,7 @@ static void esp_mqtt_task(void *pv)
break;
case MQTT_STATE_INIT:
xEventGroupClearBits(client->status_bits, RECONNECT_BIT | DISCONNECT_BIT);
xEventGroupClearBits(client->status_bits, RECONNECT_BIT | DISCONNECT_BIT | STOP_REQ_BIT);
client->transport = client->config->transport;
if (!client->transport) {
@@ -2185,7 +2186,7 @@ static void esp_mqtt_task(void *pv)
}
MQTT_API_UNLOCK(client);
xEventGroupWaitBits(client->status_bits, RECONNECT_BIT, false, true,
xEventGroupWaitBits(client->status_bits, RECONNECT_BIT | STOP_REQ_BIT, false, false,
max_poll_timeout(client, client->wait_timeout_ms / 2 / portTICK_PERIOD_MS));
// continue the while loop instead of break, as the mutex is unlocked
continue;
@@ -2352,6 +2353,7 @@ esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client)
client->run = false;
client->state = MQTT_STATE_DISCONNECTED;
xEventGroupSetBits(client->status_bits, STOP_REQ_BIT);
MQTT_API_UNLOCK(client);
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
return ESP_OK;
+1 -1
View File
@@ -1,4 +1,4 @@
idf_component_register(SRCS "test_mqtt_client.cpp" "test_mqtt5_client.cpp" "mqtt5_client_test_adapter.c" "test_log_intercept.cpp" "test_log_matchers.cpp" "test_log_parser.cpp"
idf_component_register(SRCS "test_mqtt_client.cpp" "test_mqtt5_client.cpp" "mqtt_client_test_adapter.c" "mqtt5_client_test_adapter.c" "test_log_intercept.cpp" "test_log_matchers.cpp" "test_log_parser.cpp"
REQUIRES cmock mqtt esp_timer esp_hw_support http_parser log
WHOLE_ARCHIVE)
+14
View File
@@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mqtt_client_priv.h"
void test_mqtt_client_enter_reconnect_wait(esp_mqtt_client_handle_t client)
{
static int mqtt_task;
client->run = true;
client->state = MQTT_STATE_WAIT_RECONNECT;
client->task_handle = (TaskHandle_t)&mqtt_task;
}
+55
View File
@@ -34,6 +34,7 @@ extern "C" {
#include "Mockidf_additions.h"
#endif
#include "Mockesp_timer.h"
void test_mqtt_client_enter_reconnect_wait(esp_mqtt_client_handle_t client);
/*
* The following functions are not directly called but the generation of them
* from cmock is broken, so we need to define them here.
@@ -102,6 +103,45 @@ static BaseType_t fail_to_create_fake_task(TaskFunction_t, const char *const, co
return pdFALSE;
}
struct stop_wake_trace_t {
EventGroupHandle_t event_group;
int phase;
bool invalid_call;
};
static stop_wake_trace_t stop_wake_trace;
static EventBits_t track_event_group_set_bits(EventGroupHandle_t event_group,
EventBits_t bits, int)
{
static constexpr EventBits_t stop_req_bit = 1U << 3;
if (event_group != stop_wake_trace.event_group || bits != stop_req_bit ||
stop_wake_trace.phase != 0) {
stop_wake_trace.invalid_call = true;
}
stop_wake_trace.phase = 1;
return bits;
}
static EventBits_t track_event_group_wait_bits(EventGroupHandle_t event_group,
EventBits_t bits, BaseType_t clear_on_exit,
BaseType_t wait_for_all, TickType_t ticks_to_wait,
int)
{
static constexpr EventBits_t stopped_bit = 1U << 0;
if (event_group != stop_wake_trace.event_group || bits != stopped_bit ||
clear_on_exit || !wait_for_all || ticks_to_wait != portMAX_DELAY ||
stop_wake_trace.phase != 1) {
stop_wake_trace.invalid_call = true;
}
stop_wake_trace.phase = 2;
return stopped_bit;
}
SCENARIO("MQTT Client Operation")
{
// Set expectations for the mocked calls.
@@ -335,6 +375,21 @@ SCENARIO("MQTT Client Operation")
// Only need to start the client, destroy is called automatically at the
// end of scope
}
SECTION("Stop wakes a client waiting to reconnect") {
/* The host build does not run esp_mqtt_task. This checks the signal
* stop() publishes, which is the bit that wait waits on, and that
* the signal is not the reconnect request. */
test_mqtt_client_enter_reconnect_wait(client.get());
stop_wake_trace = {
reinterpret_cast<EventGroupHandle_t>(&event_group), 0, false
};
xTaskGetCurrentTaskHandle_IgnoreAndReturn(nullptr);
xEventGroupSetBits_Stub(track_event_group_set_bits);
xEventGroupWaitBits_Stub(track_event_group_wait_bits);
REQUIRE(esp_mqtt_client_stop(client.get()) == ESP_OK);
REQUIRE(stop_wake_trace.phase == 2);
REQUIRE_FALSE(stop_wake_trace.invalid_call);
}
SECTION("get_state reports client lifecycle correctly") {
SECTION("returns NOT_INITIALIZED for null handle") {
REQUIRE(esp_mqtt_client_get_state(nullptr) ==