-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add memory_type Support for Flexible Memory Allocation in WebSocket Client to permit other memory type caps to be used (IDFGH-13127) #586
base: master
Are you sure you want to change the base?
Changes from all commits
3374cdb
0309547
832fdcd
8d5e60c
6468375
3d6ee84
4252f7c
65c9c64
95d507e
aba846b
cb6e035
611b4cf
ca28216
6bbf7d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,8 @@ | |
#include "esp_system.h" | ||
#include <errno.h> | ||
#include <arpa/inet.h> | ||
#include "esp_heap_caps.h" | ||
|
||
|
||
static const char *TAG = "websocket_client"; | ||
|
||
|
@@ -39,6 +41,20 @@ static const char *TAG = "websocket_client"; | |
#define WEBSOCKET_KEEP_ALIVE_INTERVAL (5) | ||
#define WEBSOCKET_KEEP_ALIVE_COUNT (3) | ||
|
||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) | ||
#define VALID_MEMORY_FLAGS (MALLOC_CAP_EXEC | MALLOC_CAP_32BIT | MALLOC_CAP_8BIT | MALLOC_CAP_DMA | \ | ||
MALLOC_CAP_PID2 | MALLOC_CAP_PID3 | MALLOC_CAP_PID4 | MALLOC_CAP_PID5 | \ | ||
MALLOC_CAP_PID6 | MALLOC_CAP_PID7 | MALLOC_CAP_SPIRAM | MALLOC_CAP_INTERNAL | \ | ||
MALLOC_CAP_DEFAULT | MALLOC_CAP_IRAM_8BIT | MALLOC_CAP_RETENTION | \ | ||
MALLOC_CAP_RTCRAM | MALLOC_CAP_TCM | MALLOC_CAP_INVALID) | ||
#else | ||
#define VALID_MEMORY_FLAGS (MALLOC_CAP_EXEC | MALLOC_CAP_32BIT | MALLOC_CAP_8BIT | MALLOC_CAP_DMA | \ | ||
MALLOC_CAP_PID2 | MALLOC_CAP_PID3 | MALLOC_CAP_PID4 | MALLOC_CAP_PID5 | \ | ||
MALLOC_CAP_PID6 | MALLOC_CAP_PID7 | MALLOC_CAP_SPIRAM | MALLOC_CAP_INTERNAL | \ | ||
MALLOC_CAP_DEFAULT | MALLOC_CAP_IRAM_8BIT | MALLOC_CAP_RETENTION | \ | ||
MALLOC_CAP_RTCRAM | MALLOC_CAP_INVALID) | ||
#endif | ||
|
||
#define ESP_WS_CLIENT_MEM_CHECK(TAG, a, action) if (!(a)) { \ | ||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Memory exhausted"); \ | ||
action; \ | ||
|
@@ -137,6 +153,8 @@ struct esp_websocket_client { | |
int payload_offset; | ||
esp_transport_keep_alive_t keep_alive_cfg; | ||
struct ifreq *if_name; | ||
int memory_type; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not against making the selection in runtime but, in other components, we make this choice in compile time through Kconfig. |
||
int core; | ||
}; | ||
|
||
static uint64_t _tick_get_ms(void) | ||
|
@@ -152,14 +170,14 @@ static esp_err_t esp_websocket_new_buf(esp_websocket_client_handle_t client, boo | |
free(client->tx_buffer); | ||
} | ||
|
||
client->tx_buffer = calloc(1, client->buffer_size); | ||
client->tx_buffer = heap_caps_calloc(1, client->buffer_size,client->memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->tx_buffer, return ESP_ERR_NO_MEM); | ||
} else { | ||
if (client->rx_buffer) { | ||
free(client->rx_buffer); | ||
} | ||
|
||
client->rx_buffer = calloc(1, client->buffer_size); | ||
client->rx_buffer = heap_caps_calloc(1, client->buffer_size,client->memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->rx_buffer, return ESP_ERR_NO_MEM); | ||
} | ||
#endif | ||
|
@@ -249,7 +267,7 @@ static esp_err_t esp_websocket_client_error(esp_websocket_client_handle_t client | |
if (client->errormsg_buffer) { | ||
free(client->errormsg_buffer); | ||
} | ||
client->errormsg_buffer = malloc(needed_size); | ||
client->errormsg_buffer = heap_caps_malloc(needed_size,client->memory_type); | ||
if (client->errormsg_buffer == NULL) { | ||
client->errormsg_size = 0; | ||
ESP_LOGE(TAG, "Failed to allocate..."); | ||
|
@@ -268,7 +286,7 @@ static esp_err_t esp_websocket_client_error(esp_websocket_client_handle_t client | |
return ESP_OK; | ||
} | ||
|
||
static char *http_auth_basic(const char *username, const char *password) | ||
static char *http_auth_basic(const char *username, const char *password, int memory_type) | ||
{ | ||
int out; | ||
char *user_info = NULL; | ||
|
@@ -285,7 +303,7 @@ static char *http_auth_basic(const char *username, const char *password) | |
} | ||
|
||
esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info)); | ||
digest = calloc(1, strlen(WS_HTTP_BASIC_AUTH) + n + 1); | ||
digest = heap_caps_calloc(1, strlen(WS_HTTP_BASIC_AUTH) + n + 1,memory_type); | ||
if (digest) { | ||
strcpy(digest, WS_HTTP_BASIC_AUTH); | ||
esp_crypto_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info)); | ||
|
@@ -332,7 +350,7 @@ static esp_err_t esp_websocket_client_set_config(esp_websocket_client_handle_t c | |
|
||
if (cfg->username && cfg->password) { | ||
free(cfg->auth); | ||
cfg->auth = http_auth_basic(cfg->username, cfg->password); | ||
cfg->auth = http_auth_basic(cfg->username, cfg->password, client->memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->auth, return ESP_ERR_NO_MEM); | ||
} | ||
|
||
|
@@ -619,9 +637,35 @@ static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_hand | |
|
||
esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_client_config_t *config) | ||
{ | ||
esp_websocket_client_handle_t client = calloc(1, sizeof(struct esp_websocket_client)); | ||
int memory_type = 0; | ||
if ((config->memory_type & ~VALID_MEMORY_FLAGS) != 0) { | ||
ESP_LOGE("TAG", "Invalid memory_type flags set.\n"); | ||
return NULL; | ||
} else if (config->memory_type == 0) { | ||
memory_type = MALLOC_CAP_DEFAULT; //MALLOC_CAP_DEFAULT | ||
ESP_LOGD("TAG","memory_type is not set, using MALLOC_CAP_DEFAULT memory [%d] versus external [%d].\n",memory_type,MALLOC_CAP_SPIRAM); | ||
} else { | ||
memory_type = config->memory_type; | ||
ESP_LOGD("TAG","memory_type is set to %d \n", config->memory_type); | ||
} | ||
|
||
esp_websocket_client_handle_t client = heap_caps_calloc(1, sizeof(struct esp_websocket_client),memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client, return NULL); | ||
|
||
if (config->core){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer it to be introduced in a separated PR. Since we'll have to take into consideration portability among different IDF versions given the proposal of usage |
||
if (config->core >2){ | ||
ESP_LOGW("TAG", "Invalid core affinity flag set, using default tskNO_AFFINITY.\n"); | ||
client->core = tskNO_AFFINITY; | ||
} | ||
else{ | ||
client->core = config->core; | ||
} | ||
} | ||
else { | ||
ESP_LOGW("TAG", "Invalid core affinity flag set, using default tskNO_AFFINITY.\n"); | ||
client->core = tskNO_AFFINITY; | ||
} | ||
|
||
esp_event_loop_args_t event_args = { | ||
.queue_size = WEBSOCKET_EVENT_QUEUE_SIZE, | ||
.task_name = NULL // no task will be created | ||
|
@@ -633,6 +677,8 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie | |
return NULL; | ||
} | ||
|
||
client->memory_type = memory_type; | ||
|
||
if (config->keep_alive_enable == true) { | ||
client->keep_alive_cfg.keep_alive_enable = true; | ||
client->keep_alive_cfg.keep_alive_idle = (config->keep_alive_idle == 0) ? WEBSOCKET_KEEP_ALIVE_IDLE : config->keep_alive_idle; | ||
|
@@ -641,15 +687,15 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie | |
} | ||
|
||
if (config->if_name) { | ||
client->if_name = calloc(1, sizeof(struct ifreq) + 1); | ||
client->if_name = heap_caps_calloc(1, sizeof(struct ifreq) + 1,memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->if_name, goto _websocket_init_fail); | ||
memcpy(client->if_name, config->if_name, sizeof(struct ifreq)); | ||
} | ||
|
||
client->lock = xSemaphoreCreateRecursiveMutex(); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->lock, goto _websocket_init_fail); | ||
|
||
client->config = calloc(1, sizeof(websocket_config_storage_t)); | ||
client->config = heap_caps_calloc(1, sizeof(websocket_config_storage_t),memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config, goto _websocket_init_fail); | ||
|
||
if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) { | ||
|
@@ -661,7 +707,7 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie | |
} | ||
|
||
if (!config->disable_auto_reconnect && config->reconnect_timeout_ms <= 0) { | ||
client->wait_timeout_ms = WEBSOCKET_RECONNECT_TIMEOUT_MS; | ||
client->wait_timeout_ms = WEBSOCKET_RECONNECT_TIMEOUT_MS; | ||
ESP_LOGW(TAG, "`reconnect_timeout_ms` is not set, or it is less than or equal to zero, using default time out %d (milliseconds)", WEBSOCKET_RECONNECT_TIMEOUT_MS); | ||
} else { | ||
client->wait_timeout_ms = config->reconnect_timeout_ms; | ||
|
@@ -708,11 +754,11 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie | |
client->errormsg_buffer = NULL; | ||
client->errormsg_size = 0; | ||
#ifndef CONFIG_ESP_WS_CLIENT_ENABLE_DYNAMIC_BUFFER | ||
client->rx_buffer = malloc(buffer_size); | ||
client->rx_buffer = heap_caps_malloc(buffer_size,client->memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->rx_buffer, { | ||
goto _websocket_init_fail; | ||
}); | ||
client->tx_buffer = malloc(buffer_size); | ||
client->tx_buffer = heap_caps_malloc(buffer_size,client->memory_type); | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, client->tx_buffer, { | ||
goto _websocket_init_fail; | ||
}); | ||
|
@@ -842,7 +888,7 @@ esp_err_t esp_websocket_client_append_header(esp_websocket_client_handle_t clien | |
|
||
// If no previous headers exist | ||
if (cfg->headers == NULL) { | ||
cfg->headers = (char *)malloc(len); | ||
cfg->headers = (char *)heap_caps_malloc(len,client->memory_type); | ||
if (cfg->headers == NULL) { | ||
ESP_LOGE(TAG, "Failed to allocate..."); | ||
return ESP_ERR_NO_MEM; | ||
|
@@ -856,7 +902,7 @@ esp_err_t esp_websocket_client_append_header(esp_websocket_client_handle_t clien | |
size_t new_len = current_len + len; | ||
|
||
// Allocate memory for new headers | ||
char *new_headers = (char *)malloc(new_len); | ||
char *new_headers = (char *)heap_caps_malloc(new_len,client->memory_type); | ||
if (new_headers == NULL) { | ||
ESP_LOGE(TAG, "Failed to allocate..."); | ||
return ESP_ERR_NO_MEM; | ||
|
@@ -1088,7 +1134,8 @@ static void esp_websocket_client_task(void *pv) | |
if (client->selected_for_destroying == true) { | ||
destroy_and_free_resources(client); | ||
} | ||
vTaskDelete(NULL); | ||
vTaskDeleteWithCaps(client->task_handle); | ||
|
||
} | ||
|
||
esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client) | ||
|
@@ -1105,12 +1152,72 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client) | |
return ESP_FAIL; | ||
} | ||
|
||
if (xTaskCreate(esp_websocket_client_task, client->config->task_name ? client->config->task_name : "websocket_task", | ||
client->config->task_stack, client, client->config->task_prio, &client->task_handle) != pdTRUE) { | ||
ESP_LOGE(TAG, "Error create websocket task"); | ||
return ESP_FAIL; | ||
} | ||
xEventGroupClearBits(client->status_bits, STOPPED_BIT | CLOSE_FRAME_SENT_BIT); | ||
if (!client->core) { | ||
// No core affinity | ||
if (!client->memory_type) { | ||
// Default memory | ||
if (xTaskCreatePinnedToCoreWithCaps(esp_websocket_client_task, | ||
client->config->task_name ? client->config->task_name : "websocket_task", | ||
client->config->task_stack, | ||
client, | ||
client->config->task_prio, | ||
&client->task_handle, | ||
tskNO_AFFINITY, | ||
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) != pdTRUE) { | ||
ESP_LOGE(TAG, "Failed to create websocket task without core affinity using default memory"); | ||
return ESP_FAIL; | ||
} | ||
ESP_LOGD(TAG, "Websocket task created without core affinity using default memory"); | ||
} else { | ||
// Specific memory type without core affinity | ||
if (xTaskCreatePinnedToCoreWithCaps(esp_websocket_client_task, | ||
client->config->task_name ? client->config->task_name : "websocket_task", | ||
client->config->task_stack, | ||
client, | ||
client->config->task_prio, | ||
&client->task_handle, | ||
tskNO_AFFINITY, | ||
client->memory_type) != pdTRUE) { | ||
ESP_LOGE(TAG, "Failed to create websocket task without core affinity using specified memory type"); | ||
return ESP_FAIL; | ||
} | ||
ESP_LOGD(TAG, "Websocket task created without core affinity using specified memory type"); | ||
} | ||
} else { | ||
// With core affinity | ||
if (!client->memory_type) { | ||
// Default memory with core affinity | ||
if (xTaskCreatePinnedToCoreWithCaps(esp_websocket_client_task, | ||
client->config->task_name ? client->config->task_name : "websocket_task", | ||
client->config->task_stack, | ||
client, | ||
client->config->task_prio, | ||
&client->task_handle, | ||
client->core, | ||
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) != pdTRUE) { | ||
ESP_LOGE(TAG, "Failed to create websocket task with core affinity using default memory (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)"); | ||
return ESP_FAIL; | ||
} | ||
ESP_LOGD(TAG, "Websocket task created with core affinity using default memory (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)"); | ||
} else { | ||
// Specific memory type with core affinity | ||
if (xTaskCreatePinnedToCoreWithCaps(esp_websocket_client_task, | ||
client->config->task_name ? client->config->task_name : "websocket_task", | ||
client->config->task_stack, | ||
client, | ||
client->config->task_prio, | ||
&client->task_handle, | ||
client->core, | ||
client->memory_type) != pdTRUE) { | ||
ESP_LOGE(TAG, "Failed to create websocket task with core affinity using specified memory type"); | ||
return ESP_FAIL; | ||
} | ||
ESP_LOGD(TAG, "Websocket task created with core affinity using specified memory type (%d)", client->memory_type); | ||
} | ||
} | ||
|
||
|
||
xEventGroupClearBits(client->status_bits, STOPPED_BIT | CLOSE_FRAME_SENT_BIT); | ||
return ESP_OK; | ||
} | ||
|
||
|
@@ -1143,7 +1250,7 @@ static int esp_websocket_client_send_close(esp_websocket_client_handle_t client, | |
uint8_t *close_status_data = NULL; | ||
// RFC6455#section-5.5.1: The Close frame MAY contain a body (indicated by total_len >= 2) | ||
if (total_len >= 2) { | ||
close_status_data = calloc(1, total_len); | ||
close_status_data = heap_caps_calloc(1, total_len,client->memory_type);; | ||
ESP_WS_CLIENT_MEM_CHECK(TAG, close_status_data, return -1); | ||
// RFC6455#section-5.5.1: The first two bytes of the body MUST be a 2-byte representing a status | ||
uint16_t *code_network_order = (uint16_t *) close_status_data; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,6 +127,8 @@ typedef struct { | |
int network_timeout_ms; /*!< Abort network operation if it is not completed after this value, in milliseconds (defaults to 10s) */ | ||
size_t ping_interval_sec; /*!< Websocket ping interval, defaults to 10 seconds if not set */ | ||
struct ifreq *if_name; /*!< The name of interface for data to go through. Use the default interface without setting */ | ||
int memory_type; /*!< The name of memory region type to be used in the heap_caps_malloc / heap_caps_calloc */ | ||
esp_transport_handle_t ext_transport; /*!< External WebSocket tcp_transport handle to the client; or if null, the client will create its own transport handle. */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to be some kind of leftover. |
||
} esp_websocket_client_config_t; | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to modify, in fact you should not, the Changelog.
As stated in the contribution guide, you only need to follow the conventional commits, and we take care of the release notes when releasing a new version of the component.