summaryrefslogtreecommitdiff
path: root/test/tx/cmake/regression
AgeCommit message (Collapse)Author
2026-09-15Restored the random stack fill value cleared during thread creation (#732)Frédéric Desbiens
Fixes #723 With `TX_ENABLE_STACK_CHECKING` and `TX_ENABLE_RANDOM_NUMBER_STACK_FILLING` both enabled, `_tx_thread_create` picked a random byte, stored it in `tx_thread_stack_fill_value`, filled the stack with it -- and then cleared the whole control block with `TX_MEMSET`. The stack held the pattern while the control block claimed zero, so `TX_THREAD_STACK_CHECK` and `_tx_thread_stack_analyze` compared against the wrong value for the entire life of the thread. The value is now computed into a local and written back after the clear. The clear stays where it is, because the module manager's error checking walks the created list before the control block may be touched. Fixed in `common/src/tx_thread_create.c`, `common_smp/src/tx_thread_create.c` and `txm_module_manager_thread_create.c`, where it additionally left a user-mode module thread's kernel stack filled with zeros. `threadx_thread_stack_fill_value_test` creates sixteen unstarted threads and checks each control block against the pattern in its stack, tolerating a random zero byte without letting that hide the defect. Added to both suites: `ERROR #3` on `dev` in `stack_checking_rand_fill_build`, green with the fix. Five tx configurations at 104 tests, five SMP at 117, and all three sources clean under `-Wall -Wextra` across every combination of the four stack-filling switches. Assisted-by: Copilot (Opus 5) <[email protected]>
2026-09-10Refused thread delete and reset while an exit transition is in progress (#724)Frédéric Desbiens
_tx_thread_shell_entry and _tx_thread_terminate both publish a thread's terminal state -- TX_COMPLETED or TX_TERMINATED -- and then call that thread's exit notification callback, before the thread has been detached from the ready list and before either service has finished with the pointer it holds to the control block. That terminal state is exactly the state _tx_thread_delete and _tx_thread_reset accept as authorization to invalidate or rebuild the control block, and neither service tested whether the transition producing it had finished. A callback could therefore delete the thread it was called for -- and then lawfully recreate it over the same memory, since delete exists to permit that -- while the scheduler was still linked to the old incarnation. tx_thread_create zeroes the whole control block and can auto-start the new one, so the old priority list is left heading at a block whose own priority field names a different list, with the old priority's map bit set behind nothing. Alternatively a callback could reset a terminated thread, which moves it out of the terminal state, and then resume it: the interrupted- suspension logic in _tx_thread_system_resume refuses to void a suspension only while the state is still terminal, so with the reset allowed first the resume clears the suspending flag and restores TX_READY, the outer service then finds the flag clear and skips the removal, and tx_thread_terminate returns TX_SUCCESS for a thread that is runnable again. Interrupt masking does not close the window, because the kernel restores the prior posture before invoking the callback deliberately. On SMP TX_RESTORE also releases the global protection, so the target can be executing on another core while its callback runs -- and a reset there memsets the stack a live core is running on. On the Linux, Win32 and Win64 host simulation ports the consequence is more immediate than corruption: TX_THREAD_DELETE_PORT_COMPLETION cancels and joins the host thread backing the deleted thread, so a callback-side delete of the completing thread destroys the host thread the callback is running on. The fix marks the transition and has the two services refuse a marked target, returning the errors they already document, TX_DELETE_ERROR and TX_NOT_DONE. The refusal is transient and the same call succeeds once the transition has completed, so no documented lifecycle is lost; and it is in the core services rather than the _txe_ wrappers, so disabling error checking cannot disable it. Refusing the reset is also what closes the resume path, without touching _tx_thread_system_resume: its existing terminal-state test is sufficient once nothing can turn the terminal state into TX_SUSPENDED from inside the window. tx_thread_suspending is the marker, rather than a new control-block field. It already means "a suspension is in progress" and is already true across the callback in the two interruptable paths, so no field is added, the public structure is unchanged, and sizeof(TX_THREAD) is unchanged -- which matters, because the Module Manager's object handling depends on the sizes of the control blocks. Widening its lifetime was checked against every reader rather than assumed. There are four: two in _tx_thread_system_suspend and two in _tx_thread_system_resume. In every window this change widens, the state is TX_COMPLETED or TX_TERMINATED, and both resume readers already refuse to void a suspension for exactly those two states, so their behaviour is unchanged; and no suspension routine is called on the target in those windows, so the suspend readers never see them. No suspension-initiating service can set the marker again inside a window either: every one of them acts on a thread that is ready or suspended. Three sites needed changing beyond the two refusals, and the shape of each was decided by where the marker can safely be cleared: - The non-ready branch of _tx_thread_terminate cleared the marker before the terminated extension and the callback, which is what left them free to act on a control block the service still had mutex-release processing to do against. The clear moves to the common tail, after the last dereference of the target, and becomes the single clear site for the whole service. In the interruptable ready branch the flag is already false there, because _tx_thread_system_suspend cleared it when it detached the thread, so the tail store is a second store of a value the flag already holds -- cheaper than testing for it, and it keeps one clear site. - Under TX_NOT_INTERRUPTABLE neither path set the marker at all, because that configuration does not use the interruptable suspension path that sets it. Both now set it before the callback. Interrupts being disabled there does not help: the callback is reached by a direct call. - In the TX_NOT_INTERRUPTABLE completion path the marker is cleared before _tx_thread_system_ni_suspend rather than after it. That call returns to the scheduler for a thread that is the current thread, which a completing thread is, and does not come back; clearing afterwards would leave a normally completed thread marked for ever and therefore permanently undeletable. Nothing is lost by clearing early there, because everything from that point to the detachment runs with interrupts disabled and calls no application code. The change is the same change twice. All four files are byte-for-byte identical between common and common_smp at this commit and stay so after it, so common_smp was written by copying rather than by repeating the edits. tx_thread_system_suspend.c and tx_thread_system_resume.c, which do differ between the kernels, are deliberately untouched. Tests. The in-tree regression test goes to both trees and is byte-for-byte identical between them. It drives seven scenarios: terminating a ready non-current target with two peers ready at the same priority, with the callback attempting the delete and recreating the block if it succeeded; the same with the callback attempting the reset and then the resume; terminating a target suspended on a semaphore while owning a mutex, which is the non-ready branch; natural completion alone at its priority, including the safe post-completion reset, terminate, delete and recreate at another priority; self termination; a benign callback, whose notification count and ordering are unchanged; and the state and boundary cases, where the new refusal must not fire. It measures rather than describes. The callback records the published state, the marker, and the status of every lifecycle service it can reach, calling the core service as well as the wrapper wherever a refusal is expected. A snapshot taken under interrupt lockout -- which is the global SMP protection on an SMP port -- checks that every ready list agrees with the control blocks it heads, that the priority map agrees with the lists, and that each execute pointer is a member of the list its own priority field names. Every walk is bounded, so a corrupted ring costs an assertion and not a hang, and no test in the suite can hang. Expectations are counted inside a scenario and gated between scenarios, so a failing kernel reports how much it failed by without being driven further into its own corruption. The consequences are demonstrated from the terminator's context rather than the completing thread's, which is what makes the pre-fix behaviour an assertion instead of a wedged simulator. Compiled against the unfixed sources the test fails 9 of the 24 expectations it reaches in the uniprocessor tree and 10 of 24 in the SMP tree, and the failures are the finding: the callback-side delete succeeds, the recreate succeeds, the consistency snapshot disagrees, the target is neither terminal nor detached when the service returns, and a peer has left the ready ring the recreated block hijacked. The SMP tree gets a second test for the case that needs concurrency. The victim is excluded to core 1 and spins there without relinquishing while the controller, excluded to core 0, terminates it, so the callback runs on one core while the target executes on another. The callback-side reset and delete must both be refused, and a sentinel written into the unused low end of the victim's stack must survive -- a reset would have memset the whole stack before rebuilding the frame. Every wait is bounded, and if the remote precondition cannot be established the test says so and drops only the assertions that depend on it rather than reporting a pass it did not earn; measured over twenty consecutive runs it established the precondition every time. TX_NOT_INTERRUPTABLE and TX_DISABLE_ERROR_CHECKING are not among the five build configurations either tree compiles, and each tree builds the whole library once per configuration, so neither can be reached from inside the suites. The lines this change adds under TX_NOT_INTERRUPTABLE are therefore in no configuration the trees build, and they are where the permanent- undeletability failure mode lives, so they get their own harness rather than a compile check: the four sources plus the two error wrappers are compiled directly into a test executable, once per combination, with recorders standing behind the scheduler services they call. That is what makes the marker's value at the moment of detachment directly observable. It runs 66 expectations under TX_NOT_INTERRUPTABLE, 66 under that with error checking disabled, 45 under that with notification disabled, and 63 under error checking disabled alone; against the unfixed sources those fail 25, 22, 6 and 20 respectively. The harness lives in the uniprocessor tree only, because the four sources are identical between the kernels and the shim replaces the very primitive the SMP port differs in, so a second copy would compile the same text under the same macros. It is deliberately left out of the coverage instrumentation, since the same source under different feature macros has a different line set and merging those would confuse the union rather than add to it. Results. Both suites pass in all five configurations with GCC 14: 103 of 103 in the uniprocessor tree, up from 98, and 116 of 116 in the SMP tree, up from 114. Merged line coverage is 100% in the uniprocessor tree and 5172 of 5183 in the SMP tree, whose eleven uncovered lines are the same eleven that were uncovered before this change and are in tx_byte_pool_search and tx_thread_smp_utilities; all four changed files are at 100% line coverage in both trees, and SMP branch coverage rises from 2819 of 3548 to 2831 of 3556. Cross-compiled with arm-none-eabi-gcc at -Wall -Wextra for Cortex-M4 against common and for Cortex-A7 SMP against common_smp, all four files produce no diagnostics at all and an identical warning set to before the change, under -std=gnu99 and -std=c99 alike -- unlike the module ports, -std=c99 does not fail on these base ports, and even -Wconversion is clean. No MISRA deviation is required: explicit comparisons to TX_TRUE, existing ThreadX types, single-entry and single-exit control flow, no goto, and two added constant-time tests that change no real-time complexity. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-08-26Covered the trace entry update paths and the misaligned stack adjustment (#666)Frédéric Desbiens
Against the merged coverage report -- every build configuration instrumented and unioned -- eighteen lines of common/src were uncovered. Seventeen of them were one missing scenario rather than eighteen separate gaps. tx_block_allocate, tx_byte_allocate, tx_thread_system_suspend and tx_thread_system_resume each carry blocks under TX_ENABLE_EVENT_TRACE that go back and patch a trace entry once the call has done its work, all of the shape: if (entry_ptr != TX_NULL) { if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp) entry_ptr comes from _tx_trace_buffer_current_ptr, which stays TX_NULL until tx_trace_enable is called at run time. Building with TX_ENABLE_EVENT_TRACE is not enough, and exactly one test in the suite enables tracing -- threadx_trace_basic_test -- which tests the enable API itself and never calls either allocator. So those blocks sat in the report's denominator and never in its covered set. threadx_trace_entry_update_test enables tracing and then drives both allocators twice each, once on the path that succeeds immediately and once through a suspension that a second thread satisfies, since each allocator carries one update block on either side. It then sleeps so that the last runnable thread suspends with nothing ready to take over: tx_thread_system_suspend lines 345 and 351 are on the branch that sets _tx_thread_execute_ptr to TX_NULL, and the two allocator suspensions never reach it because the other thread was always ready. The same test closes tx_trace_object_register's TX_NULL name break by creating a semaphore with no name. A semaphore and not a thread deliberately: for TX_TRACE_OBJECT_TYPE_THREAD the register function dereferences the pointer it is given to read the thread's priority, so that type needs a real TX_THREAD behind it. threadx_trace_basic_test makes the equivalent call only under ifndef TX_ENABLE_EVENT_TRACE, against the no-op stub. threadx_thread_misaligned_stack_test covers the remaining line, tx_thread_create.c:136, where a stack that does not begin on a ULONG boundary costs a ULONG of size so that rounding the start up cannot run past the end of the caller's buffer. Every other test hands tx_thread_create an aligned stack. That line is compiled only under TX_ENABLE_STACK_CHECKING, so it is absent from three of the five configurations' reports rather than uncovered in them, and it was verified under stack_checking_build. Measured on the merged report, all 480 tests passing and 5 of 5 configurations green: 4485 of 4503 covered before, 4502 of 4503 after, denominator unchanged. One line remains, tx_thread_system_resume.c:529, and it is the report's last flapping line rather than a standing gap -- two clean runs of the same tree gave 4503 of 4503 and 4502 of 4503. Reaching it by construction was tried twice and failed both times, so it is left alone here. It needs the preempt disable flag and the system state both clear, and tx_thread_resume raises the preempt disable flag before calling _tx_thread_system_resume, as do the put and send paths; creating a higher priority auto-start thread from thread context does not raise it but does not reach the check either, which a probe showed is executed only during initialisation, with the system state at TX_INITIALIZE_IN_PROGRESS. Assisted-by: Claude Opus 5 <[email protected]>
2026-06-06Added copyright headers to files missing themFrédéric Desbiens
Applied the standard MIT license header to all project-owned C, header, assembly, shell, and Python files that were missing a copyright notice. Third-party, toolchain startup, and auto-generated files were excluded. Co-authored-by: Copilot <[email protected]>
2026-05-27Refactored, consolidated, and cleaned up RV32/RV64 ports (#536)Frédéric Desbiens
risc-v: refactor, consolidate, and fix RV32/RV64 ports Consolidates the RISC-V 32-bit and 64-bit GNU/Clang port sources, fixes two pre-existing assembly bugs discovered during testing, and hardens the build infrastructure for both the regression suite and the CORE-V MCU example. --- Port consolidation (RV32 GNU + Clang) --- - Delete ports/risc-v32/clang/src/ (8 .S files had no Clang-specific directives; diverged from GNU only due to missing bug fixes). The Clang port CMakeLists.txt now compiles from ../gnu/src/. - Change .global -> .weak for _tx_initialize_low_level in gnu/src/ to allow BSP-level override without a linker conflict (adopted from Clang port). - Create ports/risc-v32/common/tx_port_riscv32_common.h with all definitions shared between GNU and Clang ports. Reduce both tx_port.h files to thin wrappers. - Add a prominent comment in risc-v64/gnu/inc/tx_port.h explaining why LONG/ULONG are intentionally 32-bit on RV64 (ThreadX ABI requirement, mirrors win64/MSVC LLP64). --- Shared CMake helper --- - Add cmake/threadx_riscv_port.cmake with threadx_add_riscv_port(). All three port CMakeLists.txt files are reduced to ~8 lines each. Include path is relative to CMAKE_CURRENT_LIST_DIR so the helper works whether ports are built standalone or as a subdirectory of the test framework. --- Shared example-build drivers --- - Create canonical driver files under ports/risc-v_common/: inc/csr.h (uintptr_t-based; portable RV32 + RV64) example_build/plic/ (plic.c, plic.h) example_build/uart/ (uart_qemu_ns16550.c/h; static inline putc_nolock) example_build/trap/ (trap_qemu.c; XLEN-portable mcause constants) - Replace per-example copies with symlinks in all qemu_virt and cva6_ariane example directories. - Fix OS_IS_INTERRUPT typo (was OS_IS_INTERUPT) in shared trap_qemu.c. - Gate print_hex() behind TX_RISCV_TRAP_DEBUG. --- Bug fixes in RV32 assembly --- tx_thread_schedule.S: - Solicited-return FP path: reload t0 from the mepc stack slot before csrw mepc, t0. After the FP restore block, t0 held the fcsr value (0 for new threads), which caused mepc = 0 and an immediate instruction-address fault on the first context switch. - Same path: reload t0 from the mstatus stack slot before csrw mstatus, t0 to avoid writing the stale fcsr value into mstatus. tx_thread_system_return.S: - FP callee-saved registers were saved unconditionally before the mstatus.FS check, causing an illegal instruction trap (mcause=0x2) when a thread with FS=Off (lazy FPU, thread has never used FP) voluntarily yielded. - Apply the same FS guard pattern used in tx_thread_context_save.S: read mstatus first, isolate FS[1:0], and skip fsw/fsd if FS == Off. Both bugs were pre-existing on origin/dev and are unrelated to the consolidation changes. --- RV64 64-bit pointer compatibility --- - Add TX_TIMER_INTERNAL_EXTENSION, TX_THREAD_CREATE_TIMEOUT_SETUP, and TX_THREAD_TIMEOUT_POINTER_SETUP to risc-v64/gnu/inc/tx_port.h to store the thread timeout pointer in a VOID * extension field rather than truncating it into a 32-bit ULONG. Mirrors the win64 port pattern. - Define TX_TIMER_EXTENSION_PTR_DEFINED as a portable sentinel. - Update threadx_thread_basic_execution_test.c guard from #if defined(_WIN64) to #if defined(_WIN64) || defined(TX_TIMER_EXTENSION_PTR_DEFINED). - Disable -Wconversion for the RV64 test build: ULONG = unsigned int (32-bit) is intentional for ThreadX ABI but triggers spurious warnings when sizeof() (8 bytes on RV64) appears in arithmetic with ULONG in common/src/. --- Regression suite cmake fixes --- test/tx/cmake/riscv/regression/CMakeLists.txt: - Build testcontrol_weak_defaults.c as a separate OBJECT library and include it in every test executable via $<TARGET_OBJECTS:>. GNU ld does not extract objects from a static archive to satisfy weak symbols, so bundling it in test_utility was insufficient for the standalone threadx_initialize_kernel_setup_test. test/tx/cmake/regression/CMakeLists.txt, test/smp/cmake/regression/CMakeLists.txt: - Same fix applied to the Linux and SMP regression builds. The symbols abort_all_threads_suspended_on_mutex, suspend_lowest_priority, and abort_and_resume_byte_allocating_thread were introduced by the win64 merge and left the standalone test unlinkable. --- CORE-V MCU toolchain and build fixes --- cmake/riscv64-gcc-rv32imc.cmake: - Resolve riscv64-unknown-elf-gcc via PATH so the riscv-collab toolchain in /opt/riscv/bin is preferred when it appears first. ports/risc-v32/gnu/example_build/core_v_mcu/bsp/clz.c (new): - The riscv-collab toolchain is built without rv32 multilib, so its libgcc does not define __clzsi2 (the helper emitted for __builtin_clz() in fll.c). Add a weak __clzsi2 fallback so the build is self-contained with any riscv64-unknown-elf toolchain. The weak attribute yields to a libgcc-provided strong symbol when the Ubuntu multilib package is used. core_v_mcu/CMakeLists.txt: - Add bsp/clz.c to sources. - Reference CMAKE_TOOLCHAIN_FILE via message(STATUS) to suppress the false- positive "Manually-specified variables were not used by the project" CMake warning and to show the active toolchain at configure time. --- Housekeeping --- - Rename azrtos_test_* -> threadx_test_* (eliminate Azure RTOS branding). - Add RV64 QEMU CI test script: ports/risc-v64/gnu/example_build/qemu_virt/test/ threadx_test_tx_gnu_riscv64_qemu.py - Normalize entry.s -> entry.S in all 4 example directories. - .gitignore: exclude build_m7/ and .codex local artifacts. - CI: comment out the riscv regression workflow job and remove it from the deploy job's needs list (preserved in-place for easy re-enablement). --- Verified --- - 95/95 RV32 regression tests pass (QEMU virt) - 95/95 RV64 regression tests pass (QEMU virt) - All 5 Linux build configurations build cleanly (default_build_coverage, disable_notify_callbacks_build, stack_checking_build, stack_checking_rand_fill_build, trace_build) - CORE-V MCU example_build links cleanly with /opt/riscv toolchain Co-authored-by: Copilot [email protected]
2026-05-26Added win64 ports of ThreadX and ThreadX SMP (#529)Frédéric Desbiens
Windows x64 port and regression suite This PR adds the Windows x64 (Win64) simulation port for both the standalone and SMP variants of ThreadX, along with the full CMake build and test infrastructure needed to run the regression suite on Windows. New ports Win64 standalone (ports/win64/vs_2022): self-contained Windows simulation port using Win32 threading primitives as virtual cores. Includes CMake integration, build/test scripts, and MSVC project files. Win64 SMP (ports/win64_smp/vs_2022): multi-core Windows simulation port. Supports up to 4 virtual cores backed by Windows host threads. Scheduler and timer improvements The initial port used coarse polling and synchronous SuspendThread/ResumeThread pairs throughout the scheduler hot path. Several rounds of optimization reduced the SMP regression suite runtime from ~150 s to ~78 s (-48%), with no regressions: - Replaced scheduler polling with an event-driven wake path; switched the simulated timer to one-shot rearming to eliminate catch-up ticks. - Skip SuspendThread when _tx_thread_preempt_disable != 0 (new suspension type 3) -- the primary optimization, yielding up to 7.9x speedup on preemption-heavy tests. - Skip SuspendThread when a thread is spinning on the Win32 critical section (suspension type 4), and fix a stale-TLS bug in _tx_win32_critical_section_obtain that could stamp mutex_access on the wrong virtual core. - Added a 2 ms scheduler event timeout (matching the Linux SMP port) to prevent stalls on any missed SetEvent. - Enabled high-resolution waitable timers (SetWaitableTimerEx) for accurate 100 Hz tick cadence. - Increased TX_WIN32_CONTENTION_PAUSE_COUNT from 64 to 256 to reduce SwitchToThread overhead under heavy CS contention. Build and test infrastructure - Hardened the Windows build wrapper (scripts/build_tx.ps1): invoke Ninja directly for Ninja build trees, fix timeout detection, add a default build timeout, and limit fallback replay to real timeout cases. - Added -Clean support to Windows test scripts to remove stale CTest state before each run. - Skip Visual Studio DevShell re-entry when the active MSVC environment already matches the requested architecture. - Fixed scripts/build_tx.sh (Linux) regression source generation: replaced brittle exact-string insertion with line-based matching so the interrupt dispatcher hook is inserted reliably for both simulator ports. Test suite updates - Introduced test/tx/regression/threadx_test_port.h with portable macros (TX_TEST_POINTER_WORD, TX_TEST_STORE_POINTER) for storing pointers in test arrays on 64-bit targets where ULONG remains 32-bit. - Adjusted pool-capacity and pointer-storage patterns in regression tests to use ALIGN_TYPE-sized slots, making the suite correct on 64-bit hosts. - Restored stricter event flag, sleep, and timer expectations now that port-level fixes make prior Windows accommodations unnecessary. - Tightened SMP watchdog and clean-build timeout defaults. Version metadata Updated Win32, Win64, and Win64 SMP port version strings to 6.5.1.202602. Co-authored-by: Copilot <[email protected]> Co-authored-by: Codex (gpt 5.5) <[email protected]>
2025-04-02Make queue max message size configurableHaithem Rahmani
Summary ------- This commit fixes the issue #424 Details -------- - Add a the configuration option TX_QUEUE_MESSAGE_MAX_SIZE in the tx_api.h with default value set to TX_ULONG_16 to keep backword compatibility. - Update the txe_queue_create() to check on TX_QUEUE_MESSAGE_MAX_SIZE rather than TX_ULONG_16 as max message size. - Add a new unitary test to cover the new change.
2025-02-24Updated CMake minimal version to 3.13Frédéric Desbiens
2023-04-04Release ThreadX regression systemTiejun Zhou