@@ -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 ;
@@ -514,11 +526,18 @@ static esp_err_t stop_wait_task(esp_websocket_client_handle_t client)
514526 client -> run = false;
515527 xEventGroupSetBits (client -> status_bits , REQUESTED_STOP_BIT );
516528 xEventGroupWaitBits (client -> status_bits , STOPPED_BIT , false, true, portMAX_DELAY );
529+
530+ if (client -> task_handle ) {
531+ while (eTaskGetState (client -> task_handle ) != eSuspended ) {
532+ vTaskDelay (1 );
533+ }
534+ vTaskDelete (client -> task_handle );
535+ client -> task_handle = NULL ;
536+ }
537+
517538 client -> state = WEBSOCKET_STATE_UNKNOW ;
518539 return ESP_OK ;
519540}
520-
521- #if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
522541static void websocket_header_hook (void * client , const char * line , int line_len )
523542{
524543 ESP_LOGD (TAG , "%s header:%.*s" , __func__ , line_len , line );
@@ -747,7 +766,11 @@ static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_hand
747766
748767esp_websocket_client_handle_t esp_websocket_client_init (const esp_websocket_client_config_t * config )
749768{
769+ #if CONFIG_ESP_WS_CLIENT_ALLOC_IN_EXT_RAM
770+ esp_websocket_client_handle_t client = heap_caps_calloc (1 , sizeof (struct esp_websocket_client ), MALLOC_CAP_SPIRAM );
771+ #else
750772 esp_websocket_client_handle_t client = calloc (1 , sizeof (struct esp_websocket_client ));
773+ #endif
751774 ESP_WS_CLIENT_MEM_CHECK (TAG , client , return NULL );
752775
753776 esp_event_loop_args_t event_args = {
@@ -782,7 +805,11 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
782805 ESP_WS_CLIENT_MEM_CHECK (TAG , client -> tx_lock , goto _websocket_init_fail );
783806#endif
784807
808+ #if CONFIG_ESP_WS_CLIENT_ALLOC_IN_EXT_RAM
809+ client -> config = heap_caps_calloc (1 , sizeof (websocket_config_storage_t ), MALLOC_CAP_SPIRAM );
810+ #else
785811 client -> config = calloc (1 , sizeof (websocket_config_storage_t ));
812+ #endif
786813 ESP_WS_CLIENT_MEM_CHECK (TAG , client -> config , goto _websocket_init_fail );
787814
788815 if (config -> transport == WEBSOCKET_TRANSPORT_OVER_TCP ) {
@@ -886,6 +913,29 @@ esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client)
886913 return ESP_ERR_INVALID_ARG ;
887914 }
888915
916+ if (client -> status_bits ) {
917+ xSemaphoreTakeRecursive (client -> lock , portMAX_DELAY );
918+ EventBits_t bits = xEventGroupGetBits (client -> status_bits );
919+ if (bits & DESTRUCTION_IN_PROGRESS_BIT ) {
920+ xSemaphoreGiveRecursive (client -> lock );
921+ ESP_LOGD (TAG , "Destruction already in progress, skipping" );
922+ return ESP_OK ;
923+ }
924+
925+ if ((bits & STOPPED_BIT ) == 0 ) {
926+ if (xTaskGetCurrentTaskHandle () == client -> task_handle ) {
927+ ESP_LOGI (TAG , "esp_websocket_client_destroy called from task, deferring..." );
928+ client -> run = false;
929+ client -> selected_for_destroying = true;
930+ xEventGroupSetBits (client -> status_bits , DESTRUCTION_IN_PROGRESS_BIT );
931+ xSemaphoreGiveRecursive (client -> lock );
932+ return ESP_OK ;
933+ }
934+ }
935+ xEventGroupSetBits (client -> status_bits , DESTRUCTION_IN_PROGRESS_BIT );
936+ xSemaphoreGiveRecursive (client -> lock );
937+ }
938+
889939 if (client -> status_bits && (STOPPED_BIT & xEventGroupGetBits (client -> status_bits )) == 0 ) {
890940 stop_wait_task (client );
891941 }
@@ -1118,6 +1168,23 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
11181168
11191169static int esp_websocket_client_send_close (esp_websocket_client_handle_t client , int code , const char * additional_data , int total_len , TickType_t timeout );
11201170
1171+ static void esp_websocket_client_destroy_task (void * pv )
1172+ {
1173+ esp_websocket_client_handle_t client = (esp_websocket_client_handle_t )pv ;
1174+ ESP_LOGI (TAG , "Deferred destruction of websocket client" );
1175+
1176+ if (client -> task_handle ) {
1177+ while (eTaskGetState (client -> task_handle ) != eSuspended ) {
1178+ vTaskDelay (1 );
1179+ }
1180+ vTaskDelete (client -> task_handle );
1181+ client -> task_handle = NULL ;
1182+ }
1183+
1184+ destroy_and_free_resources (client );
1185+ vTaskDelete (NULL );
1186+ }
1187+
11211188static void esp_websocket_client_task (void * pv )
11221189{
11231190 const int lock_timeout = portMAX_DELAY ;
@@ -1377,9 +1444,23 @@ static void esp_websocket_client_task(void *pv)
13771444 xEventGroupSetBits (client -> status_bits , STOPPED_BIT );
13781445 client -> state = WEBSOCKET_STATE_UNKNOW ;
13791446 if (client -> selected_for_destroying == true) {
1380- destroy_and_free_resources (client );
1447+ bool already_destroying = false;
1448+ xSemaphoreTakeRecursive (client -> lock , portMAX_DELAY );
1449+ if (xEventGroupGetBits (client -> status_bits ) & DESTRUCTION_IN_PROGRESS_BIT ) {
1450+ already_destroying = true;
1451+ } else {
1452+ xEventGroupSetBits (client -> status_bits , DESTRUCTION_IN_PROGRESS_BIT );
1453+ }
1454+ xSemaphoreGiveRecursive (client -> lock );
1455+
1456+ if (!already_destroying ) {
1457+ if (xTaskCreate (esp_websocket_client_destroy_task , "ws_destroy" , 4096 , client , client -> config -> task_prio , NULL ) != pdPASS ) {
1458+ ESP_LOGE (TAG , "Failed to create destroy task, memory will leak" );
1459+ xEventGroupClearBits (client -> status_bits , DESTRUCTION_IN_PROGRESS_BIT );
1460+ }
1461+ }
13811462 }
1382- vTaskDelete (NULL );
1463+ vTaskSuspend (NULL );
13831464}
13841465
13851466esp_err_t esp_websocket_client_start (esp_websocket_client_handle_t client )
@@ -1400,8 +1481,58 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
14001481 }
14011482 }
14021483
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 ) {
1484+ client -> task_handle = NULL ;
1485+ BaseType_t res = pdPASS ;
1486+ #if CONFIG_ESP_WS_CLIENT_TASK_STACK_IN_EXT_RAM
1487+ if (client -> config -> task_stack > 0 ) {
1488+ if (client -> task_stack_buffer == NULL ) {
1489+ client -> task_stack_buffer = (StackType_t * )heap_caps_calloc (1 , client -> config -> task_stack , MALLOC_CAP_SPIRAM );
1490+ }
1491+ // TCB must be in internal RAM for xTaskCreateStaticPinnedToCore
1492+ if (client -> task_buffer == NULL ) {
1493+ client -> task_buffer = (StaticTask_t * )heap_caps_calloc (1 , sizeof (StaticTask_t ), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT );
1494+ }
1495+
1496+ if (client -> task_stack_buffer && client -> task_buffer ) {
1497+ ESP_LOGI (TAG , "Allocated %d bytes stack in PSRAM for WebSocket task" , client -> config -> task_stack );
1498+ client -> task_handle = xTaskCreateStaticPinnedToCore (
1499+ esp_websocket_client_task ,
1500+ client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1501+ client -> config -> task_stack / sizeof (StackType_t ),
1502+ client ,
1503+ client -> config -> task_prio ,
1504+ client -> task_stack_buffer ,
1505+ client -> task_buffer ,
1506+ tskNO_AFFINITY
1507+ );
1508+ if (client -> task_handle == NULL ) {
1509+ res = pdFAIL ;
1510+ }
1511+ } else {
1512+ ESP_LOGW (TAG , "Failed to allocate PSRAM stack, falling back to internal RAM" );
1513+ if (client -> task_stack_buffer ) {
1514+ heap_caps_free (client -> task_stack_buffer );
1515+ client -> task_stack_buffer = NULL ;
1516+ }
1517+ if (client -> task_buffer ) {
1518+ heap_caps_free (client -> task_buffer );
1519+ client -> task_buffer = NULL ;
1520+ }
1521+
1522+ res = xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1523+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , tskNO_AFFINITY );
1524+ }
1525+ } else {
1526+ res = xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1527+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , tskNO_AFFINITY );
1528+ }
1529+ #else
1530+ res = xTaskCreatePinnedToCore (esp_websocket_client_task , client -> config -> task_name ? client -> config -> task_name : "websocket_task" ,
1531+ client -> config -> task_stack , client , client -> config -> task_prio , & client -> task_handle , tskNO_AFFINITY );
1532+ #endif
1533+
1534+ if (res != pdPASS || client -> task_handle == NULL ) {
1535+ client -> task_handle = NULL ;
14051536 ESP_LOGE (TAG , "Error create websocket task" );
14061537 return ESP_FAIL ;
14071538 }
0 commit comments