Files
Frédéric DesbiensandTilen Majerle cf577c7137 Made ThreadX object names const-qualifiable behind an option (#761)
Fixes #61

Object names are exposed as writable pointers throughout the kernel API, which
rejects string literals in C++ and lets a caller modify a name an object still
holds. Information services return those names through writable double
pointers.

Create services, control blocks, information services, the module manager and
trace registration now preserve const qualification, behind
TX_ENABLE_CONST_NAMES. The option defaults to off, so a build that says nothing
gets exactly the types it got before. It is opt-in rather than opt-out because
it changes the type of a public struct field: application code that copies a
name into a writable CHAR * stops compiling, which is a reasonable thing to ask
of a minor release and not of a patch one. Issue #780 tracks making it the
default in 6.6.

Two things the option reaches that its own call sites do not.
TX_CHAR_TO_UCHAR_POINTER_CONVERT has exactly two users, both of them reading an
object name in _tx_trace_object_register, and every form of that macro but the
MISRA one casts the qualifier away without saying so; the conversion is now
const in and const out, so nothing launders const to make the build pass. The
FreeRTOS adapter holds the name pcTaskGetName retrieves in a TX_NAME_CONST
pointer so that it tracks whichever declaration tx_thread_info_get has, and
keeps its writable return type through an explicit MISRA C:2012 Rule 11.8 cast,
because that signature is part of the FreeRTOS API.

Default build: all seven host configurations and all five SMP configurations
build with zero warnings and pass -- 113/113 on five host configurations,
100/100 on the two MISRA builds, 118/118 on SMP, 3/3 FreeRTOS. With
TX_ENABLE_CONST_NAMES set, the host default and both MISRA configurations, the
SMP trace configuration and the FreeRTOS adapter build with zero warnings and
pass.

Co-authored-by: Tilen Majerle <tilen@majerle.eu>
Assisted-by: Codex (gpt-6-astra) <noreply@openai.com>
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-09-28 14:08:13 -04:00

385 lines
12 KiB
C

/***************************************************************************
* 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 test harness */
/** */
/**************************************************************************/
#include "txfr_test_harness.h"
#include <stdio.h>
#include <stdlib.h>
/* Accounting state. Only the single test thread touches these. */
static int txfr_accounting;
static TXFR_INJECT_TARGET txfr_inject_target;
static int txfr_inject_fail_on;
static int txfr_inject_call_index;
static TXFR_COUNTERS txfr_counters;
static int txfr_failures;
/* The real entry points, supplied by the linker in response to --wrap. */
UINT __real__txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size, ULONG wait_option);
UINT __real__txe_byte_release(VOID *memory_ptr);
UINT __real__txe_semaphore_create(TX_SEMAPHORE *semaphore_ptr, TX_NAME_CONST CHAR *name_ptr, ULONG initial_count, UINT semaphore_control_block_size);
UINT __real__txe_semaphore_delete(TX_SEMAPHORE *semaphore_ptr);
UINT __real__txe_mutex_create(TX_MUTEX *mutex_ptr, TX_NAME_CONST CHAR *name_ptr, UINT inherit, UINT mutex_control_block_size);
UINT __real__txe_mutex_delete(TX_MUTEX *mutex_ptr);
UINT __real__txe_event_flags_create(TX_EVENT_FLAGS_GROUP *group_ptr, TX_NAME_CONST CHAR *name_ptr, UINT event_control_block_size);
UINT __real__txe_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr);
UINT __real__txe_timer_create(TX_TIMER *timer_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*expiration_function)(ULONG input),
ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks,
UINT auto_activate, UINT timer_control_block_size);
UINT __real__txe_timer_delete(TX_TIMER *timer_ptr);
UINT __real__txe_thread_create(TX_THREAD *thread_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*entry_function)(ULONG entry_input),
ULONG entry_input, VOID *stack_start, ULONG stack_size, UINT priority,
UINT preempt_threshold, ULONG time_slice, UINT auto_start,
UINT thread_control_block_size);
UINT __real__txe_thread_delete(TX_THREAD *thread_ptr);
UINT __real__txe_thread_resume(TX_THREAD *thread_ptr);
/* Determine whether the call now being made is the one the test asked to fail.
Only calls made while accounting is active are considered, which keeps the
objects created by tx_freertos_init() and by the harness out of the count. */
static int txfr_should_fail(TXFR_INJECT_TARGET target)
{
if((txfr_accounting == 0) || (txfr_inject_target != target))
{
return 0;
}
txfr_inject_call_index++;
return (txfr_inject_call_index == txfr_inject_fail_on) ? 1 : 0;
}
UINT __wrap__txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size, ULONG wait_option)
{
if(txfr_accounting != 0)
{
txfr_counters.byte_allocate++;
}
return __real__txe_byte_allocate(pool_ptr, memory_ptr, memory_size, wait_option);
}
UINT __wrap__txe_byte_release(VOID *memory_ptr)
{
if(txfr_accounting != 0)
{
txfr_counters.byte_release++;
}
return __real__txe_byte_release(memory_ptr);
}
UINT __wrap__txe_semaphore_create(TX_SEMAPHORE *semaphore_ptr, TX_NAME_CONST CHAR *name_ptr, ULONG initial_count, UINT semaphore_control_block_size)
{
if(txfr_should_fail(TXFR_INJECT_SEMAPHORE_CREATE) != 0)
{
txfr_counters.semaphore_create++;
return TX_SEMAPHORE_ERROR;
}
if(txfr_accounting != 0)
{
txfr_counters.semaphore_create++;
}
return __real__txe_semaphore_create(semaphore_ptr, name_ptr, initial_count, semaphore_control_block_size);
}
UINT __wrap__txe_semaphore_delete(TX_SEMAPHORE *semaphore_ptr)
{
if(txfr_accounting != 0)
{
txfr_counters.semaphore_delete++;
}
return __real__txe_semaphore_delete(semaphore_ptr);
}
UINT __wrap__txe_mutex_create(TX_MUTEX *mutex_ptr, TX_NAME_CONST CHAR *name_ptr, UINT inherit, UINT mutex_control_block_size)
{
if(txfr_should_fail(TXFR_INJECT_MUTEX_CREATE) != 0)
{
txfr_counters.mutex_create++;
return TX_MUTEX_ERROR;
}
if(txfr_accounting != 0)
{
txfr_counters.mutex_create++;
}
return __real__txe_mutex_create(mutex_ptr, name_ptr, inherit, mutex_control_block_size);
}
UINT __wrap__txe_mutex_delete(TX_MUTEX *mutex_ptr)
{
if(txfr_accounting != 0)
{
txfr_counters.mutex_delete++;
}
return __real__txe_mutex_delete(mutex_ptr);
}
UINT __wrap__txe_event_flags_create(TX_EVENT_FLAGS_GROUP *group_ptr, TX_NAME_CONST CHAR *name_ptr, UINT event_control_block_size)
{
if(txfr_should_fail(TXFR_INJECT_EVENT_FLAGS_CREATE) != 0)
{
txfr_counters.event_flags_create++;
return TX_GROUP_ERROR;
}
if(txfr_accounting != 0)
{
txfr_counters.event_flags_create++;
}
return __real__txe_event_flags_create(group_ptr, name_ptr, event_control_block_size);
}
UINT __wrap__txe_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr)
{
if(txfr_accounting != 0)
{
txfr_counters.event_flags_delete++;
}
return __real__txe_event_flags_delete(group_ptr);
}
UINT __wrap__txe_timer_create(TX_TIMER *timer_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*expiration_function)(ULONG input),
ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks,
UINT auto_activate, UINT timer_control_block_size)
{
if(txfr_should_fail(TXFR_INJECT_TIMER_CREATE) != 0)
{
txfr_counters.timer_create++;
return TX_TIMER_ERROR;
}
if(txfr_accounting != 0)
{
txfr_counters.timer_create++;
}
return __real__txe_timer_create(timer_ptr, name_ptr, expiration_function, expiration_input,
initial_ticks, reschedule_ticks, auto_activate, timer_control_block_size);
}
UINT __wrap__txe_timer_delete(TX_TIMER *timer_ptr)
{
if(txfr_accounting != 0)
{
txfr_counters.timer_delete++;
}
return __real__txe_timer_delete(timer_ptr);
}
UINT __wrap__txe_thread_create(TX_THREAD *thread_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*entry_function)(ULONG entry_input),
ULONG entry_input, VOID *stack_start, ULONG stack_size, UINT priority,
UINT preempt_threshold, ULONG time_slice, UINT auto_start,
UINT thread_control_block_size)
{
if(txfr_should_fail(TXFR_INJECT_THREAD_CREATE) != 0)
{
txfr_counters.thread_create++;
return TX_THREAD_ERROR;
}
if(txfr_accounting != 0)
{
txfr_counters.thread_create++;
}
return __real__txe_thread_create(thread_ptr, name_ptr, entry_function, entry_input, stack_start,
stack_size, priority, preempt_threshold, time_slice, auto_start,
thread_control_block_size);
}
UINT __wrap__txe_thread_delete(TX_THREAD *thread_ptr)
{
if(txfr_accounting != 0)
{
txfr_counters.thread_delete++;
}
return __real__txe_thread_delete(thread_ptr);
}
UINT __wrap__txe_thread_resume(TX_THREAD *thread_ptr)
{
if(txfr_should_fail(TXFR_INJECT_THREAD_RESUME) != 0)
{
txfr_counters.thread_resume++;
return TX_RESUME_ERROR;
}
if(txfr_accounting != 0)
{
txfr_counters.thread_resume++;
}
return __real__txe_thread_resume(thread_ptr);
}
void txfr_test_account_start(TXFR_INJECT_TARGET target, int fail_on_call)
{
TX_MEMSET(&txfr_counters, 0, sizeof(txfr_counters));
txfr_inject_target = target;
txfr_inject_fail_on = fail_on_call;
txfr_inject_call_index = 0;
txfr_accounting = 1;
}
void txfr_test_account_stop(TXFR_COUNTERS *p_counters)
{
txfr_accounting = 0;
txfr_inject_target = TXFR_INJECT_NONE;
if(p_counters != NULL)
{
*p_counters = txfr_counters;
}
}
void txfr_test_check(const char *label, int condition)
{
printf("%-52s %s\n", label, (condition != 0) ? "PASS" : "FAIL");
if(condition == 0)
{
txfr_failures++;
}
}
void txfr_test_check_counts(const char *label, const TXFR_COUNTERS *p_counters,
int expect_allocate, int expect_release,
int expect_object_create, int expect_object_delete)
{
int object_create;
int object_delete;
int ok;
/* Only one kind of kernel object is exercised per check, so the totals
identify it unambiguously. */
object_create = p_counters->semaphore_create + p_counters->mutex_create +
p_counters->event_flags_create + p_counters->timer_create +
p_counters->thread_create;
object_delete = p_counters->semaphore_delete + p_counters->mutex_delete +
p_counters->event_flags_delete + p_counters->timer_delete +
p_counters->thread_delete;
ok = ((p_counters->byte_allocate == expect_allocate) &&
(p_counters->byte_release == expect_release) &&
(object_create == expect_object_create) &&
(object_delete == expect_object_delete)) ? 1 : 0;
printf("%-52s %s\n", label, (ok != 0) ? "PASS" : "FAIL");
if(ok == 0)
{
printf("%-52s got alloc=%d release=%d create=%d delete=%d\n", "",
p_counters->byte_allocate, p_counters->byte_release, object_create, object_delete);
printf("%-52s expected alloc=%d release=%d create=%d delete=%d\n", "",
expect_allocate, expect_release, expect_object_create, expect_object_delete);
txfr_failures++;
}
}
int txfr_test_failures(void)
{
return txfr_failures;
}
/* Test thread. The byte pool operations under test require thread context. */
static TX_THREAD txfr_test_thread;
static ULONG txfr_test_stack[4096];
static void txfr_test_thread_entry(ULONG id)
{
(void)id;
txfr_test_body();
printf("\n%s\n", (txfr_failures == 0) ? "ALL CHECKS PASSED" : "THERE WERE FAILED CHECKS");
fflush(stdout);
/* The kernel owns the calling thread, so returning here would simply idle.
Leave the process with a status ctest can read. */
exit((txfr_failures == 0) ? 0 : 1);
}
void tx_application_define(void *first_unused_memory)
{
UINT ret;
(void)first_unused_memory;
ret = tx_freertos_init();
if(ret != TX_SUCCESS)
{
printf("tx_freertos_init() failed with %u\n", ret);
exit(2);
}
ret = tx_thread_create(&txfr_test_thread, "txfr_test", txfr_test_thread_entry, 0u,
txfr_test_stack, sizeof(txfr_test_stack), 16u, 16u, 0u, TX_AUTO_START);
if(ret != TX_SUCCESS)
{
printf("tx_thread_create() failed with %u\n", ret);
exit(2);
}
}
int main(void)
{
tx_kernel_enter();
return 0;
}