diff options
Diffstat (limited to 'test/freertos/regression/txfr_queue_create_test.c')
| -rw-r--r-- | test/freertos/regression/txfr_queue_create_test.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/test/freertos/regression/txfr_queue_create_test.c b/test/freertos/regression/txfr_queue_create_test.c new file mode 100644 index 00000000..aa996070 --- /dev/null +++ b/test/freertos/regression/txfr_queue_create_test.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** FreeRTOS compatibility layer, queue creation resource accounting */ +/** */ +/**************************************************************************/ + +/* xQueueCreate() takes two byte pool allocations, the descriptor and the + backing storage, and then creates two semaphores. Since a failure returns + NULL, the caller has no handle and cannot call vQueueDelete(), so anything + the layer fails to undo on the way out is lost for good. These checks pin + down the accounting on each of those paths. + + The failure paths here are the ones reported in issue 570. */ + +#include "txfr_test_harness.h" + +#include "queue.h" + +#define QUEUE_LENGTH 8u +#define QUEUE_ITEM_SIZE 32u + +/* Storage for the static variant. */ +static StaticQueue_t queue_control_block; +static uint8_t queue_storage[QUEUE_LENGTH * QUEUE_ITEM_SIZE]; + + +void txfr_test_body(void) +{ + QueueHandle_t queue; + TXFR_COUNTERS counters; + int i; + int cycles; + + /* A successful creation takes both allocations and creates both + semaphores, and releases nothing. */ + txfr_test_account_start(TXFR_INJECT_NONE, 0); + queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE); + txfr_test_account_stop(&counters); + txfr_test_check("xQueueCreate returns a handle", queue != NULL); + txfr_test_check_counts("xQueueCreate accounting", &counters, 2, 0, 2, 0); + + /* Deleting it gives both allocations back and deletes both semaphores. */ + if(queue != NULL) + { + txfr_test_account_start(TXFR_INJECT_NONE, 0); + vQueueDelete(queue); + txfr_test_account_stop(&counters); + txfr_test_check_counts("vQueueDelete accounting", &counters, 0, 2, 0, 2); + } + + /* The read semaphore fails. Nothing was created that needs deleting, but + both allocations must be released. */ + txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 1); + queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE); + txfr_test_account_stop(&counters); + txfr_test_check("xQueueCreate returns NULL on read_sem failure", queue == NULL); + txfr_test_check_counts("read_sem failure releases both allocations", &counters, 2, 2, 1, 0); + + /* The write semaphore fails. The read semaphore already exists by then, so + it must be deleted as well as both allocations released. */ + txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 2); + queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE); + txfr_test_account_stop(&counters); + txfr_test_check("xQueueCreate returns NULL on write_sem failure", queue == NULL); + txfr_test_check_counts("write_sem failure unwinds read_sem and memory", &counters, 2, 2, 2, 1); + + /* The static variant takes no allocations and creates both semaphores. */ + txfr_test_account_start(TXFR_INJECT_NONE, 0); + queue = xQueueCreateStatic(QUEUE_LENGTH, QUEUE_ITEM_SIZE, queue_storage, &queue_control_block); + txfr_test_account_stop(&counters); + txfr_test_check("xQueueCreateStatic returns a handle", queue != NULL); + txfr_test_check_counts("xQueueCreateStatic accounting", &counters, 0, 0, 2, 0); + + if(queue != NULL) + { + txfr_test_account_start(TXFR_INJECT_NONE, 0); + vQueueDelete(queue); + txfr_test_account_stop(&counters); + txfr_test_check_counts("vQueueDelete on static queue", &counters, 0, 0, 0, 2); + } + + /* The static variant owns no memory, but it still creates two semaphores + and still hands back nothing on failure, so the caller cannot call + vQueueDelete(). A read semaphore left behind here would stay registered + in the kernel, pointing into the caller's StaticQueue_t. */ + txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 1); + queue = xQueueCreateStatic(QUEUE_LENGTH, QUEUE_ITEM_SIZE, queue_storage, &queue_control_block); + txfr_test_account_stop(&counters); + txfr_test_check("xQueueCreateStatic returns NULL on read_sem failure", queue == NULL); + txfr_test_check_counts("static read_sem failure leaves nothing behind", &counters, 0, 0, 1, 0); + + txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 2); + queue = xQueueCreateStatic(QUEUE_LENGTH, QUEUE_ITEM_SIZE, queue_storage, &queue_control_block); + txfr_test_account_stop(&counters); + txfr_test_check("xQueueCreateStatic returns NULL on write_sem failure", queue == NULL); + txfr_test_check_counts("static write_sem failure unwinds read_sem", &counters, 0, 0, 2, 1); + + /* Repeated create and delete cycles must leave the pool able to serve the + same request. This would not catch a small leak on its own, which is why + the counts above are checked directly, but it does catch a descriptor + that is released to the wrong pool or released twice. */ + cycles = 0; + for(i = 0; i < 64; i++) + { + queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE); + if(queue == NULL) + { + break; + } + + cycles++; + vQueueDelete(queue); + } + txfr_test_check("64 create and delete cycles succeed", cycles == 64); +} |
