Локальное хранилище потока (Thread Local Storage, или TLS) позволяет разработчику приложения сохранять значения внутри блока управления задачей (task control block, TCB) делая это значение специфичным (локальным) для самой задачи, и позволяет каждой задаче иметь свое собственное уникальное значение.
TLS наиболее часто используется для сохранения значений, которые однопоточная программа иначе хранила бы в глобальной переменной. Например, многие библиотеки подключают глобальную переменную с именем errno. Если библиотечная функция вернет состояние ошибки в вызывающую функцию, то вызывающая функция может проверить значение errno, чтобы определить, в чем была ошибка. В однопоточном приложении достаточно декларировать errno как глобальную переменную, но в многопоточном приложении каждый поток (задача task) должен иметь собственное уникальное значение errno - иначе одна задача может прочитать значение errno, которое было предназначено для другой задачи.
Thread Local Storage Pointers. FreeRTOS предоставляет разработчику приложения гибкий механизм TLS через использование указателей на локальное хранилище состояния потока (thread local storage pointers).
Константа конфигурации времени компиляции configNUM_THREAD_LOCAL_STORAGE_POINTERS содержит размерность массива пустых указателей (void*) для каждой задачи. API-функция vTaskSetThreadLocalStoragePointer() используется для установки значения в массиве указателей на void, и API-функция pvTaskGetThreadLocalStoragePointer() используется для чтения значения из этого массива.
Thread Local Integers. Значения, у которых размер меньше или равен указателю на void, можно напрямую сохранять в массив локального хранилища указателей. Например, если sizeof(void*) равно 4, то в переменной массива указателей на void можно сохранить 32-разрядное значение, используя простое приведения типа (type cast), чтобы избежать предупреждений компилятора. Однако, если sizeof(void*) равно 2, то напрямую можно сохранить только 16-разрядное значение.
// Пример прямого сохранения и извлечения 32-разрядных значений, хранящихся
// в массиве указателей на void, который находится в локальном хранилище
// задачи (TLS).
uint32_t ulVariable;
/* Запись 32-битного значения 0x12345678 напрямую по индексу 1 в массив указателей,
находящийся в TLS. Передача NULL в качестве дескриптора задачи (task handle)
дает эффект записи в массив TLS вызывающей задачи. */
vTaskSetThreadLocalStoragePointer( NULL, /* Task handle. */
1, /* Индекс в массиве. */
( void * ) 0x12345678 );
/* Сохранение 32-битной переменной ulVariable по индексу 0 массива указателей
TLS вызывающей задачи. */
ulVariable = ERROR_CODE;
vTaskSetThreadLocalStoragePointer( NULL, /* Task handle. */
0, /* Индекс в массиве. */
( void * ) ulVariable );
/* Чтение значения, сохраненного по индексу 5 массива указателей
TLS вызывающей задачи в переменную ulVariable. */
ulVariable = ( uint32_t ) pvTaskGetThreadLocalStoragePointer( NULL, 5 );
Thread Local Structures. В предыдущем примере был показан прямой доступ к значениям, которые хранятся в массиве, размещенном в TLS. Следующий пример демонстрирует использование значения в массиве TLS как указателя на структуру, память для которой выделена где-то в другом месте.
// Пример сохранения указателя на структуру в массив указателей на void,
// который находится в TLS вызывающей задачи. Сама структура, на которую
// ссылается указатель, находится где-то в другой памяти.
typedef struct
{
uint32_t ulValue1;
uint32_t ulValue2;
}xExampleStruct;
xExampleStruct *pxStruct;
/* Создание структуры для использования этой задачей. */
pxStruct = pvPortMalloc( sizeof( xExampleStruct ) );
/* Установка полей структуры. */
pxStruct->ulValue1 = 0;
pxStruct->ulValue2 = 1;
/* Сохранение указателя на структуру по индексу 0 массива указателей
в TLS вызывающей задачи. */
vTaskSetThreadLocalStoragePointer( NULL, /* Task handle. */
0, /* Индекс в массиве. */
( void * ) pxStruct );
/* Нахождение структуры, используемой вызывающей задачей путем
его чтения из массива в TLS по индексу 0. */
pxStruct = ( xExampleStruct * ) pvTaskGetThreadLocalStoragePointer( NULL, 0 );
[Ссылки]
1. Thread Local Storage Pointers site:freertos.org. 2. FreeRTOS: оповещения задач. 3. FreeRTOS: семафоры со счетчиком и оповещения задач. 4. ESP-IDF FreeRTOS Task API. 5. FreeRTOS: xTaskNotifyWait / xTaskNotifyWaitIndexed. |