mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-04 21:24:32 +02:00
freertos: Fix queue set tests for SMP FreeRTOS
The "Test Queue sets thread safety" tests relied on IDF FreeRTOS specific scheduling behavior, thus resulting in the test getting stuck when running with SMP FreeRTOS. This commit updates and refactors the tests to work on both IDF and SMP FreeRTOS.
This commit is contained in:
@@ -10,134 +10,178 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/idf_additions.h"
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
#define QUEUE_LEN 4
|
||||||
|
|
||||||
|
static void allocate_resources(int num_queues, int queue_len, QueueHandle_t *queue_list_ret, QueueSetHandle_t *queue_set_ret)
|
||||||
|
{
|
||||||
|
// Create queue set
|
||||||
|
*queue_set_ret = xQueueCreateSet(num_queues * queue_len);
|
||||||
|
TEST_ASSERT_NOT_EQUAL(NULL, *queue_set_ret);
|
||||||
|
// Create queues and add them to the queue set
|
||||||
|
for (int i = 0; i < num_queues; i++) {
|
||||||
|
queue_list_ret[i] = xQueueCreate(queue_len, sizeof(BaseType_t));
|
||||||
|
TEST_ASSERT_NOT_EQUAL(NULL, queue_list_ret[i]);
|
||||||
|
TEST_ASSERT_EQUAL(pdPASS, xQueueAddToSet(queue_list_ret[i], *queue_set_ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_resources(int num_queues, QueueHandle_t *queue_list, QueueSetHandle_t queue_set)
|
||||||
|
{
|
||||||
|
// Remove queues form queue set and delete the queues
|
||||||
|
for (int i = 0; i < num_queues; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(pdPASS, xQueueRemoveFromSet(queue_list[i], queue_set));
|
||||||
|
vQueueDelete(queue_list[i]);
|
||||||
|
}
|
||||||
|
vQueueDelete(queue_set);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Basic queue set tests. Multiple queues are added to a queue set then each
|
Test queue sets basic
|
||||||
* queue is filled in a sequential order. The members returned from the queue
|
|
||||||
* set must adhered to the order in which the queues were filled.
|
|
||||||
*/
|
|
||||||
#define NO_OF_QUEUES 5
|
|
||||||
#define QUEUE_LEN 4
|
|
||||||
#define ITEM_SIZE sizeof(uint32_t)
|
|
||||||
|
|
||||||
static QueueHandle_t handles[NO_OF_QUEUES];
|
Purpose:
|
||||||
static QueueSetHandle_t set_handle;
|
- Test that queue set works as expected
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Create NUM_QUEUES queues and add them to the same queue set
|
||||||
|
- Fill each queue sequentially with QUEUE_LEN items
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
- Each call to xQueueSend() should generate a member in the queue set
|
||||||
|
- The order of the members should match the order in which xQueueSend() was called
|
||||||
|
- The item sent by the xQueueSend() is correct when read
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NUM_QUEUES 5
|
||||||
|
|
||||||
TEST_CASE("Test Queue sets", "[freertos]")
|
TEST_CASE("Test Queue sets", "[freertos]")
|
||||||
{
|
{
|
||||||
//Create queue set, queues, and add queues to queue set
|
// Create queues and queue set
|
||||||
set_handle = xQueueCreateSet(NO_OF_QUEUES * QUEUE_LEN);
|
QueueHandle_t queues[NUM_QUEUES];
|
||||||
for (int i = 0; i < NO_OF_QUEUES; i++) {
|
QueueSetHandle_t queue_set;
|
||||||
handles[i] = xQueueCreate(QUEUE_LEN, ITEM_SIZE);
|
allocate_resources(NUM_QUEUES, QUEUE_LEN, queues, &queue_set);
|
||||||
TEST_ASSERT_MESSAGE(handles[i] != NULL, "Failed to create queue");
|
|
||||||
TEST_ASSERT_MESSAGE(xQueueAddToSet(handles[i], set_handle) == pdPASS, "Failed to add to queue set");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Fill queue set via filling each queue
|
// Fill each queue sequentially with QUEUE_LEN items
|
||||||
for (int i = 0; i < NO_OF_QUEUES; i++) {
|
for (int i = 0; i < NUM_QUEUES; i++) {
|
||||||
for (int j = 0; j < QUEUE_LEN; j++) {
|
for (int j = 0; j < QUEUE_LEN; j++) {
|
||||||
uint32_t item_num = (i * QUEUE_LEN) + j;
|
BaseType_t item = j;
|
||||||
TEST_ASSERT_MESSAGE(xQueueSendToBack(handles[i], &item_num, portMAX_DELAY) == pdTRUE, "Failed to send to queue");
|
TEST_ASSERT_EQUAL(pdTRUE, xQueueSend(queues[i], &item, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check queue set is notified in correct order
|
for (int i = 0; i < NUM_QUEUES; i++) {
|
||||||
for (int i = 0; i < NO_OF_QUEUES; i++) {
|
|
||||||
for (int j = 0; j < QUEUE_LEN; j++) {
|
for (int j = 0; j < QUEUE_LEN; j++) {
|
||||||
QueueSetMemberHandle_t member = xQueueSelectFromSet(set_handle, portMAX_DELAY);
|
// Check the queue set member
|
||||||
TEST_ASSERT_EQUAL_MESSAGE(handles[i], member, "Incorrect queue set member returned");
|
QueueSetMemberHandle_t member = xQueueSelectFromSet(queue_set, 0);
|
||||||
uint32_t item;
|
TEST_ASSERT_EQUAL(queues[i], member);
|
||||||
xQueueReceive((QueueHandle_t)member, &item, 0);
|
// Check the queue's items
|
||||||
TEST_ASSERT_EQUAL_MESSAGE(((i * QUEUE_LEN) + j), item, "Incorrect item value");
|
BaseType_t item;
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(member, &item, 0));
|
||||||
|
TEST_ASSERT_EQUAL(j, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check that there are no more members
|
||||||
|
TEST_ASSERT_EQUAL(NULL, xQueueSelectFromSet(queue_set, 0));
|
||||||
|
|
||||||
//Remove queues from queue set and delete queues
|
// Cleanup queues and queue set
|
||||||
for (int i = 0; i < NO_OF_QUEUES; i++) {
|
free_resources(NUM_QUEUES, queues, queue_set);
|
||||||
TEST_ASSERT_MESSAGE(xQueueRemoveFromSet(handles[i], set_handle), "Failed to remove from queue set");
|
|
||||||
vQueueDelete(handles[i]);
|
|
||||||
}
|
|
||||||
vQueueDelete(set_handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Queue set thread safety test. Test the SMP thread safety by adding two queues
|
|
||||||
* to a queue set and have a task on each core send to the queues simultaneously.
|
|
||||||
* Check returned queue set members are valid.
|
|
||||||
*/
|
|
||||||
#ifndef CONFIG_FREERTOS_UNICORE
|
#ifndef CONFIG_FREERTOS_UNICORE
|
||||||
static volatile bool sync_flags[portNUM_PROCESSORS];
|
/*
|
||||||
static SemaphoreHandle_t sync_sem;
|
Test queue set SMP thread safety
|
||||||
|
|
||||||
static void send_task(void *arg)
|
Purpose:
|
||||||
|
- Test that queue set works when being used from different cores simultaneously
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Create a queue for each core and add them to the same queue set
|
||||||
|
- Create a task on each core to send QUEUE_LEN items to their assigned queue
|
||||||
|
- Synchronize the tasks so that the start sending at the same time
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
- Each call to xQueueSend() should generate a member in the queue set
|
||||||
|
- The item sent by the xQueueSend() is correct when read
|
||||||
|
*/
|
||||||
|
|
||||||
|
static volatile bool start_other_cores;
|
||||||
|
static SemaphoreHandle_t done_sem = NULL;
|
||||||
|
|
||||||
|
static void send_func(void *arg)
|
||||||
{
|
{
|
||||||
QueueHandle_t queue = (QueueHandle_t)arg;
|
QueueHandle_t queue = (QueueHandle_t)arg;
|
||||||
|
BaseType_t core_id = xPortGetCoreID();
|
||||||
//Wait until task on the other core starts running
|
if (core_id == 0) {
|
||||||
xSemaphoreTake(sync_sem, portMAX_DELAY);
|
// We are core 0. Trigger the other cores to start
|
||||||
sync_flags[xPortGetCoreID()] = true;
|
start_other_cores = true;
|
||||||
while (!sync_flags[!xPortGetCoreID()]) {
|
} else {
|
||||||
;
|
// Wait to be started by main core
|
||||||
}
|
while (!start_other_cores) {
|
||||||
|
;
|
||||||
//Fill queue
|
|
||||||
for (int i = 0; i < QUEUE_LEN; i++) {
|
|
||||||
uint32_t item = i;
|
|
||||||
xQueueSendToBack(queue, &item, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
xSemaphoreGive(sync_sem);
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Test Queue sets thread safety", "[freertos]")
|
|
||||||
{
|
|
||||||
//Create queue set, queues, and a send task on each core
|
|
||||||
sync_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
|
|
||||||
QueueHandle_t queue_handles[portNUM_PROCESSORS];
|
|
||||||
QueueSetHandle_t queueset_handle = xQueueCreateSet(portNUM_PROCESSORS * QUEUE_LEN);
|
|
||||||
for (int i = 0; i < portNUM_PROCESSORS; i++) {
|
|
||||||
sync_flags[i] = false;
|
|
||||||
queue_handles[i] = xQueueCreate(QUEUE_LEN, ITEM_SIZE);
|
|
||||||
TEST_ASSERT_MESSAGE(xQueueAddToSet(queue_handles[i], queueset_handle) == pdPASS, "Failed to add to queue set");
|
|
||||||
xTaskCreatePinnedToCore(send_task, "send", 2048, (void *)queue_handles[i], 10, NULL, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start both send tasks
|
|
||||||
portDISABLE_INTERRUPTS();
|
|
||||||
for (int i = 0; i < portNUM_PROCESSORS; i++) {
|
|
||||||
xSemaphoreGive(sync_sem);
|
|
||||||
}
|
|
||||||
portENABLE_INTERRUPTS();
|
|
||||||
vTaskDelay(2);
|
|
||||||
|
|
||||||
//Check returned queue set members are valid
|
|
||||||
uint32_t expect_0 = 0;
|
|
||||||
uint32_t expect_1 = 0;
|
|
||||||
for (int i = 0; i < (portNUM_PROCESSORS * QUEUE_LEN); i++) {
|
|
||||||
QueueSetMemberHandle_t member = xQueueSelectFromSet(queueset_handle, portMAX_DELAY);
|
|
||||||
uint32_t item;
|
|
||||||
if (member == queue_handles[0]) {
|
|
||||||
xQueueReceive((QueueHandle_t)member, &item, 0);
|
|
||||||
TEST_ASSERT_EQUAL_MESSAGE(expect_0, item, "Incorrect item value");
|
|
||||||
expect_0++;
|
|
||||||
} else if (member == queue_handles[1]) {
|
|
||||||
xQueueReceive((QueueHandle_t)member, &item, 0);
|
|
||||||
TEST_ASSERT_EQUAL_MESSAGE(expect_1, item, "Incorrect item value");
|
|
||||||
expect_1++;
|
|
||||||
} else {
|
|
||||||
TEST_ASSERT_MESSAGE(0, "Incorrect queue set member returned");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < portNUM_PROCESSORS; i++) {
|
// Fill the queue assigned to the current core
|
||||||
xSemaphoreTake(sync_sem, portMAX_DELAY);
|
for (int i = 0; i < QUEUE_LEN; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xQueueSend(queue, &core_id, 0));
|
||||||
}
|
}
|
||||||
for (int i = 0; i < portNUM_PROCESSORS; i++) {
|
|
||||||
xQueueRemoveFromSet(queueset_handle, handles[i]);
|
if (core_id != 0) {
|
||||||
vQueueDelete(queue_handles[i]);
|
// Indicate completion to core 0 and self delete
|
||||||
|
xSemaphoreGive(done_sem);
|
||||||
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
vQueueDelete(queueset_handle);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
TEST_CASE("Test queue sets multi-core", "[freertos]")
|
||||||
|
{
|
||||||
|
// Create done semaphore
|
||||||
|
done_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS - 1, 0);
|
||||||
|
TEST_ASSERT_NOT_EQUAL(NULL, done_sem);
|
||||||
|
|
||||||
|
// Create queues and queue set
|
||||||
|
QueueHandle_t queues[portNUM_PROCESSORS];
|
||||||
|
QueueSetHandle_t queue_set;
|
||||||
|
allocate_resources(portNUM_PROCESSORS, QUEUE_LEN, queues, &queue_set);
|
||||||
|
|
||||||
|
// Create tasks of the same priority for all cores except for core 0
|
||||||
|
for (int i = 1; i < portNUM_PROCESSORS; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(send_func, "send", 2048, (void *)queues[i], UNITY_FREERTOS_PRIORITY, NULL, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core 0 calls send_func as well triggering the simultaneous sends from all cores
|
||||||
|
send_func((void *)queues[0]);
|
||||||
|
|
||||||
|
// Wait for all other cores to be done
|
||||||
|
for (int i = 1; i < portNUM_PROCESSORS; i++) {
|
||||||
|
xSemaphoreTake(done_sem, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read queues from the queue set, then read an item from the queue
|
||||||
|
uint32_t queues_check_count[portNUM_PROCESSORS] = {0};
|
||||||
|
QueueSetMemberHandle_t member = xQueueSelectFromSet(queue_set, 0);
|
||||||
|
while (member != NULL) {
|
||||||
|
// Read the core ID from the queue, check that core ID is sane
|
||||||
|
BaseType_t core_id;
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(member, &core_id, 0));
|
||||||
|
TEST_ASSERT_LESS_THAN(portNUM_PROCESSORS, core_id);
|
||||||
|
queues_check_count[core_id]++;
|
||||||
|
|
||||||
|
// Get next member
|
||||||
|
member = xQueueSelectFromSet(queue_set, 0);
|
||||||
|
}
|
||||||
|
// Check that all items from all queues have been read
|
||||||
|
for (int i = 0; i < portNUM_PROCESSORS; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(QUEUE_LEN, queues_check_count[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup queues and queue set
|
||||||
|
free_resources(portNUM_PROCESSORS, queues, queue_set);
|
||||||
|
// Cleanup done sem
|
||||||
|
vSemaphoreDelete(done_sem);
|
||||||
|
done_sem = NULL;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_FREERTOS_UNICORE
|
||||||
|
Reference in New Issue
Block a user