summaryrefslogtreecommitdiff
path: root/test/tx/regression
AgeCommit message (Collapse)Author
2026-09-28Made ThreadX object names const-qualifiable behind an option (#761)Frédéric Desbiens
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 <[email protected]> Assisted-by: Codex (gpt-6-astra) <[email protected]> Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-23Fixed RISC-V regression portability failures (#773)Frédéric Desbiens
RISC-V regression builds ran only at -O0, leaving a timer callback counter that spins forever at -O2. The trace regression was excluded because it referenced a port-specific interrupt-save variable, and its adjacent pools could start misaligned on RV64. Made the timer counter volatile, gave the trace test aligned pool storage and a portable saved-interrupt value, and enabled it on RISC-V. Added an -O2 QEMU configuration to keep the optimized failure covered. CMake/Ninja/QEMU: RV64 default, optimized and trace suites passed 97/97 each; RV32 passed 96/96 each. Two ISR event tests passed 30 repeats each at -O2. The Linux/GCC 14 trace test passed. The reported timing resonance did not recur. Assisted-by: Codex (gpt-6-sol) <[email protected]>
2026-09-16Fixed the zero trace time stamps in the Linux ports' MISRA builds (#749)Frédéric Desbiens
Both Linux ports define TX_TRACE_TIME_SOURCE as _tx_misra_time_stamp_get() when TX_MISRA_ENABLE is set, and neither implements that function, so both inherit the generic `return(0);` from tx_misra.c. Every trace event is stamped zero. The buffer carries no timing, and the kernel's own check for an entry having been overwritten -- time_stamp against the entry's own stamp, in the block and byte allocates and in the system suspend and resume -- compares zero with zero, so it never fires and a service patches whatever now occupies the slot. Both ports now read in MISRA builds the clock they already read otherwise, _tx_linux_time_stamp.tv_nsec, which TX_TRACE_PORT_EXTENSION refreshes on every recorded event in both forms of the insert. The non-SMP port's non-MISRA macro carried a trailing semicolon, which made it a statement and is why the MISRA insert -- which takes the time source as a function argument -- could not use it; that is dropped and the two branches become one definition. Both headers keep the _tx_misra_time_stamp_get declaration, because tx_misra.c still defines it and is compiled for these ports. The MISRA insert evaluates its time source before the callee refreshes the clock, so each entry carries the reading taken at the previous recorded event. Stamps are real, distinct and ordered, which is what the overwrite check needs. The trace entry update test gains an assertion that the buffer holds an entry the port actually stamped. It fails on dev with ERROR #13 under misra_trace_build and passes with this change. Suites green: 7/7 ThreadX configurations, 5/5 SMP. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-16Added MISRA build configurations to the ThreadX regression matrix (#747)Frédéric Desbiens
TX_MISRA_ENABLE routes the kernel's pointer conversions, its memset and its stack check through the shim in common/src/tx_misra.c. No build configuration defined it, so that file was never compiled or tested, and the source the MISRA analysis runs against was not the source the suite exercises. Adding event tracing alongside it reaches a further region of the shim that neither macro alone compiles. Both defects found in this area were invisible for the same reason: #741 broke under TX_MISRA_ENABLE and #745 under both macros together, and neither combination was built anywhere. misra_build and misra_trace_build fill that gap. Two things had to give way for them. TX_POINTER_TO_ALIGN_TYPE_CONVERT exists only when TX_MISRA_ENABLE is absent, so threadx_test_port.h writes the conversion out rather than taking it from the API; it is test scaffolding storing a pointer in a word wide enough to hold one, not kernel source. And thread_transition and module_manager compile hand-picked common/src sources directly instead of linking the library, which is what lets them reach feature macros the library-per-configuration model cannot; under TX_MISRA_ENABLE those sources call into the shim, and pulling the shim in pulls its own callees after it. Neither directory exists to test the shim, so the MISRA configurations skip them. 100/100 tests pass in each of the two new configurations, against 105/105 in the existing five - the five not run are the four thread transition tests and the module manager test, exactly the two directories skipped. Build is 4 to 5 seconds and the suites 13 and 15 seconds, against 4 seconds and 9 to 17 for the configurations already there, so the tx job grows by roughly forty seconds. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-16Decorrelated the wait abort ISR test's interrupt source from the thread it ↵Frédéric Desbiens
samples (#743) threadx_thread_wait_abort_and_isr_test waits for the periodic timer interrupt to land while the preempt disable flag is set. The same interrupt also puts the semaphore that wakes the thread being sampled, so the two run in lockstep: the tick wakes thread 0, thread 0 does a fixed amount of work and suspends, and the next tick arrives a fixed interval later with thread 0 in the same place every time. That is the resonance the handler's own comment describes, and perturbing the handler's duration only shifts the phase rather than breaking the correlation. The window itself is a few instructions wide, so a sampler locked to the thread's own cycle can miss it indefinitely, which is what #644 and #649 measured and worked around. The simulator's timer thread waits on _tx_linux_timer_semaphore with a one-tick deadline and delivers an interrupt early when the semaphore is posted, which the port already relies on in _tx_thread_schedule. The test now runs a plain POSIX thread that posts it, injecting interrupts at moments unrelated to the tick grid and sampling thread 0 at arbitrary points in its cycle rather than the same one. The injector posts only when nothing is outstanding, so interrupts can never be queued faster than they are serviced. It is confined to the Linux simulation port and no port file changes. The count of windows asked for goes back to ten, on the same reasoning that lowered it to three: ask for what a run can actually reach. Measured over six build configurations of a branch carrying this change, every one reaches ten of ten in under a second, against one to three of three previously with three of the six pinned at the 180 second budget. The budget and the zero window ceiling are untouched, so a run that somehow still falls behind behaves exactly as it does today. The SMP copy deliberately does not get the injector, and its count stays at twenty. It has never been in the slow mode, and injecting interrupts there measurably hurts: 8.9 seconds against 0 to 1 for the same twenty windows, which is the extra interrupt traffic contending across the simulated cores. Only the tx copy has the problem this solves, so only the tx copy changes; the budget and ceiling logic stays identical between them. Verified on this branch: the tx suite passes 105 of 105 with the test at 0.44 seconds, and four direct runs reach ten of ten in 0 to 1 seconds. The SMP suite passes 117 of 117 unmodified, with its own test at 0 to 1 seconds over four runs. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-15Normalized the AI disclosure comment to one fixed line per file (#740)Frédéric Desbiens
The per-edit disclosure named the product and model, so every agent and every model version appended another line. 74 files carried two to four of them, and the same five products had accumulated 13 spellings -- Copilot against GitHub Copilot, Claude Sonnet 4.6 against claude-sonnet-4.6, four spellings of Codex. Twenty assembly lines carried a doubled comment marker, `; //` or `@ //`. Every file now carries exactly one line, fixed text naming no product: Portions of this file were generated with AI assistance. It is written with the comment character that file already uses, so the `;` and `@` assembly files keep theirs and the doubled markers are gone. Precise attribution stays on the commit, where the Assisted-by trailer is per-change, dated and attached to the diff it describes. A header line cannot hold that record honestly, because the code it names gets rewritten and the line stays. A file-level flag answers whether; the history answers who. Comment-only. 455 files, 455 insertions and 574 deletions: every removed line was a disclosure line, every added line is the fixed text, and no file is left with zero or with more than one. `scripts/check_ports.sh` passes, including the reproducibility check that would catch a ports_arch master and its generated copies drifting apart. Recompiled against dev, every file that builds without a vendor toolchain gives a byte-identical object: 19 of 19 C files under common, 100 of 100 GNU assembly files, and all 16 assemblable files whose comment marker changed. The 10 remaining marker changes are ac5 and IAR sources where `;` already started the comment and only the redundant `//` was removed. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-15Completed Windows simulator support and regression coverage (#736)Frédéric Desbiens
Completed and stabilized the Win32 and Win64 MSVC simulator ports. - Replaced high-latency host synchronization with bounded scheduler handoffs and critical sections. - Added a high-resolution timer, tick batching, idle fast-forward, shutdown coordination and Win64 extension-pointer support. - Brought the Windows regression tooling and the new thread-transition tests up on CMake, Ninja and the Visual Studio Build Tools. - Extended the SMP teardown diagnostics and corrected 64-bit trace-test handling. This supersedes the historical `win64`, `win32-perf`, `windows-sim-ports` and `windows-sim-ports-completion` branches; no unmerged change from them is missing here. The original Win64 port landed in #529. 1,610 of 1,610 tests pass, across five configurations each: Win32 515, Win64 515, Win64 SMP 580. No external dependency was added, and the existing MSVC warnings in the trace configuration are unchanged. Hardware validation does not apply to host simulator ports. Assisted-by: Codex (gpt-5.6-sol) <[email protected]>
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-15Allowed tx_timer_change to be called from tx_application_define (#730)Frédéric Desbiens
Fixes #224 `_txe_timer_change` returned `TX_CALLER_ERROR` for any call at or above `TX_INITIALIZE_IN_PROGRESS`, making `tx_timer_change` the only timer service that could not be called from `tx_application_define` -- while still being allowed from an ISR. The check has no technical basis: `_tx_timer_change` only writes the expiration fields of a timer that is not on an active list, with interrupts disabled. Removed it from both the `common` and `common_smp` copies of `txe_timer_change.c`, with the now-unused includes and the `TX_CALLER_ERROR` line in the header comment. Relaxing an error check is backward compatible. `testcontrol.c` in both suites now calls `tx_timer_change` at initialization, so the timer simple test covers it: `ERROR #30` on `dev`, green with the fix. 116/116 SMP, 103/103 non-SMP. Assisted-by: Copilot (Opus 5) <[email protected]>
2026-09-15Guarded _tx_thread_stack_analyze against inverted stack pointers (#727)Frédéric Desbiens
Fixes #460 `TX_ULONG_POINTER_DIF` casts the pointer difference to `ULONG`, so when `tx_thread_stack_highest_ptr` sits below `tx_thread_stack_start` the midpoint wraps to a huge value, the probe lands outside the stack, and the search never converges: the caller hangs or faults. `_tx_thread_stack_analyze` now requires the highest pointer to be strictly above the start of the stack, and bounds the final scan by it. #464 covered the `TX_THREAD_STACK_CHECK` path; this covers direct callers too, as @billlamiework suggested on the issue. Inconsistent pointers still mean a real overflow or a corrupted control block, which remains the application's problem. What changes is that ThreadX reports it through the stack error handler instead of hanging. New cases for an inverted and an equal pointer pair crash the suite without the fix and pass with it. 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-08-20Asked the wait abort test for three windows, and failed a run that reached ↵Frédéric Desbiens
none (#649) Four CI runs of the same tree, twenty configuration-runs in total, show this test's budget being reached far more often than the first green run suggested, and a pass being reported every time it was: trace_build 3 of 10 windows in 121 seconds disable_notify 3 of 10 windows in 121 seconds default_coverage 4 of 10 windows in 121 seconds stack_checking 7 of 10 windows in 121 seconds trace_build 0 of 10 windows in 121 seconds disable_notify 7 of 10 windows in 121 seconds stack_checking 3 of 10 windows in 121 seconds Seven of twenty, and the shortfall message only ever reaches an artifact: ctest is run with --output-on-failure, so a passing test's output is not in the job log at all. The suite has been quietly losing most of this test's coverage in whole configurations and reporting green. The loop runs in two modes, not one. A window arrives in milliseconds in the fast mode, and costs between 17 and 40 seconds in the slow one, with nothing in between across those twenty runs. Ten windows are therefore unreachable inside any budget worth having: at 40 seconds each that is 400 seconds, and the unbounded runs measured before any of this took up to 726. Raising the budget to cover the slow mode would trade a quiet loss of coverage for five configurations approaching the sixty minute step timeout. So ask for what a run can reach. Three windows cost 51 to 120 seconds in the slow mode and under a second in the fast one, and the later hits repeat what the first ones establish, so what is given up is small. The budget goes to 180 seconds because three windows at the worst rate measured is exactly the 120 it was, which would have truncated at two. The count is printed on every run rather than only on a short one. A number that appears only on shortfall cannot be told apart from a number nobody recorded. Reaching the window no times at all is a different matter, and was the worst of the seven. The check after the loop compares semaphore bookkeeping that a window has to have touched to mean anything, so a run that reached none of them compares a counter against the value it was initialised to and reports a pass having verified nothing. That run now keeps trying to a 300 second ceiling, and fails if it still has not reached the window. A genuine resonance that holds for five minutes is worth a failure; the old behaviour was worth nothing. The SMP copy keeps its count of twenty. It reaches them in under half a second in all five of its configurations, in all four runs, so the slow mode has never been observed there and the coverage is free. Both copies get the ceiling and the unconditional report, so the logic stays identical between them. Verified locally on all five configurations: the test reaches 3 of 3 in 5 to 14 seconds, and the full suites pass 96 of 96 and 110 of 110 run one test at a time. With the handler's window made unreachable and the ceiling lowered to 5 seconds, the test stops after 6 seconds, prints the count it reached, and reports ERROR #8 with the harness recording a failure rather than a pass. With the count raised past what the budget allows, a run that reaches two windows still passes, so falling short and reaching nothing stay distinct. The TX_NOT_INTERRUPTABLE branch, which no configuration in either suite builds, was compile-checked in both copies with the configurations' own compile commands. Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-08-20Bounded the wait for the delayed suspension window (#645)Frédéric Desbiens
threadx_thread_delayed_suspension_test waits for an interrupt to land while thread 2 is part way through suspending, and waits for it with no bound: while(delayed_suspend_set == 0) { tx_thread_wait_abort(&thread_2); tx_thread_relinquish(); } How long that takes depends on the build to a degree that is easy to miss. The loop finishes in between a tenth of a second and three seconds in four of the five ThreadX configurations. In trace_build it took 490 seconds, which was 40 percent of the whole ThreadX suite and more than every other test in that configuration put together. This is the third test in these suites built the same way, after threadx_thread_priority_change and threadx_thread_wait_abort_and_isr_test: spin until an interrupt happens to land in a narrow window, with nothing to stop the spin if it does not. The other two have been given bounds already. Give this one a wall clock budget too, for the same reason as the last: a tick is delivered only when the port's timer thread runs, so the tick clock falls behind real time under load or instrumentation, and instrumentation is exactly what trace_build turns on. The check after the loop needs care that the other two did not. It compares thread_2_counter against thread_2_counter_capture, and the capture is taken inside the interrupt handler at the moment the window is hit. Leaving that check in place after a run that never reached the window would compare a live counter against the zero it was initialised to and report a defect that is not there. So the check is skipped when the window was not reached, and the run says so. Reaching the window still exercises it exactly as before. Verified both ways in trace_build, which is the configuration that was slow: the window is reached in 8 seconds here and the test passes as it always did, and with the budget forced to zero the test reports that the window was not reached and passes without the dependent check firing. Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-08-20Gave the wait abort ISR test a budget instead of an open-ended wait (#644)Frédéric Desbiens
threadx_thread_wait_abort_and_isr_test waits for an interrupt to land while the preempt disable flag is set, and waits for it ten times, twenty in the SMP copy, with no bound on how long that takes. The handler in the same file already says what can go wrong: It is possible for this test to get into a resonance condition in which the ISR never occurs while preemption is disabled and perturbs its own duration to break out of it. That helps but guarantees nothing, and if the resonance holds, the loop does not end. It is also, by a wide margin, the most expensive thing in either suite. Run one test at a time in CI it took between 148 and 726 seconds per configuration: 1936 seconds of the ThreadX suite's 2209, against 273 seconds for the other ninety five tests together. Nothing else in the suite is within two orders of magnitude of it. The budget is in wall clock seconds, not ticks. That distinction turned out to matter. A tick is delivered only when the port's timer thread gets to run, so the simulated clock falls behind real time under load or under coverage instrumentation, and never makes the loss up. A first attempt bounded the wait at 20000 ticks, nominally 200 seconds, and failed to stop a run that took 726 seconds, because fewer than 20000 ticks had gone by. tx_time_get() cannot bound elapsed time here; time() can. A run that falls short says how many windows it reached rather than going quiet, and the check after the loop is untouched. That check compares semaphore bookkeeping which holds whatever number of windows were hit, so it still means exactly what it did before. Hitting the race a few times rather than ten is a smaller loss than it looks: the value is in reaching the window at all, and the later hits repeat what the first ones established. Verified by forcing the budget to 3 seconds, where the test stops after 3.14 seconds of wall clock and reports reaching 0 of 10 windows, with the following check intact. At 120 seconds both suites pass every configuration run serially. Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-08-18Bounded the wait in the thread priority change regression tests (#640)Frédéric Desbiens
Both copies of this test, the SMP one and the non-SMP one, install an interrupt handler and then spin until it clears a flag: test_isr_dispatch = test_isr; do { ... } while (test_isr_dispatch); The handler clears that flag only on a narrow window: thread 3 at priority 6, ready, and not yet at the head of its priority list, which exists only part way through a priority change. If an interrupt never lands inside that window, the loop never ends. That is what has been failing in CI. The SMP suite has been red since 30 June, and this test times out in three of the last four failing runs, always in a stack-checking configuration. The evidence that it is a hang rather than slow work: the test carries no per-test timeout property, so ctest's --timeout 1000 applies, and locally the test finishes in 0.12 seconds with a worst case of 0.29 over thirty runs. Nothing turns that into more than a thousand seconds. It also survived --repeat until-pass:2, so it hung twice in succession. The TX_NOT_INTERRUPTABLE path in the same handler already stops after a fixed amount of work. Only the interruptable path, which is the one the failing configuration uses, had no protection. Cap the loop and clear the handler on the way out. When the window is not reached the test says so and still passes: not reaching it is a gap in what this run covered, not a fault in the code under test, and failing would report a defect that does not exist. The counters are left alone so the checks that follow keep their previous meaning. The cap is 100000 attempts. An exhausted cap takes about 60 seconds, measured, and a successful run takes 0.12 seconds, which puts the usual cost around two hundred attempts and leaves the cap roughly two orders of magnitude clear of it. That is wide enough not to lose coverage on a slower machine, while replacing a timeout that says nothing with a message that says what happened. Not reproducible here, which fits the diagnosis rather than contradicting it: on sixteen cores the window is hit almost at once. Thirty sequential runs, two hundred at parallelism thirty-two, sixty pinned to two CPUs, forty pinned to one, and three full-suite passes at the parallelism CI uses all came back clean. The defect is the reliance on the window, not any particular machine. Verified with the window deliberately made unreachable: before this change the test runs until it is killed, and after it exits in about a minute reporting that the window was not reached. The full SMP suite passes 110 of 110 at CI's parallelism.
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]>
2026-05-19Add QEMU based CI regression test infra for RV32 and RV64 (#526)Akif Ejaz
Added a QEMU virt-machine BSP and CTest infrastructure to run the ThreadX regression suite on both RISC-V 32-bit and 64-bit targets in CI. New components: - BSP (entry, trap, PLIC, CLINT timer, UART, linker script) targeting QEMU virt machine for RV32 and RV64 - CMake build system with Ninja, supporting multiple build configs - CI scripts: install_riscv.sh (toolchain + QEMU), build_tx_riscv.sh, test_tx_riscv.sh - GitHub Actions workflow job for RISC-V regression gating Port fixes: - RV32 tx_thread_context_restore.S: set MPIE alongside MPP (0x1800 → 0x1880) so mret re-enables interrupts - RV32/RV64 tx_port.h: add TX_REGRESSION_TEST extension macros needed by the test harness - RV32/RV64 example_build scripts: add compile and QEMU launch steps Regression test portability fixes: - Block memory tests: increase pool sizes (320 → 340) to accommodate larger RISC-V block-header alignment - Byte memory test: replace hardcoded offsets with BYTE_POOL_OVERHEAD macro for portable pool-size computation - Event flag timeout test: make counter tolerance unconditional, removing linux-only guard Signed-off-by: Akif Ejaz <[email protected]>
2026-04-15Merge branch 'dev' into typoFrédéric Desbiens
2026-03-05Updated copyright headers and version number constants (#509)Frédéric Desbiens
* Updated version number constants * Removed revision history from all files * Added Eclipse ThreadX contributors' copyright header
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.
2024-02-24fix the typosYang Hau
2023-05-12Add random number stack filling option. (#257)Xiuwen Cai
Co-authored-by: TiejunZhou <[email protected]>
2023-04-17Fix path to test reports in pipeline (#247)TiejunZhou
* Fix path to test reports in pipeline * Fix test case when CPU starves, the thread 2 can run 14 ronuds.
2023-04-14Fix random failure in test case threadx_event_flag_suspension_timeout_test.c ↵TiejunZhou
(#246) Depending on the starting time, thread 1 can run either 32 or 33 rounds.
2023-04-04Release ThreadX regression systemTiejun Zhou