feat(core): Enhance the cancellation mechanism

This commit is contained in:
lvhaiyu
2026-04-19 20:23:26 +08:00
parent a4a53a1c18
commit 9097fafbd4
22 changed files with 2242 additions and 662 deletions
@@ -156,6 +156,46 @@ esp_err_t display_hal_create(esp_lcd_panel_handle_t panel_handle,
ESP_GOTO_ON_FALSE(lcd_width > 0 && lcd_height > 0, ESP_ERR_INVALID_ARG,
fail, TAG, "invalid lcd size");
/* Idempotent re-create: if a previous Lua script left the HAL fully
* initialized with identical handles and geometry, return success without
* churning resources. This is the happy path when the cap_lua
* exclusive("display") arbiter swaps scripts of the same UI stack.
*
* The "fully initialized" guard matters because the fail: epilogue below
* only rolls back the swap buffer, not the basic fields / semaphore /
* callbacks. Without this guard a partial-init failure (e.g. semaphore
* alloc failed in a previous call) would leave the basic fields matching
* and trick the next call into a no-op success while the underlying sync
* objects are missing. We therefore require every resource the HAL hands
* out to be present before declaring a no-op. */
if (s_state.panel == panel_handle &&
s_state.io == io_handle &&
s_state.panel_if == panel_if &&
s_state.width == lcd_width &&
s_state.height == lcd_height &&
s_state.display_flush_done != NULL &&
s_state.display_callbacks_registered &&
(!display_hal_panel_requires_swap() || s_state.submit_swap_buffer != NULL)) {
ESP_LOGD(TAG, "display_hal_create: already initialized with matching params, no-op");
ret = ESP_OK;
goto fail;
}
/* Defensive cleanup: if a prior session left a swap buffer behind (e.g. a
* Lua script crashed before display.deinit), free it before reallocating
* to avoid leaks that would accumulate across runs. */
if (s_state.submit_swap_buffer) {
ESP_LOGW(TAG, "display_hal_create: freeing leftover swap buffer (%u px)",
(unsigned)s_state.submit_swap_buffer_pixels);
heap_caps_free(s_state.submit_swap_buffer);
s_state.submit_swap_buffer = NULL;
s_state.submit_swap_buffer_pixels = 0;
}
if (s_state.display_callbacks_registered) {
ESP_LOGW(TAG, "display_hal_create: clearing leftover display callbacks");
(void)display_hal_clear_display_callbacks_locked();
}
s_state.panel = panel_handle;
s_state.io = io_handle;
s_state.panel_if = panel_if;
@@ -169,8 +209,6 @@ esp_err_t display_hal_create(esp_lcd_panel_handle_t panel_handle,
s_state.frame_active = false;
s_state.flush_in_flight = false;
s_state.framebuffer_initialized = false;
s_state.submit_swap_buffer = NULL;
s_state.submit_swap_buffer_pixels = 0;
if (display_hal_panel_requires_swap()) {
s_state.submit_swap_buffer = heap_caps_aligned_alloc(16, (size_t)lcd_width * (size_t)lcd_height * sizeof(uint16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(s_state.submit_swap_buffer != NULL, ESP_ERR_NO_MEM, fail, TAG, "alloc submit swap buffer failed");