feat(esp_common): Adds ESP_RETURN_ON_ERROR_CLEANUP macro

Merges: https://github.com/espressif/esp-idf/pull/15525
This commit is contained in:
Robot
2025-03-05 20:13:21 +00:00
committed by Konstantin Kondrashov
parent d18019de5b
commit fdb78272a4
4 changed files with 141 additions and 2 deletions
+49
View File
@@ -435,6 +435,55 @@ extern "C" {
#endif // !CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT
/**
* @brief Evaluate an esp_err_t expression, run cleanup on error, and return.
*
* Evaluates @p x once and stores the result in a local variable `err_rc_`.
* If the result is not ESP_OK, the macro:
* - executes the code passed in `__VA_ARGS__` (e.g., cleanup functions or logging),
* - returns `err_rc_` from the current function.
* The cleanup code can freely use `err_rc_`.
*
* Example:
* @code{c}
* esp_err_t initialize_device(void)
* {
* // Simple case: single cleanup action
* ESP_RETURN_ON_ERROR_CLEANUP(open_device("/dev/adc0"), close_device());
*
* // Multiple cleanup actions
* ESP_RETURN_ON_ERROR_CLEANUP(
* allocate_buffer(4096),
* free_buffer(),
* close_device(),
* printf("Failed to initialize sensor: %s\n", esp_err_to_name(err_rc_))
* );
*
* // Complex cleanup with conditional logic and ESP_LOG logging
* ESP_RETURN_ON_ERROR_CLEANUP(
* calibrate_sensor(),
* do {
* if (err_rc_ == ESP_ERR_INVALID_STATE) {
* ESP_LOGE(TAG, "Sensor in invalid state during calibration");}
* }
* free_buffer();
* close_device();
* ESP_LOGE(TAG, "Drop connection");
* } while (0)
* );
*
* return ESP_OK;
* }
* @endcode
*/
#define ESP_RETURN_ON_ERROR_CLEANUP(x, ...) do { \
esp_err_t err_rc_ = (x); \
if (unlikely(err_rc_ != ESP_OK)) { \
__VA_ARGS__; \
return err_rc_; \
} \
} while (0)
#ifdef __cplusplus
}
#endif
@@ -1,4 +1,7 @@
idf_component_register(SRCS "test_app_main.c" "test_attr.c" "test_esp_macro.c"
idf_component_register(SRCS "test_app_main.c"
"test_attr.c"
"test_esp_check.c"
"test_esp_macro.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity esp_mm esp_psram
WHOLE_ARCHIVE)
@@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "esp_err.h"
#include "esp_check.h"
#include "esp_macros.h"
#include "esp_log.h"
#include "unity.h"
static const char *TAG = "TEST";
static void close_device(void)
{
ESP_LOGI(TAG, "Closing device");
}
static void free_buffer(void)
{
ESP_LOGI(TAG, "Freeing buffer");
}
static int where_to_fail = 0;
static esp_err_t test_esp_checks(esp_err_t error)
{
if (where_to_fail == 0) {
where_to_fail++;
ESP_RETURN_ON_ERROR_CLEANUP(error);
}
if (where_to_fail == 1) {
where_to_fail++;
ESP_RETURN_ON_ERROR_CLEANUP(error, close_device());
}
if (where_to_fail == 2) {
where_to_fail++;
ESP_RETURN_ON_ERROR_CLEANUP(
error,
free_buffer(),
printf("Failed to initialize sensor: %s\n", esp_err_to_name(err_rc_))
);
}
if (where_to_fail == 3) {
where_to_fail++;
ESP_RETURN_ON_ERROR_CLEANUP(
error,
do {
if (err_rc_ == ESP_ERR_NOT_FINISHED) {
ESP_LOGE(TAG, "Sensor in invalid state during calibration %s", esp_err_to_name(err_rc_));
}
free_buffer();
close_device();
ESP_LOGE(TAG, "Drop connection");
} while (0)
);
}
return ESP_OK;
}
TEST_CASE("Tests of example ESP_RETURN_ON_ERROR_CLEANUP", "[esp_check]")
{
where_to_fail = 0;
TEST_ESP_ERR(test_esp_checks(ESP_FAIL), ESP_FAIL);
TEST_ESP_ERR(test_esp_checks(ESP_ERR_NOT_SUPPORTED), ESP_ERR_NOT_SUPPORTED);
TEST_ESP_ERR(test_esp_checks(ESP_ERR_NO_MEM), ESP_ERR_NO_MEM);
TEST_ESP_ERR(test_esp_checks(ESP_ERR_NOT_FINISHED), ESP_ERR_NOT_FINISHED);
TEST_ESP_ERR(test_esp_checks(ESP_FAIL), ESP_OK);
}
+16 -1
View File
@@ -95,7 +95,7 @@ Error message will typically look like this:
Macros For Recoverable Errors
-----------------------------
For recoverable errors, ESP-IDF provides a set of macros defined in ``esp_check.h``. The **ESP_RETURN_ON_...**, **ESP_GOTO_ON_...**, and **ESP_RETURN_VOID_ON_...** macros enable concise and consistent error handling, improving code readability and maintainability. Unlike ``ESP_ERROR_CHECK``, these macros do not terminate the program; instead, they print an error message and return or jump as appropriate. For use in interrupt service routines (ISRs), corresponding ``_ISR`` versions (such as :c:macro:`ESP_RETURN_ON_ERROR_ISR`) are available, ensuring safe operation in interrupt contexts.
For recoverable errors, ESP-IDF provides a set of macros defined in ``esp_check.h``. The **ESP_RETURN_ON_...**, **ESP_GOTO_ON_...**, **ESP_RETURN_VOID_ON_...**, and **ESP_RETURN_ON_ERROR_CLEANUP** macros enable concise and consistent error handling, improving code readability and maintainability. Unlike ``ESP_ERROR_CHECK``, these macros do not terminate the program; instead, they print an error message and return or jump as appropriate. For use in interrupt service routines (ISRs), corresponding ``_ISR`` versions (such as :c:macro:`ESP_RETURN_ON_ERROR_ISR`) are available, ensuring safe operation in interrupt contexts.
The macros are defined as follows:
@@ -120,6 +120,8 @@ The macros are defined as follows:
- :c:macro:`ESP_RETURN_VOID_ON_ERROR_ISR` - For ISR context.
- :c:macro:`ESP_RETURN_VOID_ON_FALSE_ISR` - For ISR context.
- **ESP_RETURN_ON_ERROR_CLEANUP**: Return from the function after executing cleanup code if an error is detected. This macro is useful when resources need to be released before returning an error code. It evaluates an expression that returns :cpp:type:`esp_err_t`, and if the result is not :c:macro:`ESP_OK`, it executes the cleanup code provided in the variable arguments (such as freeing resources or logging) before returning the error code from the current function.
The default behavior of these macros can be adjusted: if the :ref:`CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT` option is enabled in Kconfig, error messages will not be included in the application binary and will not be printed.
.. _check_macros_examples:
@@ -144,6 +146,19 @@ Some examples
ESP_RETURN_ON_FALSE(a, err_code, TAG, "fail reason 3"); // err message printed if `a` is not `true`, and then function returns with code `err_code`.
ESP_GOTO_ON_FALSE(a, err_code, err, TAG, "fail reason 4"); // err message printed if `a` is not `true`, `ret` is set to `err_code`, and then jumps to `err`.
ESP_RETURN_ON_ERROR_CLEANUP(init_resource(), free_mem(), deinit_resource()); // if failure, clean up resources by calling `free_mem()` and `deinit_resource()`, then return the error code.
ESP_RETURN_ON_ERROR_CLEANUP( // if more complex cleanup is needed, use a `do {...} while(0)` block, (required for ESP_LOG usage).
sensor_calibrate(),
do {
if (err_rc_ == ESP_ERR_TIMEOUT) {
ESP_LOGE(TAG, "Sensor calibration timeout");
}
sensor_deinit();
ESP_LOGE(TAG, "Sensor cleanup completed");
} while (0)
);
err:
// clean up
return ret;