Commit Graph
28 Commits
Author SHA1 Message Date
Frédéric Desbiens 17ff21a07f Fixed the garbled comment in the POSIX condition variable sources (#694)
The comment above the internal semaphore lookup in the POSIX condition
variable implementation was garbled: it contained a capitalisation typo
("COndition") and an incomplete sentence ("into a semaphore  a cast").
The four affected sources now share a single, correct wording.

This is a comment-only change; there is no functional impact.

Fixes #419

Assisted-by: Copilot (Opus 5) <noreply@github.com>
2026-09-03 12:58:26 -04:00
Frédéric Desbiens 2f6945475b Stopped pthread_self() faulting when the caller is not a pthread (#627)
Nothing prevents an application mixing tx_thread_create() with the POSIX layer,
and a thread created that way has no POSIX control block. posix_thread2tcb()
returns NULL for it, which posix_thread2tid() then read through:

    p_tcb = posix_thread2tcb(thread_ptr);
    thread_ID = p_tcb->pthreadID;

pthread_self() went on to compound it, reading the signal fields of a POSIX_TCB
out of a thread that is only a TX_THREAD:

    if (((POSIX_TCB *) thread_ptr) -> signals.signal_handler)

The first is a null dereference and the second runs off the end of the control
block into whatever the linker put there. Under qemu-system-riscv32 the first one
lands first: mcause=0x5, a load access fault, with mtval=0xb4 for the offset of
pthreadID.

Have posix_thread2tid() report zero for a thread with no control block, which is
what px_pth_join.c already does for the same call, and have pthread_self() skip
the signal check unless the ID says the caller really is a pthread. Zero cannot
collide with a real ID because px_pth_create.c uses the address of the control
block as the ID.

This also covers the case where there is no current thread at all, from an ISR or
before the scheduler starts: tx_thread_identify() returns NULL, and the same
zero comes back instead of a fault.

Add posix_pthread_self_test, which asks both kinds of thread for their ID: a
pthread, which has to report what pthread_create() returned, and a plain ThreadX
thread, which has to report zero. Reverting either half of the fix turns the test
into the load access fault above.

Verified with riscv64-unknown-elf and qemu-system-riscv32: 4 tests across the
default build, 4 of 4 passing.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-16 18:45:16 -04:00
Frédéric Desbiens f184531e7f Added the POSIX compatibility layer to the CMake build, with regression tests (#626)
* Added the POSIX compatibility layer to the CMake build

Nothing in the repository built the POSIX layer. The FreeRTOS layer next to it
has had a target since the CMake build was introduced, so the POSIX one was the
odd one out, and 106 source files went unbuilt by any target, on any
architecture.

Add a posix-threadx target, following the FreeRTOS layer's shape: a static
library, EXCLUDE_FROM_ALL so the default build is unchanged, linking threadx and
publishing its own directory as a PUBLIC include path. The sources live in their
own CMakeLists.txt rather than the top-level file, as common/ does, because there
are 106 of them. The seven posix_*.c files in the same directory are a demo and
standalone signal tests, each with its own entry point, so they stay out of the
library.

The layer does not suit every configuration, and the target is only offered where
it can work:

  - Hosted simulation ports (linux, win32, win64) build against a C library that
    already provides errno.h, pthread.h and the rest. The layer replaces those.
    Its pthread.h even uses _PTHREAD_H, the same include guard as glibc's, so its
    declarations are skipped wholesale and the build fails on missing types.
    Neutralising that guard only exposes the real problem: 69 conflicting
    definitions in a single translation unit, for time_t, struct timespec,
    sigset_t, pthread_t, pthread_mutex_t, sem_t and more. Both the layer and the
    C library implement POSIX, and only one of them can define those names. The
    linux port also emulates threads by calling the C library's pthread_create
    and sem_wait, which the layer exports itself, so linking the two would divert
    the port into the layer that sits on top of it.
  - SMP builds. px_int.h declares _tx_thread_current_ptr as a plain pointer,
    which is a per-core array under SMP, and the layer tracks no current core.

Building the layer for the first time exposed one portability defect worth
fixing rather than working around. tx_posix.h defined ssize_t as INT, with a
comment conceding it should come from <sys/types.h>. That is correct only where
the C library agrees: on AArch64 newlib makes ssize_t 64 bits, and every
translation unit that reached a library header failed to compile. Defer to the
library when it has declared the type, keyed on the _*_DECLARED guards newlib
uses, and do the same for mode_t, which had the same problem waiting. Where no
library declaration exists the previous definitions still apply, so the 32-bit
targets that did build are unaffected.

Verified by building posix-threadx for arm9, arm11, cortex_m0, cortex_m3,
cortex_m4, cortex_m7, cortex_m33, cortex_m55, cortex_m85, cortex_a7, cortex_a9,
cortex_r4, cortex_r5, cortex_a34, cortex_a53 and cortex_a55 with
arm-gnu-toolchain-14.3.rel1, and for risc-v32 and risc-v64 with
riscv64-unknown-elf: 18 of 18, 106 objects each. cortex_a78 has no non-SMP port
and fails to configure with or without this change. Linking the result against
libthreadx.a leaves only tx_application_define, _tx_initialize_low_level, the
optional execution profile hooks, and memset and strlen unresolved, all of which
the application or its C library supplies. The default build still produces
libthreadx.a and no POSIX library.

Compiling is not the same as working, and on the 64-bit targets in that list it
is not enough. The layer carries a message by putting the address of a private
buffer into the queue, and ULONG is 32 bits on every port, so that address only
fits when TX_64_BIT is defined. Without it px_mq_send.c truncates the pointer
and px_mq_receive.c casts the truncated value back, which GCC reports as nothing
worse than a -Wpointer-to-int-cast warning. Defining TX_64_BIT is not a remedy
either: tx_api.h then reaches for the extension pointer macros, which need
tx_thread_extension_ptr in the thread control block, and outside ports_smp and
ports/linux no port declares it. So the target builds everywhere, but the
message queues are only sound on the 32-bit ports. That is pre-existing, it is
not made worse here, and it is left for a change of its own.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

* Added regression tests for the POSIX compatibility layer

The POSIX layer had no tests. The seven posix_*.c programs shipped beside it are
demos: they print nothing, report no result and end in infinite loops, so they
tell a person watching a debugger something and an automated run nothing.

Add a suite under test/posix, laid out like the FreeRTOS one and driven the same
way, with scripts/build_posix.sh and scripts/test_posix.sh over a run.sh that
takes the same arguments as its RISC-V counterpart.

The tests run on emulated hardware because they have nowhere else to go. The
layer replaces the C library's POSIX headers and exports the same symbols the
linux port calls to emulate threads, so a host build is not available to it. The
RISC-V QEMU harness that the ThreadX suite already uses is, and this suite reuses
its BSP and testcontrol.c rather than growing copies of them.

Three tests to start:

  - posix_mq_basic_test sends a message through a queue and checks the contents
    and priority survive the round trip.
  - posix_mq_send_abort_test covers the leak fixed in #624, by filling a queue,
    blocking a sender on it, aborting the wait and watching the queue's byte
    pool. Reverting the fix makes it fail on the pool check, so it measures what
    it claims to.
  - posix_pthread_basic_test covers pthread creation, a mutex, a semaphore
    handoff, pthread_self and collecting an exit value through pthread_join.

The queue's pool is sized (mq_maxmsg + 1) * (mq_msgsize + 11), which leaves room
for about one message beyond a full queue, so the abort test uses small messages
and a shallow queue. With a larger message the first leaked buffer exhausts the
pool, tx_byte_allocate fails, and the sender disappears into the endless loop in
posix_internal_error() instead of reporting anything. Sizing it this way keeps
the failure legible as a pool measurement rather than a timeout.

riscv32 only, and the reason is the layer rather than the harness. The layer puts
the address of a message buffer into the queue, ULONG is 32 bits on every port,
and a 64-bit address only fits there when TX_64_BIT is defined. Defining it makes
tx_api.h use the extension pointer macros, which need tx_thread_extension_ptr in
the thread control block, and no port outside ports_smp and ports/linux declares
it. Configuring for risc-v64 stops with that explanation rather than building
something that would corrupt a pointer at runtime.

Verified with riscv64-unknown-elf and qemu-system-riscv32: 3 tests across
default_build, disable_notify_callbacks_build, stack_checking_build and
trace_build, 12 of 12 passing.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-16 18:24:48 -04:00
Frédéric Desbiens 3501c5c31d Stopped two POSIX call sites falling through their error handler (#625)
posix_internal_error() never comes back for a non-zero code: its body is
"while (error_code) { ; }". Most callers in the layer still follow it with an
explicit error return. Two do not, and both fall through into a pointer
dereference, so they are only correct because the handler happens to hang.

That is a fragile thing to rely on. The C standard permits an implementation to
assume a loop with no side effects terminates (C11 6.8.5p6), so the guarantee is
a property of the toolchain rather than of the language. Both toolchains the
project supports do preserve the loop today - checked with GCC 14.3 at -O0, -O1,
-O2 and -Os, and with Clang 22 at -O0 and -O2 - so nothing is broken right now.
Neither call site should depend on that.

mq_send() falls through with bp indeterminate, having just been told the
allocation failed, and would copy msg_len bytes through it. Report ENOMEM and
return ERROR instead.

posix_thread2tid() falls through with thread_ptr NULL. posix_thread2tcb() returns
NULL for that input, and the next line reads p_tcb->pthreadID. Return zero, which
is never a valid pthread ID because px_pth_create.c assigns the address of the
TCB as the ID.

No behaviour changes while the handler keeps hanging; both additions are
unreachable today.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-16 18:24:25 -04:00
Frédéric Desbiens 2f378a4c8e Stopped POSIX mq_send() leaking the message buffer when the send fails (#624)
mq_send() allocates a private buffer from the queue's own byte pool, copies the
caller's message into it, and passes the buffer's address through the queue. The
receiver takes ownership: px_mq_receive.c releases the buffer once it has copied
the message out.

If tx_queue_send() fails, the message never reaches the queue, so no receiver
will ever release that buffer. mq_send() returned ERROR while still holding the
only pointer to it, leaking it from the pool.

The failure is reachable. With TX_WAIT_FOREVER the send suspends, and
tx_queue_send() then returns the thread's suspend status. tx_thread_wait_abort()
sets TX_WAIT_ABORTED on a suspended sender, and the queue survives that, so the
pool keeps shrinking with every aborted send.

Queue deletion also reaches the branch, via TX_DELETED, but vq_message_area is a
TX_BYTE_POOL embedded in the queue structure and destroyed with it, so nothing
outlives the failure there.

Exhausting the pool does not merely make later calls fail. tx_byte_allocate()
failure runs into posix_internal_error(9999), which busy-loops forever on a
non-zero code, so a caller hangs rather than getting an error back.

Release the buffer before returning. The EINTR reporting is unchanged, since
TX_WAIT_ABORTED maps onto it reasonably.

Reported-by: K-ANOY <https://github.com/eclipse-threadx/threadx/issues/568>
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-16 09:45:23 -04:00
Frédéric Desbiens ba5e5897f4 Made the basic processing counter visible to the reporting thread (#616)
The thread-metric basic processing test reports 0 for every period when built
with optimisation, and additionally claims "Basic processing thread died!"
because the counter never differs from the previous reading.

tm_basic_processing_counter is written by the processing thread and read by the
reporting thread, but it was a plain global. The processing loop calls nothing,
so nothing forces the compiler to write the counter back to memory, and in an
infinite loop there is no exit path where it must. At -O2 with
arm-none-eabi-gcc 13.2.1 the counter is loaded once before the loop, incremented
in a register, and never stored:

    28:  ldr  ip, [r3]        counter loaded once
    ...                       inner loop over the volatile array
    50:  add  ip, ip, #1      increment stays in the register
    54:  b    2c              and around again

The reporting thread reads the memory location, which stays 0 for the life of
the program. This is not specific to the Armv8-R target it was reported on: the
same shape appears for Cortex-M4 in Thumb state, and at -O1, -O2, -O3 and -Os.
Only -O0 happens to work.

Declare the counter volatile so the increment becomes a real store. Read it into
a local once per pass and use the local inside the 1024-iteration loop, rather
than letting every iteration re-read the volatile: that would add a memory access
to each iteration and change the amount of work the test performs. This test is
the baseline the rest of the suite is scaled against, per the readme, so its
throughput has to stay comparable with previously published figures and with
other RTOSes.

The array was already volatile, which is why the arithmetic itself survives
optimisation; only the counter was missing.

Verified by disassembly rather than by inspection. After the change the inner
loop is instruction-for-instruction identical to what dev generates today,
ldr/ldr/add/eor/str/add/cmp/bne, with one volatile read hoisted above the loop
and one store below it:

    30:  ldr  ip, [lr]        one read per pass, outside the loop
    34:  ...                  inner loop unchanged, back edge targets 34
    54:  add  ip, ip, #1
    58:  str  ip, [lr]        the counter is now published
    5c:  b    2c

Confirmed for cortex-r52 in Arm state and cortex-m4 in Thumb state.

Reported by @hotislandn in #480, which identified the cause correctly.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-14 14:45:55 -04:00
Frédéric Desbiens cfed1d8095 Fixed the kernel object leaks on the static creation error paths (#584)
xQueueCreateStatic() and xTaskCreateStatic() take their storage from the
caller, so neither leaks memory, but both create ThreadX objects and both
return NULL when a later step fails. The caller is left without a handle and
cannot call the matching delete function, so any object already created stays
registered in the kernel, pointing into a caller buffer that the application
is now free to reuse or discard.

Three paths were affected. xQueueCreateStatic() abandoned the read semaphore
when the write semaphore could not be created. xTaskCreateStatic() abandoned
the notification semaphore when the thread could not be created, and abandoned
both the semaphore and the thread when the thread could not be resumed.

Delete what was already created before returning on each of them. The resume
path terminates the thread before deleting it, since a thread created with
TX_DONT_START is suspended rather than terminated, which is the same order the
idle task uses when it reaps a deleted task.

Extend the regression suite to cover all three paths, and add thread resume to
the set of entry points the harness can force to fail. Each static failure case
now uses its own control block, so a future regression on one path cannot carry
damage into the next case and report misleading counts there.

Verified against the layer as it stands on dev, where the three new checks fail
with the objects left behind, and against the fixed layer, where the suite
passes.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-09 08:44:30 -04:00
Frédéric Desbiens 5481fc75a0 Fixed the resource leaks on the xQueueCreate error paths (#582)
xQueueCreate() allocated the queue descriptor and its backing memory, then
created two ThreadX semaphores, and returned NULL on either semaphore failure
without releasing anything. Since no handle reached the caller, vQueueDelete()
could not be used to recover, so both allocations were lost. A failure on the
second semaphore additionally abandoned the read semaphore it had already
created, leaving a live ThreadX control block inside freed memory.

Release the backing memory and the descriptor on both paths, and delete the
read semaphore before returning when the write semaphore cannot be created.
This is the teardown order vQueueDelete() already uses, and it matches the
cleanup xTaskCreate() performs on its own error paths.

Verified with a fault injection harness that intercepts the ThreadX byte pool
and semaphore entry points to force tx_semaphore_create() to fail on a chosen
call. On a read semaphore failure the layer previously performed 2 allocations
and 0 releases, and on a write semaphore failure 2 allocations, 0 releases and
0 semaphore deletions. It now performs 2 releases in both cases and deletes
the read semaphore in the second, with the byte pool restored to its prior
state.

Fixes https://github.com/eclipse-threadx/threadx/issues/570

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
2026-08-09 07:28:19 -04:00
Frédéric DesbiensandCodex b880ffeada Fixed SMP execution profile total getters (#553)
Updated the SMP execution profile aggregate getters to copy each core's total into the matching output array element. Added a focused regression test for thread, ISR, and idle total getters.

Co-authored-by: Codex <codex@openai.com>
2026-06-22 08:03:46 -04:00
Frédéric DesbiensandCopilot 730b61874b Added copyright headers to files missing them
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 <223556219+Copilot@users.noreply.github.com>
2026-06-06 21:48:06 +02:00
Ian Thompson 9ccdd5d822 Updated Xtensa support (#525)
* Add LX8 support for > 32 interrupts

Also fix inconsistent SWPRI define in interrupt handler

* ThreadX: Fix context switch logic

- Disable interrupts prior to allocating a large exception frame;
  only reenable them after deallocating the extra memory.
- Any user tasks with stacks based on TX_MINIMUM_STACK do not
  have sufficient space for both a context switch frame and an ISR
  frame; ill-timed interrupts were causing stack overruns.

* ThreadX: Interrupt fixes for TX_ENABLE_EXECUTION_CHANGE_NOTIFY

- Must reload register trashed by notify hook function call
- Must ensure PS.WOE is set before using call8
- Remove unused XT_USE_INT_WRAPPER define and associated changes,
  which had a bug in XEA2 usage
- Fix another case where enabling the thread notify hooks for
  call0 ABI corrupted a register

* ThreadX: Support for __DYNAMIC_REENT__

- ThreadX change to handle dynamic reent for both newlib and xclib
- Adapt ThreadX xclib interface code to handle dynamic reent pointers

* ThreadX: Xtensa execution profiling support

- Update upstream execution profiling for Xtensa port

* ThreadX: Update xtensa port readme

* ThreadX: Add Xtensa example to EPK

- tx_execution_profile.h now defines an Xtensa example
  in addition to the existing Cortex example

* ThreadX: Add xtensa.cmake

* ThreadX: Update XSHAL_CLIB ifdefs in __getreent()

- Prevent a fall-through with no return value when neither
  xclib nor newlib are used.
2026-05-19 11:11:37 -04:00
Frédéric Desbiens c3259a2160 Updated copyright headers and version number constants (#509)
* Updated version number constants

* Removed revision history from all files

* Added Eclipse ThreadX contributors' copyright header
2026-03-05 10:46:30 +01:00
nopnopnop-lavine c09b444792 Fix: Return 0u when no counters available 2025-11-18 14:43:17 +08:00
bo chen 039a346397 Update the copyright for all assembly files. 2024-02-23 09:39:05 +08:00
Bo Chen (from Dev Box) 3e1da1f0b0 Update version id string. 2024-01-30 08:39:18 +08:00
Bo Chen (from Dev Box) 8276bcf711 Update copyright. 2024-01-29 13:51:15 +08:00
Xiuwen Cai 9f3e35d3dc Add check for overflow in queue size calculation in RTOS compatibility layer. (#339)
* Add check for overflow in queue size calculation.

* Update release data and version.
2023-12-28 13:17:40 +08:00
Tiejun Zhou 3e8e85cdc1 Release 6.2.0 2022-10-26 23:41:13 +00:00
Yuxin Zhou 8c3c08f108 Release 6.1.12 2022-07-26 02:04:40 +00:00
Yuxin Zhou cef9cb22a5 Release 6.1.11 2022-04-20 05:07:02 +00:00
Yuxin Zhou f7f0957188 Release 6.1.10 2022-01-29 00:24:03 +00:00
Yuxin Zhou 1af8404c54 Release 6.1.9 2021-10-14 00:51:26 +00:00
Yuxin Zhou d0dab58250 Release 6.1.8 2021-07-28 07:24:02 +00:00
Liya Du db7a3972a8 FreeRTOS readme 2021-06-07 11:38:19 +08:00
Bo Chen f5056f4923 Release 6.1.7 2021-06-02 06:45:05 +00:00
Yuxin Zhou b12bd44faa Release 6.1.6 2021-04-03 01:03:21 +00:00
Yuxin Zhou 10a7932b9d Release 6.1.5 2021-03-05 05:38:33 +00:00
Scott Larson 236374f704 add utilities and update TraceX install link 2020-10-01 13:46:52 -07:00