@@ -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,6 +906,29 @@ esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client)
886906 return ESP_ERR_INVALID_ARG ;
887907 }
888908
909+ if (client -> status_bits ) {
910+ xSemaphoreTakeRecursive (client -> lock , portMAX_DELAY );
911+ EventBits_t bits = xEventGroupGetBits (client -> status_bits );
912+ if (bits & DESTRUCTION_IN_PROGRESS_BIT ) {
913+ xSemaphoreGiveRecursive (client -> lock );
914+ ESP_LOGD (TAG , "Destruction already in progress, skipping" );
915+ return ESP_OK ;
916+ }
917+
918+ if ((bits & STOPPED_BIT ) == 0 ) {
919+ if (xTaskGetCurrentTaskHandle () == client -> task_handle ) {
920+ ESP_LOGI (TAG , "esp_websocket_client_destroy called from task, deferring..." );
921+ client -> run = false;
922+ client -> selected_for_destroying = true;
923+ xEventGroupSetBits (client -> status_bits , DESTRUCTION_IN_PROGRESS_BIT );
924+ xSemaphoreGiveRecursive (client -> lock );
925+ return ESP_OK ;
926+ }
927+ }
928+ xEventGroupSetBits (client -> status_bits , DESTRUCTION_IN_PROGRESS_BIT );
929+ xSemaphoreGiveRecursive (client -> lock );
930+ }
931+
889932 if (client -> status_bits && (STOPPED_BIT & xEventGroupGetBits (client -> status_bits )) == 0 ) {
890933 stop_wait_task (client );
891934 }
@@ -1118,6 +1161,14 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
11181161
11191162static int esp_websocket_client_send_close (esp_websocket_client_handle_t client , int code , const char * additional_data , int total_len , TickType_t timeout );
11201163
1164+ static void esp_websocket_client_destroy_task (void * pv )
1165+ {
1166+ esp_websocket_client_handle_t client = (esp_websocket_client_handle_t )pv ;
1167+ ESP_LOGI (TAG , "Deferred destruction of websocket client" );
1168+ destroy_and_free_resources (client );
1169+ vTaskDelete (NULL );
1170+ }
1171+
11211172static void esp_websocket_client_task (void * pv )
11221173{
11231174 const int lock_timeout = portMAX_DELAY ;
@@ -1377,7 +1428,9 @@ static void esp_websocket_client_task(void *pv)
13771428 xEventGroupSetBits (client -> status_bits , STOPPED_BIT );
13781429 client -> state = WEBSOCKET_STATE_UNKNOW ;
13791430 if (client -> selected_for_destroying == true) {
1380- destroy_and_free_resources (client );
1431+ if (xTaskCreate (esp_websocket_client_destroy_task , "ws_destroy" , 4096 , client , client -> config -> task_prio , NULL ) != pdPASS ) {
1432+ ESP_LOGE (TAG , "Failed to create destroy task, memory will leak" );
1433+ }
13811434 }
13821435 vTaskDelete (NULL );
13831436}
@@ -1400,8 +1453,58 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
14001453 }
14011454 }
14021455
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 ) {
1456+ client -> task_handle = NULL ;
1457+ BaseType_t res = pdPASS ;
1458+ #if CONFIG_ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM
1459+ if (client -> config -> task_stack > 0 ) {
1460+ if (client -> task_stack_buffer == NULL ) {
1461+ client -> task_stack_buffer = (StackType_t * )heap_caps_calloc (1 , client -> config -> task_stack , MALLOC_CAP_SPIRAM );
1462+ }
1463+ // TCB must be in internal RAM for xTaskCreateStaticPinnedToCore
1464+ if (client -> task_buffer == NULL ) {
1465+ client -> task_buffer = (StaticTask_t * )heap_caps_calloc (1 , sizeof (StaticTask_t ), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT );
1466+ }
1467+
1468+ if (client -> task_stack_buffer && client -> task_buffer ) {
1469+ ESP_LOGI (TAG , "Allocated %d bytes stack in PSRAM for WebSocket task" , client -> config -> task_stack );
1470+ client -> task_handle = xTaskCreateStaticPinnedToCore (
1471+ esp_websocket_client_task ,
1472+ client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1473+ client -> config -> task_stack / sizeof (StackType_t ),
1474+ client ,
1475+ client -> config -> task_prio ,
1476+ client -> task_stack_buffer ,
1477+ client -> task_buffer ,
1478+ tskNO_AFFINITY
1479+ );
1480+ if (client -> task_handle == NULL ) {
1481+ res = pdFAIL ;
1482+ }
1483+ } else {
1484+ ESP_LOGW (TAG , "Failed to allocate PSRAM stack, falling back to internal RAM" );
1485+ if (client -> task_stack_buffer ) {
1486+ heap_caps_free (client -> task_stack_buffer );
1487+ client -> task_stack_buffer = NULL ;
1488+ }
1489+ if (client -> task_buffer ) {
1490+ heap_caps_free (client -> task_buffer );
1491+ client -> task_buffer = NULL ;
1492+ }
1493+
1494+ res = xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1495+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , tskNO_AFFINITY );
1496+ }
1497+ } else {
1498+ res = xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1499+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , tskNO_AFFINITY );
1500+ }
1501+ #else
1502+ res = xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1503+ client -> config -> task_stack / sizeof (StackType_t ), client , client -> config -> task_prio , & client -> task_handle , tskNO_AFFINITY );
1504+ #endif
1505+
1506+ if (res != pdPASS || client -> task_handle == NULL ) {
1507+ client -> task_handle = NULL ;
14051508 ESP_LOGE (TAG , "Error create websocket task" );
14061509 return ESP_FAIL ;
14071510 }
0 commit comments