esp_websocket_client: Add Kconfig options for PSRAM allocation (IDFGH-17011)#980
esp_websocket_client: Add Kconfig options for PSRAM allocation (IDFGH-17011)#980shardt68 wants to merge 7 commits intoespressif:masterfrom
Conversation
13277dd to
27c4e4b
Compare
27c4e4b to
8a75888
Compare
8a75888 to
b683518
Compare
b683518 to
6c381b9
Compare
6c381b9 to
e84c0eb
Compare
cbf6bad to
016bf62
Compare
016bf62 to
0ae9008
Compare
15cba04 to
9d9f1c9
Compare
9d9f1c9 to
391fb0b
Compare
|
Hi @gabsuren, I wanted to follow up on this PR to see if there is any feedback or if any additional information is needed from my side to move this forward. These changes are quite important for memory-constrained applications using multiple concurrent TLS/HTTPS connections (like Spotify Connect implementations). By allowing the WebSocket task stack and client structure to be moved to PSRAM, we can significantly free up critical internal RAM. Beyond the memory allocation, this PR also introduces a more robust lifecycle management (using DESTRUCTION_IN_PROGRESS_BIT and deferred task deletion). This hardening has proven to be very stable and effectively prevents race conditions during rapid reconnect/destroy cycles that were previously an issue. The branch is currently conflict-free and has been extensively tested on ESP32-S3. Looking forward to your thoughts! |
|
hi @shardt68 In the meantime, I noticed some compilation issues in the target tests due to the PR changes, for example errors around https://github.com/espressif/esp-protocols/actions/runs/20578631568/job/61449098113?pr=980 Best regards, |
|
Hi @gabsuren, Thank you for your feedback! I've fixed the build issues and the client->run state inconsistency identified by the bot in the latest commit. Regarding the scope of this PR: While the primary goal was to add PSRAM allocation support, we encountered several race conditions and lifecycle issues during testing (some of which were also highlighted by the Cursor review bot). To ensure the stability of the component—especially when using static task buffers in PSRAM—we felt it was necessary to harden the task lifecycle. Key additions beyond the memory allocation include: A deferred destruction mechanism (esp_websocket_client_destroy_task) and a DESTRUCTION_IN_PROGRESS_BIT to prevent use-after-free and deadlocks when destroying the client from within its own event loop or callback. Looking forward to your guidance! |
|
Hi @shardt68, I've reviewed the recent commits, and I agree with your assessment. These changes significantly touch the core lifecycle and concurrency model of the client. To answer your question: Yes, please split these changes into a separate PR. Also, regarding the version update in Could you please: Thanks again for your contribution! |
|
|
||
| xSemaphoreTakeRecursive(client->lock, portMAX_DELAY); | ||
| if (client->task_handle) { | ||
| while (client->task_handle && eTaskGetState(client->task_handle) != eSuspended) { |
There was a problem hiding this comment.
Wait for task to suspend might block indefinitely if the task is blocked elsewhere. If the client is stuck in a network call or blocked on a mutex, stop() will hang the caller forever
| } | ||
| } | ||
| vTaskDelete(NULL); | ||
| vTaskSuspend(NULL); |
There was a problem hiding this comment.
If the task crashes, blocks, or enters an infinite loop before it reaches line 1502, it will never become suspended right?
| xSemaphoreGiveRecursive(client->lock); | ||
|
|
||
| if (!already_destroying) { | ||
| if (xTaskCreate(esp_websocket_client_destroy_task, "ws_destroy", 4096, client, client->config->task_prio, NULL) != pdPASS) { |
There was a problem hiding this comment.
If the system is Out Of Memory (OOM), the helper task is not created. The client task suspends itself, but no one is left to free its allocated memory.
gabsuren
left a comment
There was a problem hiding this comment.
Left some notes. Please take a look.
The task cleanup logic is duplicated across 4 different locations. This increases maintenance burden and the risk that future changes will introduce bugs if not applied consistently to all 4 spots.
|
@shardt68 Regarding the concurrency issues you encountered: Thank you again! |
|
Hi @gabsuren, Thank you for the guidance! I have updated the PR to focus strictly on the PSRAM allocation configuration. Specifically, I have: Reverted the version bump in idf_component.yml and CHANGELOG.md.
Example Scenario:
I have already prepared these hardening fixes in a separate branch and will submit them as a new PR for independent review once this PSRAM feature is addressed. Please let me know if this version looks good for merging! Best regards, |
d2242b7 to
61a8100
Compare
|
@shardt68 thank you for simplifying the PR to consider only the PSRAM part. Best regards, |
@shardt68 sorry I missed your last message, thanks for providing an example. The deadlock occurs because Instead of destroying the client immediately, signal it to destroy itself upon exit. ❌ Incorrect (Causes Deadlock): ✅ Correct: Best regards, |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
This is the final PR Bugbot will review for you during this billing cycle
Your free Bugbot reviews will reset on March 20
Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
| client->task_handle = xTaskCreateStaticPinnedToCore( | ||
| esp_websocket_client_task, | ||
| client->config->task_name ? client->config->task_name : "websocket_task", | ||
| client->config->task_stack / sizeof(StackType_t), |
There was a problem hiding this comment.
Stack size incorrectly divided, reducing it to 1/4
High Severity
In ESP-IDF's FreeRTOS, xTaskCreateStaticPinnedToCore expects ulStackDepth in bytes (unlike vanilla FreeRTOS which uses words). The code passes client->config->task_stack / sizeof(StackType_t), which divides the intended stack size by 4 on 32-bit platforms. With the default 4096-byte stack, the task only receives 1024 bytes, almost certainly causing a stack overflow. The value passed here needs to match what was allocated — just client->config->task_stack.
|
You have used all of your free Bugbot PR reviews. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |


Description
This PR introduces two new Kconfig options to the
esp_websocket_clientcomponent to allow memory allocation in external RAM (PSRAM):ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM: Enables allocation of the WebSocket task stack in PSRAM usingxTaskCreateStaticPinnedToCore.ESP_WS_CLIENT_ALLOC_IN_EXT_RAM: Enables allocation of theesp_websocket_clientstructure and its internal configuration storage in PSRAM.Motivation
In applications requiring multiple concurrent secure connections (e.g., TLS/HTTPS), internal RAM is a critical bottleneck. Each TLS session typically requires 16-20KB of internal RAM for buffers. By allowing the WebSocket client's task stack and internal structures to be moved to PSRAM, significant internal memory is freed for these critical network operations, improving overall system stability and preventing
ESP_ERR_NO_MEMfailures in memory-constrained scenarios.Implementation Details
heap_caps_callocwithMALLOC_CAP_SPIRAMfor the client structure and config.xTaskCreateStaticPinnedToCorefor the task stack when PSRAM is selected.Related
Testing
heap_caps_get_free_sizethat internal RAM usage decreased by the expected amount (~5KB for structure + stack size).Note
Medium Risk
Touches task creation and memory allocation/free paths, so mis-sizing the stack or allocation failures could prevent the client task from starting or could leak memory. Defaults remain unchanged unless the new Kconfig options are enabled.
Overview
Adds two new Kconfig options to move WebSocket client memory usage into PSRAM: allocating the
esp_websocket_client/config storage viaheap_caps_callocand optionally allocating the WebSocket task stack in PSRAM.When
ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAMis enabled,esp_websocket_client_start()switches toxTaskCreateStaticPinnedToCorewith a PSRAM-backed stack and internal-RAM TCB, and ensures these buffers are freed during client teardown.Written by Cursor Bugbot for commit 46b2d47. This will update automatically on new commits. Configure here.