@@ -75,6 +75,7 @@ const static int STOPPED_BIT = BIT0;
7575const static int CLOSE_FRAME_SENT_BIT = BIT1 ; // Indicates that a close frame was sent by the client
7676// and we are waiting for the server to continue with clean close
7777const static int REQUESTED_STOP_BIT = BIT2 ; // Indicates that a client stop has been requested
78+ const static int DESTRUCTION_IN_PROGRESS_BIT = BIT3 ; // Indicates that the client is being destroyed
7879
7980ESP_EVENT_DEFINE_BASE (WEBSOCKET_EVENTS );
8081
@@ -156,6 +157,8 @@ struct esp_websocket_client {
156157 int payload_offset ;
157158 esp_transport_keep_alive_t keep_alive_cfg ;
158159 struct ifreq * if_name ;
160+ StackType_t * task_stack_buffer ;
161+ StaticTask_t * task_buffer ;
159162};
160163
161164static uint64_t _tick_get_ms (void )
@@ -497,6 +500,15 @@ static void destroy_and_free_resources(esp_websocket_client_handle_t client)
497500 free (client -> errormsg_buffer );
498501 if (client -> status_bits ) {
499502 vEventGroupDelete (client -> status_bits );
503+ client -> status_bits = NULL ;
504+ }
505+ if (client -> task_stack_buffer ) {
506+ heap_caps_free (client -> task_stack_buffer );
507+ client -> task_stack_buffer = NULL ;
508+ }
509+ if (client -> task_buffer ) {
510+ heap_caps_free (client -> task_buffer );
511+ client -> task_buffer = NULL ;
500512 }
501513 free (client );
502514 client = NULL ;
@@ -747,7 +759,11 @@ static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_hand
747759
748760esp_websocket_client_handle_t esp_websocket_client_init (const esp_websocket_client_config_t * config )
749761{
762+ #if CONFIG_ESP_WS_CLIENT_ALLOC_IN_EXT_RAM
763+ esp_websocket_client_handle_t client = heap_caps_calloc (1 , sizeof (struct esp_websocket_client ), MALLOC_CAP_SPIRAM );
764+ #else
750765 esp_websocket_client_handle_t client = calloc (1 , sizeof (struct esp_websocket_client ));
766+ #endif
751767 ESP_WS_CLIENT_MEM_CHECK (TAG , client , return NULL );
752768
753769 esp_event_loop_args_t event_args = {
@@ -782,7 +798,11 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
782798 ESP_WS_CLIENT_MEM_CHECK (TAG , client -> tx_lock , goto _websocket_init_fail );
783799#endif
784800
801+ #if CONFIG_ESP_WS_CLIENT_ALLOC_IN_EXT_RAM
802+ client -> config = heap_caps_calloc (1 , sizeof (websocket_config_storage_t ), MALLOC_CAP_SPIRAM );
803+ #else
785804 client -> config = calloc (1 , sizeof (websocket_config_storage_t ));
805+ #endif
786806 ESP_WS_CLIENT_MEM_CHECK (TAG , client -> config , goto _websocket_init_fail );
787807
788808 if (config -> transport == WEBSOCKET_TRANSPORT_OVER_TCP ) {
@@ -886,8 +906,22 @@ esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client)
886906 return ESP_ERR_INVALID_ARG ;
887907 }
888908
889- if (client -> status_bits && (STOPPED_BIT & xEventGroupGetBits (client -> status_bits )) == 0 ) {
890- stop_wait_task (client );
909+ if (client -> status_bits ) {
910+ EventBits_t bits = xEventGroupGetBits (client -> status_bits );
911+ if (bits & DESTRUCTION_IN_PROGRESS_BIT ) {
912+ ESP_LOGD (TAG , "Destruction already in progress, skipping" );
913+ return ESP_OK ;
914+ }
915+
916+ if ((bits & STOPPED_BIT ) == 0 ) {
917+ if (stop_wait_task (client ) == ESP_FAIL ) {
918+ ESP_LOGI (TAG , "esp_websocket_client_destroy called from task, deferring..." );
919+ client -> run = false;
920+ client -> selected_for_destroying = true;
921+ return ESP_OK ;
922+ }
923+ }
924+ xEventGroupSetBits (client -> status_bits , DESTRUCTION_IN_PROGRESS_BIT );
891925 }
892926
893927 destroy_and_free_resources (client );
@@ -1118,6 +1152,14 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
11181152
11191153static int esp_websocket_client_send_close (esp_websocket_client_handle_t client , int code , const char * additional_data , int total_len , TickType_t timeout );
11201154
1155+ static void esp_websocket_client_destroy_task (void * pv )
1156+ {
1157+ esp_websocket_client_handle_t client = (esp_websocket_client_handle_t )pv ;
1158+ ESP_LOGI (TAG , "Deferred destruction of websocket client" );
1159+ esp_websocket_client_destroy (client );
1160+ vTaskDelete (NULL );
1161+ }
1162+
11211163static void esp_websocket_client_task (void * pv )
11221164{
11231165 const int lock_timeout = portMAX_DELAY ;
@@ -1377,7 +1419,9 @@ static void esp_websocket_client_task(void *pv)
13771419 xEventGroupSetBits (client -> status_bits , STOPPED_BIT );
13781420 client -> state = WEBSOCKET_STATE_UNKNOW ;
13791421 if (client -> selected_for_destroying == true) {
1380- destroy_and_free_resources (client );
1422+ if (xTaskCreate (esp_websocket_client_destroy_task , "ws_destroy" , 4096 , client , client -> config -> task_prio , NULL ) != pdPASS ) {
1423+ ESP_LOGE (TAG , "Failed to create destroy task, memory will leak" );
1424+ }
13811425 }
13821426 vTaskDelete (NULL );
13831427}
@@ -1400,8 +1444,59 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
14001444 }
14011445 }
14021446
1403- if (xTaskCreate (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1404- client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle ) != pdTRUE ) {
1447+ client -> task_handle = NULL ;
1448+ #if CONFIG_ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM
1449+ if (client -> config -> task_stack > 0 ) {
1450+ if (client -> task_stack_buffer == NULL ) {
1451+ client -> task_stack_buffer = (StackType_t * )heap_caps_malloc (client -> config -> task_stack , MALLOC_CAP_SPIRAM );
1452+ }
1453+ // TCB must be in internal RAM for xTaskCreateStaticPinnedToCore
1454+ if (client -> task_buffer == NULL ) {
1455+ client -> task_buffer = (StaticTask_t * )heap_caps_malloc (sizeof (StaticTask_t ), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT );
1456+ }
1457+
1458+ if (client -> task_stack_buffer && client -> task_buffer ) {
1459+ ESP_LOGI (TAG , "Allocated %d bytes stack in PSRAM for WebSocket task" , client -> config -> task_stack );
1460+ client -> task_handle = xTaskCreateStaticPinnedToCore (
1461+ esp_websocket_client_task ,
1462+ client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1463+ client -> config -> task_stack / sizeof (StackType_t ),
1464+ client ,
1465+ client -> config -> task_prio ,
1466+ client -> task_stack_buffer ,
1467+ client -> task_buffer ,
1468+ 0 // Core 0 (Network)
1469+ );
1470+ } else {
1471+ ESP_LOGW (TAG , "Failed to allocate PSRAM stack, falling back to internal RAM" );
1472+ if (client -> task_stack_buffer ) {
1473+ heap_caps_free (client -> task_stack_buffer );
1474+ client -> task_stack_buffer = NULL ;
1475+ }
1476+ if (client -> task_buffer ) {
1477+ heap_caps_free (client -> task_buffer );
1478+ client -> task_buffer = NULL ;
1479+ }
1480+
1481+ if (xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1482+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , 0 ) != pdPASS ) {
1483+ client -> task_handle = NULL ;
1484+ }
1485+ }
1486+ } else {
1487+ if (xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1488+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , 0 ) != pdPASS ) {
1489+ client -> task_handle = NULL ;
1490+ }
1491+ }
1492+ #else
1493+ if (xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1494+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , 0 ) != pdPASS ) {
1495+ client -> task_handle = NULL ;
1496+ }
1497+ #endif
1498+
1499+ if (client -> task_handle == NULL ) {
14051500 ESP_LOGE (TAG , "Error create websocket task" );
14061501 return ESP_FAIL ;
14071502 }
0 commit comments