summaryrefslogtreecommitdiff
path: root/test/freertos/regression/txfr_queue_create_test.c
blob: aa99607040944a3cb8ff8160490b22326f4e499d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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);
}