summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
9 daysRestored the Linux port mutex retryv6.4.1_certFrédéric Desbiens
This was reverted because a NetX Secure regression test segfaulted with it in place. The cause was the test: it hands the certificate verify path a stack-local certificate whose signature it never assigns, and this change moves the stack layout that decides whether the unassigned slot reads null or a readable leftover. That test has been fixed, so the retry comes back. Measured with the test unfixed and this change in place, on a layout built without the stack protector: eight of eight runs segfault, the fault read from the faulting process as a null signature dereferenced by the ECDSA verify. With the test fixed, nineteen build configurations run it eight times each with no fault. Without this change, a thread signalled into the port's suspend handler never returns to the contended-mutex loop, the next unlock's wake-up is lost, the scheduler is left parked, and the timer interrupt never runs -- which stalls the tick and hangs about 0.39% of instances of a few TCP tests for their full timeout.
2026-09-30Reverted the Linux port mutex retry on this branchFrédéric Desbiens
The backport fixes a real deadlock and is right in itself, but on this branch it reproducibly breaks the NetX Duo nx_secure suite: nx_secure_tls_coverage_test segfaults in 8 of 19 configurations on one run and 9 of 19 on a re-run of the same commit, against four consecutive runs with none before it. Whether the retry causes that or merely exposes something latent is not known. The first guess, a kernel object outliving its stack frame, is wrong: that test's sessions are file scope and its creates and deletes balance. The deadlock the retry cures costs one configuration's coverage data at roughly 0.4% of test instances, which the union absorbs, so the branch is measurably worse with it than without while the interaction is unexplained. This is a deferral, not a dismissal. The diagnosis stands, the reproduction is recorded, and re-applying is one cherry-pick. The upstream fix is unaffected. This reverts commit ac95c32c. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-30Stopped the Linux port losing a mutex wake-up to a suspend signalFrédéric Desbiens
The port suspends a thread with a signal whose handler calls sigsuspend and does not return until resumed, and takes its critical section with a bare pthread_mutex_lock. A thread signalled while parked on _tx_linux_mutex never returns to glibc's contended-mutex loop, so the next unlock's wake-up goes to a thread that will not act on it and every other waiter is left parked on a mutex that is free. tx_linux_mutex_lock now calls a helper that waits with pthread_mutex_timedlock and retries every TX_LINUX_MUTEX_RETRY_NSEC, one millisecond, so a lost wake-up costs a retry period instead of the process. The original fix was made by inspection, on a port never observed to deadlock. It has now been observed. A stalled regression test reads the mutex free, owner zero, with the scheduler still on its futex word; it never posts _tx_linux_isr_semaphore, the timer thread never returns from _tx_thread_context_restore, and the simulated clock stops. Bounding a wait cannot save it: one capture held 116 ticks of timeout unchanged across 40 seconds. Measured on netx_tcp_overlapping_packet_test_10, six workers on four pinned CPUs: 5 stalls in 1,266 runs before, 0 in 2,034 after, three held frozen through 60 seconds of untraced /proc sampling. netxduo/default_build_coverage is unmoved at 914/914, 121 skipped, 497 files, 11230/11256 lines and 6912/6925 branches. (cherry picked from commit b567428f2a826fa24e78fc82f6a5dd46a87b079c) Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-27Added an opt-in container options input to the regression templateFrédéric Desbiens
The tests run in a container started with the default capability set, which holds CAP_NET_RAW and not CAP_NET_ADMIN. That is right for every suite that only compiles and runs code, and wrong for the two NetX Duo interoperability suites: each of their tests builds a veth pair and addresses it, so every configuration fails on its first test with "RTNETLINK answers: Operation not permitted" before anything under test has run. Measured on the certification branch, not predicted. The container now takes an options string from the caller, empty unless a caller sets it, so the two jobs that need CAP_NET_ADMIN ask for it and nothing else changes. Granting it here for everyone would widen the privileges of five suites with no use for it. Proof: the template parses and the input defaults to empty, so every existing caller produces the same container arguments as before. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-23Excluded the SMP regression-test hook code from the coverage figureFrédéric Desbiens
Every SMP build configuration is compiled with -DTX_REGRESSION_TEST, which opens a block in the Linux SMP port header defining ten macros that expand to live code -- flag tests, state changes and calls -- at eleven sites in common_smp/src. An application defines none of them and tx_api.h expands them all to nothing, but gcov attributes a macro expansion to the file that invokes it, so the harness's own scaffolding was being counted as certified source. coverage_union.py now drops those sites and prints what it dropped, covered and total on both axes, so the exclusion can be audited rather than inferred. One site keeps its line: tx_api.h defines TX_TIMER_INITIALIZE_EXTENSION as a real statement under TX_MISRA_ENABLE, so that line ships and only its branches come out. A listed site that no longer matches the report is a hard failure naming the macro, not a silent skip. The branch gate moves 99.05 to 99.04 because the margin is ten outcomes rather than a percentage, and a smaller denominator buys nine at the old value; the line gate is unchanged. The excluded code measured 10 lines and 36 branch outcomes, every one of them covered, so the uncovered set is untouched: the same 24 outcomes at the same 22 sites in the same 13 files, over the same 194 files. Eight configurations from a wiped tree, 136/136 tests in each, 0 failed. The union goes from 5449/5450 lines and 3588/3612 branch outcomes to 5439/5440 and 3552/3576, re-derived key by key against the previous measurement. gcovr's merged report is published unchanged. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-23Excluded the regression-test hook code from the coverage denominatorFrédéric Desbiens
The test build defines TX_REGRESSION_TEST for every configuration, which opens a block in the Linux port header defining eight *_EXTENSION macros. They expand to live code -- flag tests, state changes and calls -- at eight sites in common/src, and an application defines none of them and compiles none of it. gcov attributes a macro expansion to the file that invokes it, so the harness's own scaffolding was being counted as certified source. coverage_union.py now drops those expansions and prints what it dropped beside the configurations it merged, so the exclusion is auditable, and a site that no longer matches the report fails the run rather than being skipped. tx_timer_initialize.c:275 keeps its line and loses only its branches: it is the one macro whose tx_api.h fallback is not empty, so under TX_MISRA_ENABLE that line still carries a statement in a build that ships. gcovr's merged report is not touched. The hook code is 8 lines and 22 branch outcomes, all covered, 0.183% of lines and 0.694% of outcomes. The union goes from 4383/4383 and 3150/3170 to 4376/4376 and 3128/3148: line coverage stays at 100.00%, and the branch rate falls from 99.36909% to 99.364676%, since removing an all-covered block from a figure below 100% moves it down. Both thresholds are unchanged and both pass, the branch one by 0.15 of an outcome. Seven configurations from a wiped tree, 115/115 tests in each, and the twenty uncovered outcomes are the same twenty at the same sixteen sites. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-21Corrected the gate comment's count of single-carrier outcomesFrédéric Desbiens
The comment added in the previous commit said six outcomes in the figure have no configuration to cover for them. That was the count of outcomes that both have a single carrier and were seen moving between runs, not the count of single-carrier outcomes, and as written it is wrong. Measured over sixteen runs at two commits: thirty-five outcomes are compiled by two configurations and covered by only one of them in at least one run, almost all TX_THREAD_STACK_CHECK fence ordinals that stack_checking_build holds alone because its random-fill sibling short-circuits before them. A further 894 are compiled by exactly one configuration at all, which is structural rather than fragile, the racing outcome excepted. The margin's justification is restated on the figure that actually sizes it: the largest movement observed in any one configuration is eight outcomes, and the margin is ten. No threshold changes. The gate stays at 99.79 line and 99.05 branch, and sixteen runs read 5449/5450 lines and 3588/3612 branch outcomes on the union basis with an empty unstable set on both axes. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-21Settled the SMP coverage gate margin on the evidence measurementFrédéric Desbiens
The gate's comment left open whether the ten-line, ten-outcome margin could be removed once the unstable set closed, and deferred the answer to the coverage evidence work. It has now been measured. The thresholds do not move, and the comment records why. Read per configuration over eight CI runs at one commit, eight branch outcomes and four lines move between runs, and six outcomes in the published figure are compiled by only one configuration that covers them: five TX_THREAD_STACK_CHECK arms that stack_checking_build carries alone in most runs, because stack_checking_rand_fill_build misses them in seven and four runs of eight, and the trace race outcome, which only trace_build compiles. The union figure is stable because the eight configurations cover for each other, not because the suite is deterministic, so a zero-margin gate would read a runner's timing as a regression. Eight runs at the preceding commit read 5449/5450 lines and 3588/3612 branch outcomes identically on the union basis, unstable set empty on both axes, 136/136 tests in each of the 64 configuration-runs. gcovr's merged report over those same runs reads 6561, 6563 and 6564 of 6601, which is the same movement seen through a denominator that does not collapse it. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-21Raised the SMP gate onto the floor the trace race test liftedFrédéric Desbiens
The coverage ratchet sat at 99.03 branch, set below the 3587 outcomes every run of the previous sample reached. Covering the user event insert's disabled-buffer arm moves that floor by one. Raised to 99.05, still ten outcomes below the set covered in every run, which is now 3588 of 3612. The line gate is unchanged at 99.79: neither the line total nor its denominator moved. The comment above the thresholds gains the one thing about this figure a later reader has to know -- a single outcome in it is reached by two cores racing rather than by a thread doing something in order, and it is counted in the always-covered set on a measurement over ten runs on two machines rather than on one sample. Seven CI runs at the test head, all green, 136/136 in each of eight configurations, 5449/5450 and 3588/3612 in every one. Two clean local cycles read the same two totals. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-21Covered the user event insert's own test of a disabled trace bufferFrédéric Desbiens
_tx_trace_user_event_insert tests the trace buffer pointer and returns TX_NOT_DONE if it is null; the insert sits in the else, and the macro reads the same variable and tests it again a few instructions later, inside one protection with nothing between. The second test's false arm had no route on one core and was carried as unreachable. It is an ordinary state of the running kernel on SMP, and no fault has to be invented for it. _tx_trace_disable clears that pointer and takes no lock of any kind, so a thread disabling tracing on one core while another is inside the insert clears it under that core. A new test drives 300,000 inserts against a second thread on a core of its own writing the same store the disable service writes. No kernel symbol is replaced and both services run their own code from end to end; the test asserts that the two threads held different cores and that the writer's stores reached the service, and says in the file that the arm itself is evidenced by the coverage report because nothing outside the macro can observe it. The pass count is sized from measurement on the slower of two machines. The arm is taken 49 to 132 times per run on this workstation over three runs and 521 to 1298 times per run on a CI runner over seven. 136/136 in each of eight configurations, twice from clean, 3588/3612 branch outcomes on the union basis against 3587/3612 before. Fifty standalone repeats in each of the eight, no failures. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Raised the SMP gate onto the new denominator and floorFrédéric Desbiens
The coverage ratchet sat at 99.79 line and 98.97 branch against a branch denominator of 3610. The port hook's new clause adds two covered outcomes to tx_byte_allocate.c, so the denominator is 3612, and the sweep closed two further outcomes on top of that. The branch gate goes to 99.03, still ten outcomes below the 3587 of 3612 covered in every run of the sample. The line gate is unchanged at 99.79: neither the line total nor its denominator moved. The comment above the thresholds records why the denominator moved, and that the unstable set is now empty in two consecutive samples on two different trees rather than one. The margin stays for the reason it was kept before -- five earlier samples moved an outcome in every direction there is -- and whether it can go is the evidence step's question. Seven CI runs at the sweep head, all green, 135/135 in each of eight configurations, 5449/5450 and 3587/3612 in every one. Two clean local cycles read the same two totals. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the second MISRA loop the always-true shim controlsFrédéric Desbiens
TX_LOOP_FOREVER appears twice in common_smp/src. Under TX_MISRA_ENABLE both expand to a call on _tx_misra_always_true rather than to a literal, so each carries an exit arm the kernel never takes. The timer thread's loop was already covered by replacing that shim; the per-core state initialisation loop in _tx_thread_smp_current_state_set was not, because the kernel reaches it only from the three initialisation paths that run before the scheduler starts a thread. The existing test drives it directly instead, with the shim already armed and the timer thread already ended, so nothing else in the run can reach the shim while it is refusing. The service is handed back the state its highest core already holds, so the one assignment a shortened loop performs writes the value that was there; the test asserts afterwards that every core's state is the one it saw before the call, and that the shim refused exactly twice. No second symbol is replaced and no test is added to the list of those that replace one. 2/2 at tx_thread_smp_current_state_set.c:97, seven executions of the loop and one exit, measured with the coverage isolated to this test. 135/135 in each of eight configurations and 3587/3612 branch outcomes on the union basis. Fifty standalone repeats in each of the eight, no failures. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the byte allocate's overwritten trace entryFrédéric Desbiens
The byte allocate records a trace event on the way in, saves the entry's time stamp, and comes back to write the address it returned into that same entry -- but only if the time stamp says nothing overwrote the entry meanwhile. Nothing in the kernel writes a trace event inside the window, so the other arm had no agent and no test. The Linux SMP port's TX_BYTE_ALLOCATE_EXTENSION gains a second clause, guarded by its own flag and calling a routine the harness declares weakly, in the shape TX_BYTE_RELEASE_EXTENSION already uses. The hook runs between the restore and the disable that bracket the pool search, which on SMP is a real release of the protection rather than an interrupt gap. A new test filters the byte pool event class so the allocate's own insert writes nothing and leaves the buffer parked on the saved entry, then lets the hook write a user event onto it; neither lever reaches the arm without the other. No kernel symbol is replaced and the allocate runs its own code end to end. The macro expands inside tx_byte_allocate.c, so the new clause's if is certified-source branch: the union branch denominator moves from 3610 to 3612 and both new outcomes are covered. The arm closes in trace_build alone. Under TX_MISRA_ENABLE the port's trace time source returns a constant, so the comparison reads 0 == 0 whatever the hook writes, and the test asserts the consequence only where the time source is real. 135/135 in each of eight configurations, 3586/3612 branch outcomes on the union basis against 3583/3610 before. Fifty standalone repeats in each of the eight, no failures. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Raised the SMP gate onto the floor the trace batch liftedFrédéric Desbiens
The coverage ratchet sat at 99.32 line and 97.42 branch, set below the 5423 lines and 3527 branch outcomes every run of the previous sample reached. The initialize, trace and MISRA shim batch has moved both floors. Raised to 99.79 and 98.97, still ten lines and ten outcomes below the set covered in every run of the sample, which is now 5449 lines and 3583 outcomes of 5450 and 3610. The comment above the thresholds is rewritten around the fact that made this sample different: all seven runs read the same two totals, so the unstable set is empty for the first time and the always-covered floor and the worst observation coincide. The margin stays, because five earlier samples moved an outcome in every direction there is, twice on the pair this batch has just closed; whether it can go is the evidence step's question and not this one's. Seven CI runs at the batch head, all green, 134/134 in each of eight configurations, 5449/5450 and 3583/3610 in every one. Two clean local cycles read the same two totals. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the enable's inserts with their own event class filteredFrédéric Desbiens
The two inserts the trace enable writes read an event class the same call sets to all ones a few hundred lines above them, so from inside the enabling thread neither can find it disabled. A buffer-full notification reaches the second one, because it is called from inside the first; the first has no lever of that kind and sat at 10 of 12. SMP has one the monoprocessor does not, and it is an arrangement rather than a race because the enable blocks. tx_trace_event_filter is an unprotected read-modify-write of the enable bits, while the enable takes the SMP protection after setting them and long before either insert, so a thread already holding the protection on another core parks the enable there for as long as it likes. A new test takes the protection from a core of its own, waits for the bits, clears every class and releases; the enable runs on and both inserts find the class gone. It asserts the consequence rather than the timing - neither insert wrote an entry, so the buffer is where the enable left it. A first version that waited for the window instead of holding it open was measured and discarded: it failed 11 runs of 400, and widening the window eightfold only took that to 3 of 100. tx_trace_enable.c:391 goes to 11 of 12 and :392 stays at 12. 133/133 in each of the eight configurations, and 50 standalone repeats of the new test green in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the three registry decisions ordinary use never reachesFrédéric Desbiens
The trace object registry carried one uncovered line and three uncovered branch outcomes. The name copy stops at the string's terminator, so it never left the loop by its own count and never met a caller passing no name at all. The search that reuses an entry prefers a later free one unless the earlier one has never held a valid object, and nothing the kernel registers produces an entry that is both in use and of no valid type. A new test registers an object whose name is longer than a registry entry keeps room for, one with no name, and one of the type the registry treats as not valid. It then fills the registry, frees the not-valid entry last so the search starts there, registers again, and asserts the new object landed in the entry the search kept rather than in the later free one. The registry is rearranged under one SMP protection, because every service that creates an object writes it. tx_trace_object_register.c goes to 66/66 lines and 42/42 branch outcomes on the union of the eight configurations, from 65/66 and 39/42. 133/133 in each of the eight, and 50 standalone repeats of the new test green in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered both trace inserts the enable service writes itselfFrédéric Desbiens
The enable records two running events so that a buffer handed to an analysis tool is never empty, and those two inserts are the only ones in the kernel that run inside the service that turns tracing on. The buffer pointer and the event enable bits are both set a few lines above, so neither can be found in any other state on the way in, and both inserts sat at 4 of 12. A new test enables with a buffer sized from the kernel's own structures to hold exactly one event, which puts both inserts on the wrap boundary, and with three notifications: one that counts, one that filters the running event's own class, and one that turns tracing off. The last two are what reach the second insert's filtered and no-buffer arms, and both are ordinary application behaviour rather than a contrivance - a notification that stops tracing is what an application capturing a single bufferful of history does. The forced-state passes hold one SMP protection across the write, the enable and the restore. tx_trace_enable.c:392 goes to 12 of 12 and :391 to 10 of 12 on the union of the eight configurations, from 4 each. The two arms left are the first insert's own copies of the filtered and no-buffer tests, which nothing can reach between the assignments above it and the insert. 133/133 in each of the eight, and 50 standalone repeats of the new test green in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the four trace services that carry an insert of their ownFrédéric Desbiens
The interrupt control, the two ISR inserts and the user event insert are part of the tracing machinery rather than users of it, and nothing drove their inserts beyond the conditions testcontrol.c and threadx_trace_basic_test.c happen to present. The two ISR inserts were the whole of this suite's unstable set: their buffer-wrap arms were reached when the buffer happened to wrap under an insert, which is why they moved between runs. A new test walks the services once per condition the insert distinguishes, and positions the buffer one entry short of its end rather than waiting for it to wrap, under one SMP protection and asserting the wrap per service. The interrupt control is driven at the outermost level only, and is left out of the pass that runs from interrupt context: _tx_thread_interrupt_control takes the Linux critical section on every call and hands it back in full only on an enable, so a pair completed where the section is already held leaves it two levels deeper and locks the process. Its disable half runs inside the protection, where the insert is, and its enable half once the protection is released. Its own forced-state passes reach the two arms the interrupt pass would have covered. All four sites go to 12 of 12 on the union of the eight configurations, from 7, 5, 5 and 6. 133/133 in each of the eight, and 50 standalone repeats of the new test green in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the MISRA shim's stack check and its two conversionsFrédéric Desbiens
Three regions of tx_misra.c had no caller in any of the eight build configurations. The thread stack check is reached through TX_THREAD_STACK_CHECK, whose call sites sit inside TX_ENABLE_STACK_CHECKING and which is redirected to the shim only under TX_MISRA_ENABLE, and no configuration defines both. The user timer derivation's six call sites are behind TX_TIMER_ENABLE_PERFORMANCE_INFO, three of them behind TX_TIMER_PROCESS_IN_ISR as well. The trace entry conversion has no caller in common_smp/src at all. A new test drives all three through the macro the kernel itself uses, so the shim and the in-line form the other builds compile are asserted to agree. The stack check is the one with logic in it, and is given a scratch thread one fault at a time: a cleared identifier, either stack fence overwritten, the recorded high-water mark moved above the stack pointer and below the stack. That thread is created with TX_DONT_START on a fenced buffer of the test's own and never runs, so no faulted state is ever visible to the scheduler. tx_misra.c goes from 138/163 lines and 16/30 branch outcomes to 163/163 and 30/30 on the union of the eight configurations. 133/133 in each of the eight, and 50 standalone repeats of the new test green in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Raised the SMP gate onto the floor the pool batch liftedFrédéric Desbiens
The byte pool and block pool batch moved the union figure from 5398 lines and 3399 branch outcomes to 5423 and 3531, and the gate was still set for the figure before it. A gate that trails the work by a whole batch stops being a ratchet. Set from the set of lines and outcomes covered in every one of seven CI runs -- 5423 and 3527 -- ten lines and ten outcomes below it, which is the basis the comment above the thresholds describes and the same margin the last four raises used. The always-covered branch total went 3395 to 3527; the line total, 5398 to 5423. Seven runs at this tree read 5423 lines in every one of them and between 3527 and 3531 outcomes. Four outcomes at two sites remain unstable and both sites belong to the trace batch that has not run yet; the line axis has now been stable across thirty-five runs in five samples. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Reduced the byte pool owner change test to a single claim threadFrédéric Desbiens
The test put one claim thread on every core the test thread was not using, and waited for all of them to be claiming before it started searching. On a machine with fewer processors than the port simulates cores that is not a safe wait: the claim threads walk the pool holding the protection, so two of them can keep the third from making any progress, and the test reports the arrangement as never having arrived. One CI run in seven failed that way. One claim thread is all the construct needs. It asks whether the pool can change hands under a walk, and a single other core answers it; further claim threads spend most of their time contending with each other rather than with the thread under test. The walk count goes up from five to twenty to keep the margin the extra threads were providing. Between 360 and 1,320 changes of pool ownership per run over six runs in the two configurations that schedule it slowest, against the one the coverage needs, in 2.5 seconds. 129/129 in each of eight configurations with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the byte pool search retry when the pool changes ownerFrédéric Desbiens
Where the SMP byte pool search gives the protection up, it checks on taking it back whether the pool is still its own, and if not it delays, resets the walk and claims the pool again. Three branch outcomes and eight lines of that were never executed. The pool owner is written in six places, all of them kernel services, and none can run on the searching thread's own core while that thread is inside the walk, so the construct needs a second core in use. The existing contention test does not reach it because its pool is too small to open the window at all: the protection is only given up once per multiple block search limit. A new test keeps the test thread on its own core and puts one claim thread on each of the others, each making the same unsatisfiable request and so walking the same pool. A claim thread is then usually already holding the protection when the test thread reaches a window, which is the state the construct is written against. The test waits for every claim thread to be mapped and claiming before it starts searching, rather than sleeping a fixed number of ticks, because how long the rebalance takes varies by configuration. It asserts the arrangement rather than the retry, which restores the owner the walk had already set and leaves nothing a test can read. Three outcomes and eight lines, measured at 48,971 changes of owner in a run of 2.6 seconds. This is the second test in the suite that needs more than one core. 129/129 in each of eight configurations with retries disabled, and sixty standalone repeats clean in the three configurations that schedule it slowest. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the byte pool search past its multiple block limitFrédéric Desbiens
The SMP byte pool search walks the pool holding the protection and gives it up every TX_BYTE_POOL_MULTIPLE_BLOCK_SEARCH blocks, so that a long walk does not lock the other cores out for its whole duration. No test fragmented a pool past that limit, so the release and the reacquire behind it were never executed and the branch that guards them stood at one of two. The region has no monoprocessor counterpart: that search gives the protection up on every block and has no counter. A new test fills a pool with small fragments until it refuses another, checks it broke into at least twice the limit, and then asks for one more. Every block is then in use or too small, so the search examines all of them and crosses the limit several times. The walk length is the pool's fragment count plus one and nothing else runs, so there is no timing in it. The limit is not a feature gate: tx_api.h defines it to 20 unless the application overrides it, so the region is compiled in every configuration. One outcome and three lines, in all eight configurations. 129/129 in each with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the byte allocate trace update on both of its pathsFrédéric Desbiens
The byte allocate keeps the same trace update in place the block allocate does, in the same two regions, and it was uncovered for the same reason: no test had run the service with tracing enabled, so the entry pointer checks stood at one of two and the time stamp comparisons at zero of two. A new test drives it with the three passes the block pool test uses. One configuration reaches less of it than the others and that is a property of the port rather than of the test: under TX_MISRA_ENABLE the trace time source is a routine whose body returns a constant, so every entry carries the same time stamp and the comparison reads equal whatever has been written over the entry. The overwritten round drives the same arm as the intact one there, and the arm that declines to update is reached in the plain trace configuration alone. Seven of the eight outcomes and all seven lines. The remaining one is the false arm of the immediate path's comparison. 129/129 in each of eight configurations with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the block allocate trace update on both of its pathsFrédéric Desbiens
The block allocate records its event, saves the entry and its time stamp, and comes back to write the address it handed out into that entry if nothing has overwritten it. There are two such regions, one on the path that finds a block immediately and one on the path that suspends. Nothing had ever run the service with tracing enabled, so the entry pointer checks stood at one of two with only the false arm taken and the time stamp comparisons at zero of two. A new test uses two threads and three passes: a plain successful allocate, a suspending allocate handed a block with its entry intact, and the same handover with the trace buffer written all the way round in between so the service correctly declines to update. The helper runs at a higher priority than the test thread, so every handover is a preemption rather than something that waits for a tick, and both threads keep the suite's default core mapping so only one of them runs at a time. Seven of the eight outcomes and all seven lines. The remaining one is the false arm of the immediate path's comparison, whose window is one unbroken SMP protection with no trace insert in it. 129/129 in each of eight configurations with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the byte pool trace inserts in every state the macro testsFrédéric Desbiens
Six byte pool services carry an in-line trace insert, and no test in the SMP suite drove any of them with tracing enabled. Four sites stood at one of twelve branch outcomes and the create and the delete at five of twelve, so 58 outcomes of the macro's six conditions were never taken. A new test walks the six services once per condition, in the shape the block pool test uses. The two subsystems differ in what an interrupt may call and the test reads that out of each error checking shell rather than assuming symmetry: the byte allocate and the byte release are rejected at any non-zero system state below initialisation, where the block pool permits both, so only the prioritize and the information get run from the ISR pass here. 58 of 58 outcomes, every site at twelve of twelve. 129/129 in each of eight configurations with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the block pool trace inserts in every state the macro testsFrédéric Desbiens
Six block pool services carry an in-line trace insert, and no test in the SMP suite drove any of them with tracing enabled. Four sites stood at one of twelve branch outcomes and the create and the delete at five of twelve, so 58 outcomes of the macro's six conditions were never taken. A new test walks the six services once per condition: thread context, the event class filtered out, interrupt context through the ISR dispatch, initialisation context, the state an interrupt presents, and a buffer wrap with and without a notification registered. The last two passes go through the internal entry points, because the error checking shell rejects a create from an interrupt and a delete from any caller that is not a thread. The buffer is positioned one entry short of its end and the positioning and the call are held under one SMP protection, so the boundary entry is the service's own by construction; the pass asserts a wrap per service rather than in total. 58 of 58 outcomes, every site at twelve of twelve. 129/129 in each of eight configurations with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Gave the multiple timer test margin on both of its tick countsFrédéric Desbiens
threadx_timer_multiple_test changes three timers to reschedule periods of one, two and three ticks and then requires their counters to read exactly 103, 53 and 36 after a 200 tick sleep. The first of them changes on every tick, so that equality has no margin at all. It failed one clean eight-configuration run and two of 220 standalone runs, both in misra_trace_build. The margin sweep that cleared this test measured its 200 tick reschedule and not the one tick one it takes on afterwards. The rescheduled counts are now checked against the window that actually elapsed, which is the shape the accuracy test already uses: a timer cannot expire more often than its period allows over that window, so that is the ceiling, and the floors allow five expirations of lag while staying far tighter than any period error. The earlier count of two holds anywhere between 300 and 499 ticks but was read at 301, one tick above the expiration that produces it and before the system timer thread need have processed it; that sleep goes to 310. 100 standalone runs in misra_trace_build, the configuration it failed in, all green. With the first timer's reschedule changed from one tick to two it reports ERROR #16 and exits 1. 123/123 in each of eight configurations from clean, with the coverage union unmoved at 5398/5450 lines and 3397/3610 branches. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Raised the SMP gate onto the floor the semaphore batch liftedFrédéric Desbiens
Closing the trace inserts, the zero-request refusal and the two prioritize retry arms across the semaphore, mutex and event flags sources took the set of branch outcomes covered in every run from 3195 to 3395, and the set of lines from 5396 to 5398, so the gate no longer sits ten below either. Re-derived on the union basis over a fourth sample of seven CI runs: 5398 of 5450 lines and 3395 of 3610 outcomes are covered in every one, and the gate is set ten below each, at 98.86 and 93.76. What is left unstable is four outcomes at two sites, up from two at one, and the comment above the thresholds records why that is not a regression to chase: the buffer-wrap arm of the ISR enter insert was unstable over fourteen runs, covered in all seven of the next sample with nothing touching it, and is missed in one run of seven again here. Four samples have now moved an outcome in every direction the three columns allow. Seven CI runs at this commit's parent, all green, 123/123 tests in each of eight configurations, retries disabled, and two clean workstation runs besides. The line axis did not move within the sample at all. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the priority change that does not beat an inherited priorityFrédéric Desbiens
The SMP priority change helper places a ready thread on its new priority list and then chooses which priority the thread should carry: the new one if it beats the priority inherited from a mutex, and the inherited one otherwise. The second arm and the two lines it guards had never run. Reaching it needs three things at once — a thread holding an inheritance mutex, ready rather than running, and being moved to a priority no higher than the one it has inherited — and nothing in the suite arranged all three. A new test arranges them. The owner holds two inheritance mutexes with a waiter on each, so its inherited priority comes from the higher waiter; deleting that waiter's mutex hands the owner back to the priority the remaining waiter calls for, which is the inherited priority the helper then has to choose against. The delete is issued by a thread above both waiters, so the owner is ready and not running when the change arrives, which is what sends it down the helper rather than down the full rebalance. Every thread in the suite is mapped to core 0 alone, so this needed no second core. The site reads two of two and both lines are covered, in all eight configurations. 123/123 tests pass in each of them, and the new test passes fifty standalone repeats in every one. The arrangement is its own test rather than part of the mutex work this batch wrote anyway: measured with the coverage isolated to a single test, neither the MISRA prioritize test nor the mutex trace test reaches the helper at all. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the retry arms of the two mutex put prioritize loopsFrédéric Desbiens
Under TX_MISRA_ENABLE the mutex put calls the prioritize processing through a retry loop, so that the call's result is read and the rule about discarding a return value is met. The prioritize processing passes a success status through a shim whose whole body returns success, so the loop's test can only be false on the first pass and the arm that goes round again never ran. The other six configurations compile a plain call with no branch at all. A new test defines the shim itself, which is the technique the monoprocessor suite already uses for the same two loops. The kernel is built shared and every internal reference to the shim goes through the procedure linkage table, so the definition in the test executable is the one reached, with no linker option and no preloading. A shim that withholds success on alternate calls sends each loop round exactly twice and leaves the mutex, the suspension list and the priorities as the kernel expects them. The two loops sit on different paths through a single put, and one release of an inheritance mutex with two suspended waiters reaches both. The shim's reach on this port was measured rather than carried over: objdump gives one call site in the library, in the prioritize processing, because the port overrides the other macro that could expand to it. Read the coverage with that in mind — it is coverage of a kernel whose MISRA shim is the test's, which the file says in its own header. Both sites read two of two in the two MISRA configurations. 123/123 tests pass in each of the eight configurations, and the new test passes fifty standalone repeats in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the event flags get refusal of a request for no flagsFrédéric Desbiens
The get service refuses to suspend a caller when the preemption lockout is raised or when the request asks for no flags at all. The first arm is driven by the suite's own preempt-disable safeguard; the second had no caller anywhere in the suite, so one of the site's four outcomes was never taken. Measured rather than inferred: the tracefiles put the uncovered outcome on the zero-request operand in all eight configurations. A new test asks the group for no flags under each of the four get options, with a wait option, and then confirms that the flag it raised beforehand is still set. The error checking shell places no constraint on the requested flags, so the published service reaches the refusal with nothing forced. A request for no flags can never be satisfied — an OR request finds none of the flags it asked for present, and an AND request has its empty result rejected for not matching the empty set it asked for — so a caller that suspended on one would never be resumed, which is what the check prevents. The site reads four of four on the union basis, which is the only genuine control logic this group had. 123/123 tests pass in each of the eight configurations, and the new test passes fifty standalone repeats in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the event flags trace inserts in every state the macro testsFrédéric Desbiens
The six event flags services carry the same in-line trace insert as the semaphore and mutex ones, with the same twelve outcomes from six conditions. Four of the six sites had never run the macro body and the create and the delete reached five outcomes of twelve, for the same reason: one test of 117 enabled tracing. A new test applies the pattern to the event flags services. The flag the set raises is consumed by the get that follows it and the scratch group is created by the first service and deleted by the last, so a pass leaves the system as it found it. The set notification service is compiled out under TX_DISABLE_NOTIFY_CALLBACKS and returns from above its own insert, which the per-service expected-status function absorbs. All six event flags trace sites read twelve of twelve, and the ten tx_event_ files are at 323/323 lines and 206/206 branch outcomes on the union basis. With this the twenty trace inserts across the three subsystems are closed and the thirty-three files of the group are at 1074/1074 lines. 123/123 tests pass in each of the eight configurations, and the new test passes fifty standalone repeats in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the mutex trace inserts in every state the macro testsFrédéric Desbiens
The six mutex services carry the same in-line trace insert as the semaphore ones, with the same twelve outcomes from six conditions, and the same cause left them uncovered: only one test in the suite enabled tracing. Four of the six sites had never run the macro body and the create and the delete reached five outcomes of twelve. A new test applies the pattern to the mutex services. The subset an interrupt may drive is narrower here and is decided by the shell rather than by the service: the error checking shell rejects a create, a get and a put from an interrupt and a delete from any caller that is not a thread, which leaves the prioritize and the information service for the interrupt pass. The resident mutex is created TX_NO_INHERIT deliberately, so a release with nothing suspended takes the simple path and leaves the calling thread's priority alone; the inheritance paths have tests of their own and nothing here depends on them. All six mutex trace sites read twelve of twelve. The eleven tx_mutex_ files are at 471/471 lines and 310/312 branch outcomes on the union basis; the two left are the exit arms of the TX_MISRA_ENABLE release loops, which are unreachable by construction and carry their own record. 123/123 tests pass in each of the eight configurations, and the new test passes fifty standalone repeats in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-20Covered the semaphore trace inserts in every state the macro testsFrédéric Desbiens
Each of the eight semaphore services carries one in-line trace insert, and the macro scores twelve branch outcomes from six conditions: whether tracing is enabled, whether the event class is filtered, what system state the caller is in, whether the entry wraps the trace buffer, and whether a buffer-full notification is registered. One test of 117 enabled tracing, so six of the eight sites had never run the macro body at all and the create and the delete reached five outcomes of twelve. A new test walks the services once per condition, in an order that leaves the system as it found it. The create and the delete are driven through their internal entry points in the forced-state passes, because the error checking shell rejects them from a non-thread caller and the insert sits behind it. The positioning and the call are held under one SMP protection, so the boundary entry is the service's own by construction rather than by being quick enough, and the pass asserts a wrap per service so that a service whose slot was taken by something else fails here instead of losing coverage quietly. One entry short is the right distance: every one of these inserts sits ahead of its own counting and suspension logic, so nothing the service goes on to do writes in front of it. All eight semaphore sites read twelve of twelve, and the twelve tx_semaphore_ files are at 280/280 lines and 194/194 branch outcomes on the union basis. 123/123 tests pass in each of the eight configurations, and the new test passes fifty standalone repeats in every one of them. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Raised the SMP branch gate onto the floor the walk test liftedFrédéric Desbiens
Closing the ready-list walk in the time-slice rebalance took the set of branch outcomes covered in every run from 3194 to 3195, so the gate no longer sits ten outcomes below it. Re-derived on the union basis over a third sample of seven CI runs: 5396 of 5450 lines and 3195 of 3610 outcomes are covered in every one, and the gate is set ten below each, at 98.82 and 88.22. The line threshold does not move. What is left unstable is two outcomes at one site, the buffer-wrap arm of the ISR exit trace insert, down from five at three sites. The comment above the thresholds records all three samples, including an outcome that came back covered in every run of the third with nothing touching it. Twenty-one CI runs across the three samples, all green, 117/117 tests in each of eight configurations, retries disabled. The margin earned itself immediately: the first clean workstation run after it was set read 3194, one outcome below the CI floor, on a stack check covered in all twenty-one CI runs. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered the whole of the ready-list walk in the time-slice rebalanceFrédéric Desbiens
When a thread's time slice expires the kernel moves it to the end of the ready list at its priority and walks the rest of that list looking for a thread to hand the core to, stopping at the first one it can use. The walk only runs to the end when every other thread at the priority is already the thread its own core is executing. Two tests reached that by accident of scheduling -- one in ten of twenty-five standalone runs -- so the arm read covered in twelve of fourteen CI runs and missed in two, which is neither a gap nor coverage. It needs more than one core in use, which is why nothing reaches it on purpose: with every thread mapped to core 0 alone there is one executing thread, and the walk stops at the first thread it looks at. A new test clears the core exclusions on three workers at one priority so they run one to a core, then asserts the arrangement instead of waiting for it. Two of the three ways out of the walk are ruled out before it runs, so the only early exit left is the one that hands the core over -- and that one overwrites the core's entry in the execution list, so the entry still holding the same thread afterwards means the walk ran to the end. The arrangement, the expiry and the check are held under one SMP protection, and the expiry is driven by calling the routine the timer interrupt calls, inside the protection the timer interrupt holds when it calls it. The arm is taken in 30 of 30 standalone runs against 10 of 25 before, and the test is green in 50 consecutive runs in each of the eight configurations. It is a tx_thread_ construct in a timer batch, deliberately: it belongs to a batch that closed two steps ago and no remaining batch would reach it. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Recorded the second variance sample behind the SMP coverage gateFrédéric Desbiens
The gate's thresholds were derived from one sample of seven CI runs, taken a commit before the one they ship with. A second sample of seven has since been taken at the head, and it puts the same 5396 lines and 3194 branch outcomes in the always-covered column and the same five outcomes in the unstable one. The comment above the thresholds now states both samples and the fact that they agree. The thresholds themselves do not move: a floor two independent samples report identically is the one the gate was already set ten lines and ten outcomes below. Fourteen CI runs, all green, 116/116 tests in each of eight configurations, retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Fixed a race between the two clock readings in the timer trace testFrédéric Desbiens
The test checks the published tx_time_get() against _tx_time_get() once, because the SMP header remaps the macro to a direct read of the system clock and every pass below then drives the service rather than the macro. The two readings were taken with the tick running between them, so a tick landing there made them differ and failed the test. Measured at one run in two hundred. Both readings now happen inside one SMP protection, which is what the check always meant: the same value taken twice rather than two calls into the same function. 1,200 standalone runs green -- fifty repeats of each of the three new timer tests in each of the eight configurations -- against one failure in two hundred before. 116/116 in each of eight configurations, twice from clean, with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Raised the SMP coverage gate onto the figure the timer tests reachedFrédéric Desbiens
The gate held the floor the thread tests left, 98.78 line and 85.95 branch on the union of the eight configurations. The timer and time batch has since closed 82 of its 84 branch outcomes and both of its uncovered lines, so the threshold is well below what the tree now reaches and no longer stops a regression in this batch from passing. Re-derived on the union basis over seven CI runs at the head of the batch: 5396 of 5450 lines and 3194 of 3610 branch outcomes are covered in every one of them, and the gate is set ten lines and ten outcomes below each, at 98.82 and 88.20. The line figure is now identical in all seven runs -- the two lines that used to move were the wrapped arm of the timer list calculation, and a test now builds the arrangement instead of waiting for the tick to produce it. Five branch outcomes still move: the buffer-wrap arms of the two ISR trace inserts, and the exit arm of the ready-list walk in tx_thread_time_slice.c, which was covered in all seven runs of the previous sample and so appeared on no list. The seven runs read 3194, 3195, 3195, 3195, 3197, 3199 and 3199 of 3610, all green, 116/116 tests in each of eight configurations, retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered the timer thread loop's exit through the shim it testsFrédéric Desbiens
The timer thread's body is one loop whose controlling expression is, under TX_MISRA_ENABLE, a call to a function that returns a constant rather than the literal the other six configurations compile. The exit arm therefore never executes, and in the configurations without the macro gcc folds the condition and the branch does not exist at all. The function has no behaviour of its own -- its whole body is return(TX_TRUE) -- so the test defines it. The kernel is built shared and every internal reference goes through the procedure linkage table, so the definition in the test executable is the one the loop reaches, with no linker option and no preloading. It has exactly two callers in the library: this loop, and the initialisation loop in _tx_thread_smp_current_state_set, which is reached only from the three initialisation paths that run before the scheduler starts a thread and so cannot be reached from a running test. What that costs is the timer thread, because the loop is the whole of its entry function. The test therefore does it deliberately and last: it proves three expirations have been processed, arms the shim, waits for the thread to end, and reports. Nothing after the arming depends on a tick. The test file carries the disclosure this needs -- which symbol is replaced, what the library's version does, why replacing it leaves the rest intact, and where the replaced function's own coverage comes from. tx_timer_thread_entry.c:112 reads two of two in misra_build and misra_trace_build. The file stands at 73/73 lines and 28/30 branch outcomes; the two left are the spin-on-error loops over _tx_thread_smp_core_exclude, which has behaviour of its own. 116/116 tests pass in each of eight configurations, twice from clean, with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered the timer information service's wrapped list arm on purposeFrédéric Desbiens
The active timer list is a ring of thirty-two slots and the current pointer walks it one slot a tick. A timer activated for close to a full lap lands on a slot the pointer has already passed, and the information service then measures the time remaining in two parts rather than one. Which slot a timer lands on depends on where the pointer happens to be when it is activated, so the suite reached that arm by coincidence: the branch and its two lines were covered in some runs of an unchanged tree and missed in others, which is the whole of the line variance in this suite and one of its five unstable branch outcomes. A new test reads the current position first and chooses the activation from it, with the read, the activation, the interrogation and the deactivation held under one SMP protection so the tick cannot move the pointer in between. It asserts the arrangement rather than the answer: the two arms compute the same number of ticks by design, so a test that checked only the returned count would pass without having reached either one in particular. Three arrangements are driven -- the slot ahead of the pointer, the slot behind it, and the slot behind it on a timer carrying more ticks than the list can represent. tx_timer_info_get.c reads 37/37 lines and 38/38 branch outcomes, in every one of the eight configurations and in both clean runs, which is what the coincidence never gave. 116/116 tests pass in each of eight configurations, twice from clean, with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered every timer and time trace insert with a new trace testFrédéric Desbiens
One test of the ThreadX SMP regression suite enables event tracing, so the in-line trace insert each timer and time service carries ran almost everywhere with a null trace buffer pointer and scored one of its twelve branch outcomes. Eight sites sat at one or five of twelve, 80 uncovered outcomes in all. A new test walks the eight services once per condition the macro distinguishes: thread context, both event classes filtered out, interrupt context, initialization context, the state an interrupt presents, and a positioned buffer wrap with and without a full notification registered. The timer services and the time services score against different event classes, so the filtered pass filters both. One entry is the right wrap distance for all eight: no timer or time service writes an event buffer entry ahead of its own insert, and every service in the notified pass has to report a wrap of its own, so a boundary entry taken by something else fails the test rather than quietly losing coverage. One service is driven through its internal entry point in every pass rather than only in the forced-state ones, and that is the SMP path rather than the monoprocessor's. tx_api.h remaps tx_time_get() to a direct read of the system clock unless TX_MISRA_ENABLE is defined, so in six of the eight configurations the published macro never enters _tx_time_get and the insert inside it is unreachable from above. The test checks the macro against the service and then drives the service. The monoprocessor header has no such remapping. All eight sites read twelve of twelve, taking the tx_timer_ and tx_time_ group from 170 to 250 of its 252 branch outcomes on the union of eight configurations. 116/116 tests pass in each of the eight, twice from a clean tree, with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Raised the SMP coverage gate onto the figure the thread tests reachedFrédéric Desbiens
The ratchet stops later work undoing earlier work, so it has to move when a batch closes. It was set for the figure before the thread batch: 98.70 line and 80.10 branch on the union basis, against 98.97 and 86.26 now measured. The gate becomes 98.78 line and 85.95 branch, ten lines and ten outcomes below the floor a run can honestly reach -- the 5394 lines and 3113 branch outcomes covered in every one of seven CI runs on this tree. The comment above it records what moved: the branch spread is down from eight outcomes to three and the unstable set from eleven to five, because the thread trace test closed the two buffer-wrap ordinals on the thread resume, thread suspend, system resume and system suspend inserts, and they are now covered in every run rather than in some. Two outcomes went the other way, from uncovered in seven consecutive runs to unstable, which is why the margin is not zero. Seven CI runs read 5394 to 5396 lines and 3115 to 3117 branches; two clean local runs read 5396 and 3114 to 3116. 113/113 tests in each of eight configurations, retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered the scheduler checks that find their core with nothing to runFrédéric Desbiens
The resume path and the preemption check each compare a core's current thread against its execute entry and, when they differ, pick the entry up to check its stack. Both guard that with a null test, because the entry is empty whenever the core has nothing to run. Neither guard had ever been reached with an empty entry: a core whose current thread is still set is normally a core that is running something the scheduler has already chosen a successor for. A new test constructs the state with tx_thread_smp_core_exclude, which supports excluding a thread from every core and leaves it ready and permanently unmapped. The resume pass holds preemption off and one SMP protection across the whole arrangement, so nothing else observes it: the test thread excludes itself, resumes a scratch thread that is excluded from every core as well, and the resume reaches its own preemption check with the execute entry still empty. The preemption check cannot be driven that way, because returning to the system is what it is for, so that pass gives the core up and the tick puts the exclusion mask back once the core has gone idle. A null current thread on core 0 is the signal the tick reads: the port leaves the interrupted thread in that entry for the whole of an interrupt, so it is empty only when the core had nothing running, which is the one moment that cannot be confused with the window before the check runs. The arrangement is repeated, because a tick landing in that window costs the attempt rather than the test. Two branch outcomes close, the last uncovered logic in tx_thread_system_preempt_check.c and tx_thread_system_resume.c. Both remain short of 100% branch on the null arm of the stack check behind those guards, which no valid state produces. 113/113 tests in each of eight build configurations, from clean, with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered every arm of the SMP thread stack check with a new testFrédéric Desbiens
TX_THREAD_STACK_CHECK sits on nine sites in the SMP scheduler and scores seven conditions at each of them. Ordinary use only ever takes the clean arm of five of those, so the nine sites sat between eight and eleven of fourteen -- 44 uncovered branch outcomes. A new test applies one fault to every thread the sites look at, drives one site with it in place, and takes it out again: the lowest recorded stack pointer put above the thread's own, each of the two fences around the stack overwritten, the recorded pointer dropped below the stack, the word under it made used, and the thread identifier cleared. The fault is applied per step rather than per pass, because the first site to see a bad recorded pointer repairs it on the way through. Three sites look at the thread the core is about to run rather than the one being operated on, and only a time-slice rotation separates the two, so those steps rotate the ready list and hold preemption off across the rotation and the service that follows it -- except the preemption check, which does nothing while preemption is held off. A cleared identifier is incompatible with the published entry points, so the steps drive the internal ones. The file also covers the two parts of the stack machinery no fault reaches: the realignment a create performs when it is handed an odd stack address, and the error handler's MISRA build, where there is no application handler to call and the function exists only to keep its argument referenced. 36 of the 44 outcomes close, with one branch and one line in tx_thread_create.c and two branches and three lines in tx_thread_stack_error_handler.c. The eight left are the macro's own null-thread arm. Four of the nine sites guard it in the source with a null test of their own, so it cannot be taken there at all; four more are handed a thread the caller has already dereferenced; the ninth, in tx_thread_relinquish, already takes it. 113/113 tests in each of eight build configurations, from clean, with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered every thread trace insert with a new trace-enabled thread testFrédéric Desbiens
One test of the ThreadX SMP regression suite enables event tracing, so the in-line trace insert each thread service carries ran almost everywhere with a null trace buffer pointer and scored one of its twelve branch outcomes. Eighteen sites across the tx_thread_ sources sat between one and seven of twelve, 170 uncovered outcomes in all. A new test walks the sixteen thread services once per condition the macro distinguishes: thread context, the thread and internal event classes filtered out, interrupt context, initialization context, the state an interrupt presents, and a positioned buffer wrap with and without a full notification registered. Three things are the SMP path rather than the monoprocessor's. The system state is a per-core array behind a macro that expands to a function call, so the forced-state passes define TX_THREAD_SMP_SOURCE_CODE and write the entry of the core they read back. The time-slice check reaches its insert only while a slice is active on some core, so the test thread carries one. And the scheduler's own inserts sit behind the published ones, so the resume and the suspend are wrapped two entries short as well as one. Every service in the notified wrap pass has to report a wrap of its own, so a boundary entry taken by something else fails the test rather than quietly losing coverage. tx_thread_sleep is the one insert that cannot reach twelve of twelve. The service tests the system state itself, ahead of its own insert, and refuses every caller that is not a thread, so the three outcomes that need a non-zero state have no route through either entry point. It stops at nine of twelve and the test drives the internal entry point at a forced state to record the refusal. 167 of the 170 outcomes close and seventeen of the eighteen sites read twelve of twelve. 113/113 tests in each of eight build configurations, from clean, with retries disabled. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Raised the SMP coverage gate onto the figure the queue test reachedFrédéric Desbiens
The gate is the ratchet that stops later work undoing earlier work, so it has to follow the coverage up. It sat at 98.60 line and 77.30 branch, derived when the union denominator was still being settled, and the queue trace test has since closed 103 branch outcomes that no run covered before. The new floor is 98.70 and 80.10, on the union basis. The worst of nine runs -- seven in CI and two clean local ones -- is 5390 of 5450 lines and 2906 of 3610 branches, so the gate sits ten lines and fourteen outcomes below it. The stale figures in the comment above it are corrected at the same time: the denominators are 5450 and 3610, the merged one is 6595, and the eleven branch outcomes that still move between runs are named. Fourteen outcomes of headroom rather than the twelve that preceded it, because an outcome's stability turns out to depend on how many runs have been looked at. The two ordinals at tx_trace_isr_enter_insert.c:93 were covered in three of seven runs before this change and in none of seven after it, and nothing here touches that site. A gate set flush against a worst observation is set against a sample. Verified: all eight configurations pass 110 of 110 and clear the new gate. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Covered every queue trace insert with a new trace-enabled queue testFrédéric Desbiens
Each of the nine queue services carries one in-line trace insert, and the insert behaves differently according to the system state it runs in, whether its event class is filtered, whether it is the entry that wraps the trace buffer, and whether a buffer-full notification is registered. Exactly one test in the SMP suite enabled event tracing, so at seven of the nine sites the macro body never executed at all and the other two reached five of its twelve outcomes: 91 uncovered branch outcomes in the queue sources, with no test missing for anything else in the subsystem. The new test walks the nine services once per condition, in an order that leaves the system as it found it, so the passes compose and repeat. Two things are specific to SMP. The system state is per core and is normally reached through a function call, so the forced-state passes take the array itself and write the entry of the core they are running on, read rather than assumed. The wrap pass holds the SMP protection across both the positioning and the service call, which makes the boundary entry the service's own by construction: every trace insert in the kernel is written inside that same protection, so nothing else can take the slot in between. Create and delete are driven through their internal entry points in the forced-state passes only, because the error checking shell rejects both from any caller that is not a thread; the shell is covered by its own tests and what is under test here is the insert behind it. Nothing is interposed and no certified source is touched. The queue sources are now at 622 of 622 lines and 394 of 394 branches over the union of the eight build configurations, from 303 of 394. Twelve further branch outcomes and six lines outside the subsystem arrive with it, in the MISRA shim, the trace object registry and the thread suspend and sleep paths. All eight configurations build clean and pass 110 of 110, twice from clean with retries disabled, and the new test is green in 75 consecutive runs across three configurations. Assisted-by: Claude Code (Opus 5) <[email protected]>
2026-09-19Restored the missing licence headers on the ThreadX SMP regression testsFrédéric Desbiens
109 of the 110 tracked files in test/smp/regression carry no copyright notice and no licence text at all. The one that does was added this month. Everything else in the repository -- common/src, common_smp/src, the ports, the samples -- carries the standard header, so this is an oversight in one directory rather than a deliberate exemption, and it has been there since the tests were first published. The monoprocessor tree had the same gap and was fixed the same way. The header added here is byte identical to the one in common_smp/src, including its SPDX identifier. Nothing else in these files changes: every hunk is ten added lines and none removed. There is deliberately no "Eclipse ThreadX contributors" line and no AI disclosure paragraph -- this commit contributes no code, it records the licence the files were always under. A later change that does contribute code to one of these files adds the contributor line and its own attribution at that point, which is when there is something to attribute. Verified: all eight build configurations build clean and pass 109 of 109, and the coverage figure is unmoved at 5386/5450 lines and 2807/3610 branches over the union of the eight. Assisted-by: Claude Code (Opus 5) <[email protected]>