summaryrefslogtreecommitdiff
path: root/test/tx/regression/threadx_timer_misra_loop_test.c
blob: 1d2f45cf2dfb6cc8c33af9fe28011300f055ed05 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/***************************************************************************
 * 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
 **************************************************************************/

/* This test takes the timer thread's loop round to its exit.

   The timer thread's body is one loop over a condition that under
   TX_MISRA_ENABLE is a call rather than the literal one the other six
   configurations compile, so that the constant is not written into the
   controlling expression. The function behind it takes nothing and returns true,
   so the loop's exit arm never executes and, in the six configurations without
   the macro, does not exist at all.

   That function is the only thing in the construct that is not a constant, and
   the timer thread's loop is its only user anywhere in the kernel. The test
   defines it itself; the kernel is built shared and the call goes through the
   procedure linkage table, so the definition here is the one the loop reaches.

   What this costs is the timer thread. The loop is the whole body of the timer
   thread's entry function, so an exit ends the thread, and from that point the
   system has no thread to process timer expirations, thread timeouts or time
   slices. The test therefore does it deliberately and last: it proves the timer
   is working first, arms the shim, waits for the thread to end, and reports.
   Nothing after the arming depends on a tick.

   Read the coverage this contributes with that in mind. It is coverage of a
   kernel whose MISRA loop condition is the test's rather than the library's, and
   of a system the test has just taken the timer thread out of. The shim's own
   body is covered by every other test, which call it through this same loop
   thousands of times.  */


#include   <stdio.h>
#include   "tx_api.h"
#define    TX_SOURCE_CODE
#include   "tx_thread.h"
#include   "tx_timer.h"


#define TEST_PRIORITY               16

/* The timer must expire enough times before the shim is armed for the test to
   have shown that the thread is doing its work.  */

#define EXPIRATIONS_BEFORE_ARMING   3

/* How long to wait for the thread to end, in ticks. One is enough; this is a
   bound so that a shim the kernel never reaches fails the test rather than
   hanging it.  */

#define ARMED_TICK_BUDGET           200


static unsigned long   thread_0_counter =  0;
static TX_THREAD       thread_0;

static TX_TIMER        timer_0;

static volatile ULONG  timer_0_counter =  0;


#if defined(TX_MISRA_ENABLE) && !defined(TX_TIMER_PROCESS_IN_ISR) && !defined(TX_NO_TIMER)
#define TEST_DRIVES_THE_LOOP

/* While this is set the shim below reports that the loop should end.  */

static volatile UINT   shim_armed =  TX_FALSE;

/* How many times it has done so, which is how many times the exit arm has been
   taken.  */

static volatile ULONG  shim_exits =  0;


/* The MISRA loop condition, defined here so that it is this one the timer
   thread's loop calls. The library's returns true; this one returns false once
   it is armed.  */

UINT  _tx_misra_always_true(void)
{

    if (shim_armed == TX_TRUE)
    {

        shim_exits++;

        return(TX_FALSE);
    }

    return(TX_TRUE);
}
#endif


/* Define thread prototypes.  */

static void    thread_0_entry(ULONG thread_input);


/* Prototype for test control return.  */

void  test_control_return(UINT status);


/* Count the expirations, so the test can show the timer thread was running
   before it ends it.  */

static void  timer_0_entry(ULONG timer_input)
{

    timer_0_counter++;
}


static void    thread_0_entry(ULONG thread_input)
{

UINT    status;
ULONG   deadline;


    /* Increment thread 0 counter.  */
    thread_0_counter++;

    /* Inform user of success getting to this test.  */
    printf("Running Timer MISRA Loop Test..................................... ");

    status =  tx_timer_activate(&timer_0);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #2\n");
        test_control_return(1);
    }

    /* The timer thread has to be doing its work before the test ends it,
       otherwise an exit proves nothing about the loop it exited from.  */
    deadline =  _tx_timer_system_clock + ((ULONG) ARMED_TICK_BUDGET);

    while (timer_0_counter < ((ULONG) EXPIRATIONS_BEFORE_ARMING))
    {

        if (_tx_timer_system_clock > deadline)
        {

            printf("ERROR #3\n");
            test_control_return(1);
        }
    }

#ifdef TEST_DRIVES_THE_LOOP

    /* From here on nothing may depend on a tick.  */
    shim_armed =  TX_TRUE;

    /* The thread ends the next time the tick resumes it and the loop retests
       its condition.  */
    deadline =  _tx_timer_system_clock + ((ULONG) ARMED_TICK_BUDGET);

    while (_tx_timer_thread.tx_thread_state != TX_COMPLETED)
    {

        if (_tx_timer_system_clock > deadline)
        {

            /* The tick is still running -- the clock is what this waits on --
               so a thread that has not ended means the loop reached the
               library's condition rather than this test's.  */
            shim_armed =  TX_FALSE;

            printf("ERROR #4\n");
            test_control_return(1);
        }
    }

    shim_armed =  TX_FALSE;

    /* The loop tested its condition once more and took the arm that leaves
       it.  */
    if (shim_exits != ((ULONG) 1))
    {

        printf("ERROR #5\n");
        test_control_return(1);
    }
#endif

    status =  tx_timer_deactivate(&timer_0);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #6\n");
        test_control_return(1);
    }

    /* Success!  */
    printf("SUCCESS!\n");
    test_control_return(0);
}


/* Define what the initial system looks like.  */

#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void    threadx_timer_misra_loop_application_define(void *first_unused_memory)
#endif
{

UINT    status;
CHAR    *pointer;


    /* Put first available memory address into a character pointer.  */
    pointer =  (CHAR *) first_unused_memory;

    status =  tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
            pointer, TEST_STACK_SIZE_PRINTF,
            TEST_PRIORITY, TEST_PRIORITY, TX_NO_TIME_SLICE, TX_AUTO_START);
    pointer =  pointer + TEST_STACK_SIZE_PRINTF;

    /* Check for status.  */
    if (status != TX_SUCCESS)
    {

        printf("Running Timer MISRA Loop Test..................................... ERROR #1\n");
        test_control_return(1);
    }

    /* A tick's worth of work every tick, so the timer thread is resumed
       repeatedly and the loop retests its condition each time.  */
    status =  tx_timer_create(&timer_0, "timer 0", timer_0_entry, 0,
            1, 1, TX_NO_ACTIVATE);

    /* Check for status.  */
    if (status != TX_SUCCESS)
    {

        printf("Running Timer MISRA Loop Test..................................... ERROR #1\n");
        test_control_return(1);
    }
}