feat: added thread-safety

This commit is contained in:
aZholtikov committed 2026-06-15 18:52:56 +03:00
1 parent a22ec17aa1
commit d572e79a5b
6 files changed
+47 -17

No files matched your search

+4 -3
View File
@@ -30,7 +30,7 @@ The component is designed specifically for ESP32 microcontrollers and uses ESP-I
4. **Maximum Capacity**: Up to 65,535 elements (16-bit index limit) 4. **Maximum Capacity**: Up to 65,535 elements (16-bit index limit)
5. **ESP-IDF Optimized**: Uses heap_caps functions for memory allocation with memory caps 5. **ESP-IDF Optimized**: Uses heap_caps functions for memory allocation with memory caps
6. **Error Handling**: Comprehensive error checking with detailed logging 6. **Error Handling**: Comprehensive error checking with detailed logging
7. **Thread-Safe**: Not thread-safe by default (can be made thread-safe with external mutex) 7. **Thread-Safe**: Thread-safe (uses FreeRTOS mutex)
8. **Minimal Overhead**: Low memory and CPU overhead 8. **Minimal Overhead**: Low memory and CPU overhead
--- ---
@@ -71,6 +71,7 @@ typedef struct
uint16_t size; // Number of items in the vector uint16_t size; // Number of items in the vector
uint16_t unit; // Vector item size (in bytes) uint16_t unit; // Vector item size (in bytes)
bool is_initialized; // Vector initialization status flag bool is_initialized; // Vector initialization status flag
SemaphoreHandle_t mutex; // FreeRTOS mutex for thread safety
} zh_vector_t; } zh_vector_t;
``` ```
@@ -337,7 +338,7 @@ void app_main(void)
| **Index Type** | uint16_t (16-bit) | | **Index Type** | uint16_t (16-bit) |
| **Memory Management** | heap_caps_calloc, heap_caps_realloc, heap_caps_free | | **Memory Management** | heap_caps_calloc, heap_caps_realloc, heap_caps_free |
| **Memory Caps** | MALLOC_CAP_8BIT | | **Memory Caps** | MALLOC_CAP_8BIT |
| **Thread Safety** | Not thread-safe | | **Thread Safety** | Thread-safe (uses FreeRTOS mutex) |
| **ESP-IDF Version** | >= 5.0 | | **ESP-IDF Version** | >= 5.0 |
| **Platform** | ESP32 series | | **Platform** | ESP32 series |
| **Language** | C (C99) | | **Language** | C (C99) |
@@ -404,4 +405,4 @@ limitations under the License.
--- ---
*Generated for zh_vector v1.2.0* *Generated for zh_vector v1.3.0*
+4 -3
View File
@@ -30,7 +30,7 @@
4. **Максимальная емкость**: До 65 535 элементов (ограничение 16-битного индекса) 4. **Максимальная емкость**: До 65 535 элементов (ограничение 16-битного индекса)
5. **Оптимизировано под ESP-IDF**: Использует функции heap_caps для выделения памяти с параметрами памяти 5. **Оптимизировано под ESP-IDF**: Использует функции heap_caps для выделения памяти с параметрами памяти
6. **Обработка ошибок**: Комплексная проверка ошибок с детальным логированием 6. **Обработка ошибок**: Комплексная проверка ошибок с детальным логированием
7. **Потокобезопасность**: Не является потокобезопасным по умолчанию (можно сделать потокобезопасным с внешним мьютексом) 7. **Потокобезопасность**: Является потокобезопасным (использует FreeRTOS mutex)
8. **Минимальные накладные расходы**: Низкие накладные расходы на память и процессор 8. **Минимальные накладные расходы**: Низкие накладные расходы на память и процессор
--- ---
@@ -71,6 +71,7 @@ typedef struct
uint16_t size; // Количество элементов в векторе uint16_t size; // Количество элементов в векторе
uint16_t unit; // Размер элемента вектора (в байтах) uint16_t unit; // Размер элемента вектора (в байтах)
bool is_initialized; // Флаг состояния инициализации вектора bool is_initialized; // Флаг состояния инициализации вектора
SemaphoreHandle_t mutex; // FreeRTOS mutex для потокобезопасности
} zh_vector_t; } zh_vector_t;
``` ```
@@ -337,7 +338,7 @@ void app_main(void)
| **Тип индекса** | uint16_t (16-бит) | | **Тип индекса** | uint16_t (16-бит) |
| **Управление памятью** | heap_caps_calloc, heap_caps_realloc, heap_caps_free | | **Управление памятью** | heap_caps_calloc, heap_caps_realloc, heap_caps_free |
| **Параметры памяти** | MALLOC_CAP_8BIT | | **Параметры памяти** | MALLOC_CAP_8BIT |
| **Потокобезопасность** | Не является потокобезопасным | | **Потокобезопасность** | Является потокобезопасным (использует FreeRTOS mutex) |
| **Версия ESP-IDF** | >= 5.0 | | **Версия ESP-IDF** | >= 5.0 |
| **Платформа** | Серия ESP32 | | **Платформа** | Серия ESP32 |
| **Язык** | C (C99) | | **Язык** | C (C99) |
@@ -404,4 +405,4 @@ void app_main(void)
--- ---
*Сгенерировано для zh_vector v1.2.0* *Сгенерировано для zh_vector v1.3.0*
+1 -1
View File
@@ -1,6 +1,6 @@
description: ESP32 ESP-IDF component for vector (dynamic array) description: ESP32 ESP-IDF component for vector (dynamic array)
url: https://github.com/aZholtikov/zh_vector url: https://github.com/aZholtikov/zh_vector
version: "1.2.0" version: "1.3.0"
license: "Apache-2.0" license: "Apache-2.0"
maintainers: maintainers:
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>" - "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
+3
View File
@@ -8,6 +8,8 @@
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
@@ -24,6 +26,7 @@ extern "C"
uint16_t size; /*!< Number of items in the vector. */ uint16_t size; /*!< Number of items in the vector. */
uint16_t unit; /*!< Vector item size. */ uint16_t unit; /*!< Vector item size. */
bool is_initialized; /*!< Vector initialization status flag. */ bool is_initialized; /*!< Vector initialization status flag. */
SemaphoreHandle_t mutex; /*!< FreeRTOS mutex. */
} zh_vector_t; } zh_vector_t;
/** /**
+1 -1
View File
@@ -1 +1 @@
1.2.0 1.3.0
+34 -9
View File
@@ -20,6 +20,8 @@ esp_err_t zh_vector_init(zh_vector_t *vector, uint16_t unit, ...)
ZH_LOGI("Vector initialization begin."); ZH_LOGI("Vector initialization begin.");
ZH_ERROR_CHECK(vector != NULL && unit != 0, ESP_ERR_INVALID_ARG, NULL, "Vector initialization failed. Invalid argument."); ZH_ERROR_CHECK(vector != NULL && unit != 0, ESP_ERR_INVALID_ARG, NULL, "Vector initialization failed. Invalid argument.");
ZH_ERROR_CHECK(vector->is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "Vector initialization failed. Vector is already initialized."); ZH_ERROR_CHECK(vector->is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "Vector initialization failed. Vector is already initialized.");
vector->mutex = xSemaphoreCreateMutex();
ZH_ERROR_CHECK(vector->mutex != NULL, ESP_ERR_NO_MEM, NULL, "Vector initialization failed. Failed to create mutex.");
vector->items = NULL; vector->items = NULL;
vector->capacity = 0; vector->capacity = 0;
vector->size = 0; vector->size = 0;
@@ -34,6 +36,7 @@ esp_err_t zh_vector_free(zh_vector_t *vector)
ZH_LOGI("Vector deletion begin."); ZH_LOGI("Vector deletion begin.");
ZH_ERROR_CHECK(vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Vector deletion failed. Invalid argument."); ZH_ERROR_CHECK(vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Vector deletion failed. Invalid argument.");
ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Vector deletion fail. Vector not initialized."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Vector deletion fail. Vector not initialized.");
ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Vector deletion fail. Failed to acquire mutex.");
for (uint16_t i = 0; i < vector->size; ++i) for (uint16_t i = 0; i < vector->size; ++i)
{ {
if (vector->items[i] != NULL) if (vector->items[i] != NULL)
@@ -46,6 +49,8 @@ esp_err_t zh_vector_free(zh_vector_t *vector)
vector->items = NULL; vector->items = NULL;
vector->size = 0; vector->size = 0;
vector->capacity = 0; vector->capacity = 0;
vSemaphoreDelete(vector->mutex);
vector->mutex = NULL;
vector->is_initialized = false; vector->is_initialized = false;
ZH_LOGI("Vector deletion success."); ZH_LOGI("Vector deletion success.");
return ESP_OK; return ESP_OK;
@@ -56,8 +61,11 @@ esp_err_t zh_vector_get_size(zh_vector_t *vector)
ZH_LOGI("Getting vector size begin."); ZH_LOGI("Getting vector size begin.");
ZH_ERROR_CHECK(vector != NULL, ESP_FAIL, NULL, "Getting vector size fail. Invalid argument."); ZH_ERROR_CHECK(vector != NULL, ESP_FAIL, NULL, "Getting vector size fail. Invalid argument.");
ZH_ERROR_CHECK(vector->is_initialized == true, ESP_FAIL, NULL, "Getting vector size fail. Vector not initialized."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_FAIL, NULL, "Getting vector size fail. Vector not initialized.");
ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Getting vector size fail. Failed to acquire mutex.");
size_t size = vector->size;
xSemaphoreGive(vector->mutex);
ZH_LOGI("Getting vector size success."); ZH_LOGI("Getting vector size success.");
return vector->size; return (esp_err_t)size;
} }
esp_err_t zh_vector_push_front(zh_vector_t *vector, void *item) // -V2008 esp_err_t zh_vector_push_front(zh_vector_t *vector, void *item) // -V2008
@@ -65,18 +73,20 @@ esp_err_t zh_vector_push_front(zh_vector_t *vector, void *item) // -V2008
ZH_LOGI("Adding item to beginning of vector begin."); ZH_LOGI("Adding item to beginning of vector begin.");
ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to beginning of vector fail. Invalid argument."); ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to beginning of vector fail. Invalid argument.");
ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to beginning of vector fail. Vector not initialized."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to beginning of vector fail. Vector not initialized.");
ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Adding item to beginning of vector fail. Failed to acquire mutex.");
if (vector->capacity == vector->size) if (vector->capacity == vector->size)
{ {
ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, NULL, "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap."); ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap.");
} }
for (uint16_t i = vector->size; i > 0; --i) for (uint16_t i = vector->size; i > 0; --i)
{ {
vector->items[i] = vector->items[i - 1]; vector->items[i] = vector->items[i - 1];
} }
vector->items[0] = heap_caps_calloc(1, vector->unit, MALLOC_CAP_8BIT); vector->items[0] = heap_caps_calloc(1, vector->unit, MALLOC_CAP_8BIT);
ZH_ERROR_CHECK(vector->items[0] != NULL, ESP_ERR_NO_MEM, NULL, "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap."); ZH_ERROR_CHECK(vector->items[0] != NULL, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap.");
memcpy(vector->items[0], item, vector->unit); memcpy(vector->items[0], item, vector->unit);
vector->size++; vector->size++;
xSemaphoreGive(vector->mutex);
ZH_LOGI("Adding item to beginning of vector success."); ZH_LOGI("Adding item to beginning of vector success.");
return ESP_OK; return ESP_OK;
} }
@@ -86,31 +96,35 @@ esp_err_t zh_vector_push_back(zh_vector_t *vector, void *item) // -V2008
ZH_LOGI("Adding item to vector begin."); ZH_LOGI("Adding item to vector begin.");
ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to vector fail. Invalid argument."); ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to vector fail. Invalid argument.");
ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to vector fail. Vector not initialized."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to vector fail. Vector not initialized.");
ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Adding item to vector fail. Failed to acquire mutex.");
if (vector->capacity == vector->size) if (vector->capacity == vector->size)
{ {
ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, NULL, "Adding item to vector fail. Memory allocation fail or no free memory in the heap."); ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to vector fail. Memory allocation fail or no free memory in the heap.");
} }
uint16_t idx = vector->size; uint16_t idx = vector->size;
vector->items[idx] = heap_caps_calloc(1, vector->unit, MALLOC_CAP_8BIT); vector->items[idx] = heap_caps_calloc(1, vector->unit, MALLOC_CAP_8BIT);
ZH_ERROR_CHECK(vector->items[idx] != NULL, ESP_ERR_NO_MEM, NULL, "Adding item to vector fail. Memory allocation fail or no free memory in the heap."); ZH_ERROR_CHECK(vector->items[idx] != NULL, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to vector fail. Memory allocation fail or no free memory in the heap.");
memcpy(vector->items[idx], item, vector->unit); memcpy(vector->items[idx], item, vector->unit);
vector->size++; vector->size++;
xSemaphoreGive(vector->mutex);
ZH_LOGI("Adding item to vector success."); ZH_LOGI("Adding item to vector success.");
return ESP_OK; return ESP_OK;
} }
esp_err_t zh_vector_change_item(zh_vector_t *vector, uint16_t index, void *item) esp_err_t zh_vector_change_item(zh_vector_t *vector, uint16_t index, void *item) // -V2008
{ {
ZH_LOGI("Changing item in vector begin."); ZH_LOGI("Changing item in vector begin.");
ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Changing item in vector fail. Invalid argument."); ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Changing item in vector fail. Invalid argument.");
ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Changing item in vector fail. Vector not initialized."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Changing item in vector fail. Vector not initialized.");
ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Changing item in vector fail. Failed to acquire mutex.");
ZH_ERROR_CHECK(index < vector->size, ESP_FAIL, NULL, "Changing item in vector fail. Index does not exist."); ZH_ERROR_CHECK(index < vector->size, ESP_FAIL, NULL, "Changing item in vector fail. Index does not exist.");
memcpy(vector->items[index], item, vector->unit); memcpy(vector->items[index], item, vector->unit);
xSemaphoreGive(vector->mutex);
ZH_LOGI("Changing item in vector success."); ZH_LOGI("Changing item in vector success.");
return ESP_OK; return ESP_OK;
} }
void *zh_vector_get_item(zh_vector_t *vector, uint16_t index) void *zh_vector_get_item(zh_vector_t *vector, uint16_t index) // -V2008
{ {
ZH_LOGI("Getting item from vector begin."); ZH_LOGI("Getting item from vector begin.");
if (vector == NULL) if (vector == NULL)
@@ -123,13 +137,22 @@ void *zh_vector_get_item(zh_vector_t *vector, uint16_t index)
ZH_LOGE("Getting item from vector fail. Vector not initialized.", ESP_ERR_INVALID_STATE); ZH_LOGE("Getting item from vector fail. Vector not initialized.", ESP_ERR_INVALID_STATE);
return NULL; return NULL;
} }
if (xSemaphoreTake(vector->mutex, portMAX_DELAY) != pdTRUE)
{
ZH_LOGE("Getting item from vector fail. Failed to acquire mutex.", ESP_FAIL);
return NULL;
}
void *item = NULL;
if (index >= vector->size) if (index >= vector->size)
{ {
xSemaphoreGive(vector->mutex);
ZH_LOGE("Getting item from vector fail. Index does not exist.", ESP_FAIL); ZH_LOGE("Getting item from vector fail. Index does not exist.", ESP_FAIL);
return NULL; return NULL;
} }
item = vector->items[index];
xSemaphoreGive(vector->mutex);
ZH_LOGI("Getting item from vector success."); ZH_LOGI("Getting item from vector success.");
return vector->items[index]; return item;
} }
esp_err_t zh_vector_delete_item(zh_vector_t *vector, uint16_t index) // -V2008 esp_err_t zh_vector_delete_item(zh_vector_t *vector, uint16_t index) // -V2008
@@ -137,6 +160,7 @@ esp_err_t zh_vector_delete_item(zh_vector_t *vector, uint16_t index) // -V2008
ZH_LOGI("Deleting item in vector begin."); ZH_LOGI("Deleting item in vector begin.");
ZH_ERROR_CHECK(vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Deleting item in vector fail. Invalid argument."); ZH_ERROR_CHECK(vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Deleting item in vector fail. Invalid argument.");
ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Deleting item in vector fail. Vector not initialized."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Deleting item in vector fail. Vector not initialized.");
ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Deleting item in vector fail. Failed to acquire mutex.");
ZH_ERROR_CHECK(index < vector->size, ESP_FAIL, NULL, "Deleting item in vector fail. Index does not exist."); ZH_ERROR_CHECK(index < vector->size, ESP_FAIL, NULL, "Deleting item in vector fail. Index does not exist.");
void *freed_item = vector->items[index]; void *freed_item = vector->items[index];
for (uint16_t i = index; i < (vector->size - 1); ++i) for (uint16_t i = index; i < (vector->size - 1); ++i)
@@ -147,8 +171,9 @@ esp_err_t zh_vector_delete_item(zh_vector_t *vector, uint16_t index) // -V2008
vector->items[--vector->size] = NULL; vector->items[--vector->size] = NULL;
if (vector->capacity > vector->size) if (vector->capacity > vector->size)
{ {
ZH_ERROR_CHECK(_resize(vector, vector->size) == ESP_OK, ESP_ERR_NO_MEM, NULL, "Deleting item in vector fail. Memory allocation fail or no free memory in the heap."); ZH_ERROR_CHECK(_resize(vector, vector->size) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Deleting item in vector fail. Memory allocation fail or no free memory in the heap.");
} }
xSemaphoreGive(vector->mutex);
ZH_LOGI("Deleting item in vector success."); ZH_LOGI("Deleting item in vector success.");
return ESP_OK; return ESP_OK;
} }