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>
This commit is contained in:
Frédéric Desbiens
2026-08-16 18:24:48 -04:00
committed by GitHub
parent 3501c5c31d
commit f184531e7f
11 changed files with 1132 additions and 1 deletions
+18
View File
@@ -82,6 +82,24 @@ target_sources(freertos-threadx
)
target_link_libraries(freertos-threadx PUBLIC threadx)
# Define the POSIX compatibility layer. It supplies its own errno.h, fcntl.h,
# pthread.h, sched.h, signal.h and time.h, so it cannot be combined with the
# hosted simulation ports: those build against a full C library that already
# provides those headers, and the layer's pthread.h even shares the _PTHREAD_H
# include guard with glibc's. Skip it there rather than offering a target that
# could never build.
#
# It is also single-core by construction: px_int.h declares
# _tx_thread_current_ptr as a plain pointer, which is a per-core array in an SMP
# build, and the layer has no notion of which core it is running on.
if(THREADX_ARCH MATCHES "^(linux|win32|win64)$")
message(STATUS "POSIX compatibility layer not available for ${THREADX_ARCH}")
elseif(THREADX_SMP)
message(STATUS "POSIX compatibility layer not available for SMP builds")
else()
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/utility/rtos_compatibility_layers/posix)
endif()
# If the user provided an override, copy it to the custom directory
if (NOT TX_USER_FILE)
message(STATUS "Using default tx_user.h file")
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
##############################################################################
# Copyright (c) 2026 Eclipse ThreadX contributors
#
# This program and the accompanying materials are made available under the
# terms of the MIT License which is available at
# https://opensource.org/licenses/MIT.
#
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
# The AI-generated portions may be considered public domain (CC0-1.0)
# and not subject to the project's licence. The human contributor has
# reviewed and verified that the code is correct.
#
# SPDX-License-Identifier: MIT and CC0-1.0
##############################################################################
$(dirname `realpath $0`)/../test/posix/cmake/run.sh riscv32 build all
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
##############################################################################
# Copyright (c) 2026 Eclipse ThreadX contributors
#
# This program and the accompanying materials are made available under the
# terms of the MIT License which is available at
# https://opensource.org/licenses/MIT.
#
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
# The AI-generated portions may be considered public domain (CC0-1.0)
# and not subject to the project's licence. The human contributor has
# reviewed and verified that the code is correct.
#
# SPDX-License-Identifier: MIT and CC0-1.0
##############################################################################
$(dirname `realpath $0`)/../test/posix/cmake/run.sh riscv32 test all
+83
View File
@@ -0,0 +1,83 @@
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
cmake_policy(SET CMP0054 NEW)
cmake_policy(SET CMP0057 NEW)
# Regression tests for the POSIX compatibility layer.
#
# The layer cannot run on the hosted simulation ports: it supplies its own
# pthread.h, errno.h, time.h and friends, which collide with the C library's,
# and the linux port emulates threads with the very pthread calls the layer
# exports. These tests therefore run on emulated targets, reusing the RISC-V
# BSP and QEMU setup that the ThreadX RISC-V regression suite already uses.
project(threadx_posix_test LANGUAGES C ASM)
set(BUILD_CONFIGURATIONS default_build disable_notify_callbacks_build
stack_checking_build trace_build)
set(CMAKE_CONFIGURATION_TYPES
${BUILD_CONFIGURATIONS}
CACHE STRING "list of supported configuration types" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
${CMAKE_CONFIGURATION_TYPES})
list(GET CMAKE_CONFIGURATION_TYPES 0 BUILD_TYPE)
if((NOT CMAKE_BUILD_TYPE) OR (NOT ("${CMAKE_BUILD_TYPE}" IN_LIST
CMAKE_CONFIGURATION_TYPES)))
set(CMAKE_BUILD_TYPE
"${BUILD_TYPE}"
CACHE STRING "Build Type of the project" FORCE)
endif()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.")
message(STATUS "THREADX_ARCH: ${THREADX_ARCH}")
set(default_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32)
set(disable_notify_callbacks_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_DISABLE_NOTIFY_CALLBACKS)
set(stack_checking_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_STACK_CHECKING)
set(trace_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_EVENT_TRACE)
# These tests are 32-bit only for now, and the reason is a gap in the layer
# rather than a choice about coverage.
#
# The layer passes a message by putting the address of a private buffer into the
# queue. ULONG is 32 bits on every ThreadX port, including the 64-bit ones, so
# on a 64-bit target that address only fits if TX_64_BIT is defined: the layer
# then splits it across two message words and sizes the message accordingly.
# Without it, px_mq_send.c truncates the pointer to 32 bits and px_mq_receive.c
# casts the truncated value back, which the compiler reports only as a warning.
#
# Defining TX_64_BIT is not a way out either: tx_api.h then uses the extension
# pointer macros, which need tx_thread_extension_ptr in the thread control
# block, and no 64-bit port outside ports_smp and ports/linux declares it.
# Closing that gap belongs with the layer, not with its tests.
if(THREADX_ARCH STREQUAL "risc-v64")
message(FATAL_ERROR
"The POSIX layer cannot run on a 64-bit port yet: it needs TX_64_BIT to "
"carry a 64-bit message pointer, and TX_64_BIT needs a port that declares "
"tx_thread_extension_ptr. Use the riscv32 toolchain file for these tests.")
endif()
add_compile_options(
-std=c99
-O0
-g3
-fdiagnostics-color
-Werror
-ffunction-sections
-fdata-sections
-DTX_REGRESSION_TEST
-DTEST_STACK_SIZE_PRINTF=4096
-DEXTERNAL_EXIT
${${CMAKE_BUILD_TYPE}}
)
enable_testing()
# ThreadX itself, plus the posix-threadx target the top-level file defines.
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../.. threadx)
# Reuse the RISC-V BSP from the ThreadX regression suite rather than keeping a
# second copy of the board support in this tree.
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../tx/cmake/riscv/bsp riscv_bsp)
add_subdirectory(regression)
@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
cmake_policy(SET CMP0057 NEW)
project(posix_regression_test LANGUAGES C ASM)
set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../regression)
set(TX_TEST_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../tx/regression)
set(BSP_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../tx/cmake/riscv/bsp)
if(THREADX_ARCH STREQUAL "risc-v64")
set(QEMU_SYSTEM "qemu-system-riscv64")
set(XLEN_TAG "rv64")
else()
set(QEMU_SYSTEM "qemu-system-riscv32")
set(XLEN_TAG "rv32")
endif()
set(posix_test_cases
${SOURCE_DIR}/posix_mq_basic_test.c
${SOURCE_DIR}/posix_mq_send_abort_test.c
${SOURCE_DIR}/posix_pthread_basic_test.c
)
set(WEAK_DEFAULTS_SOURCE ${CMAKE_CURRENT_LIST_DIR}/../../../shared/regression/testcontrol_weak_defaults.c)
# Weak defaults must be an OBJECT library so all objects are always linked into
# each test executable. A STATIC library would have the linker skip them, since
# weak definitions do not satisfy undefined strong references while scanning an
# archive.
add_library(posix_test_weak_defaults OBJECT ${WEAK_DEFAULTS_SOURCE})
target_compile_definitions(posix_test_weak_defaults PRIVATE
CTEST BATCH_TEST EXTERNAL_EXIT TEST_STACK_SIZE_PRINTF=4096)
target_compile_options(posix_test_weak_defaults PRIVATE -include stdlib.h)
target_link_libraries(posix_test_weak_defaults PRIVATE azrtos::threadx)
# testcontrol.c supplies main(), tx_application_define() and
# test_control_return(), and is shared with the ThreadX regression suite.
add_library(posix_test_utility ${TX_TEST_DIR}/testcontrol.c)
target_link_libraries(posix_test_utility PUBLIC riscv_bsp azrtos::threadx)
target_compile_definitions(posix_test_utility PUBLIC
CTEST BATCH_TEST EXTERNAL_EXIT TEST_STACK_SIZE_PRINTF=4096)
target_compile_options(posix_test_utility PUBLIC -include stdlib.h)
function(register_posix_test test_name)
target_link_options(${test_name} PRIVATE
-T ${BSP_DIR}/link.lds -nostartfiles
-Wl,--gc-sections)
add_test(
NAME ${CMAKE_BUILD_TYPE}::${XLEN_TAG}::${test_name}
COMMAND ${QEMU_SYSTEM} -nographic -smp 1 -bios none -m 128M
-machine virt -kernel $<TARGET_FILE:${test_name}>
)
set_tests_properties(${CMAKE_BUILD_TYPE}::${XLEN_TAG}::${test_name}
PROPERTIES TIMEOUT 120)
endfunction()
foreach(test_case ${posix_test_cases})
get_filename_component(test_name ${test_case} NAME_WE)
add_executable(${test_name} ${test_case} $<TARGET_OBJECTS:posix_test_weak_defaults>)
target_link_libraries(${test_name} PRIVATE
-Wl,--whole-archive riscv_bsp -Wl,--no-whole-archive
posix_test_utility posix-threadx)
register_posix_test(${test_name})
endforeach()
+166
View File
@@ -0,0 +1,166 @@
#!/bin/bash
##############################################################################
# Copyright (c) 2026 Eclipse ThreadX contributors
#
# This program and the accompanying materials are made available under the
# terms of the MIT License which is available at
# https://opensource.org/licenses/MIT.
#
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
# The AI-generated portions may be considered public domain (CC0-1.0)
# and not subject to the project's licence. The human contributor has
# reviewed and verified that the code is correct.
#
# SPDX-License-Identifier: MIT and CC0-1.0
##############################################################################
set -e
# POSIX compatibility layer regression test build/test runner.
# Usage: run.sh <arch> <build|test> [all|<config> ...]
# arch: riscv32
#
# Only riscv32 is available: the layer cannot carry a 64-bit message pointer
# without TX_64_BIT, and TX_64_BIT needs a port that declares
# tx_thread_extension_ptr. See the comment in ../CMakeLists.txt.
function help() {
echo "Usage: $0 <arch> <command> [all|<build_configuration> ...]"
echo ""
echo " arch: riscv32"
echo " command: build | test"
echo ""
echo "Available build configurations:"
for build in ${build_configurations[*]}; do
echo " $build"
done
exit 1
}
function validate() {
for build in ${build_configurations[*]}; do
if [ "$1" == "$build" ]; then
return
fi
done
help
}
function generate() {
local arch=$1
local build=$2
local toolchain_file
local build_dir
toolchain_file="${REPO_ROOT}/cmake/riscv32_gnu.cmake"
build_dir="build/${arch}_${build}"
cmake -B"$build_dir" -GNinja \
-DCMAKE_TOOLCHAIN_FILE="$toolchain_file" \
-DCMAKE_BUILD_TYPE="$build" \
.
}
function build() {
local arch=$1
local build=$2
cmake --build "build/${arch}_${build}"
}
function run_test() {
local arch=$1
local build=$2
local parallel_jobs=$3
pushd "build/${arch}_${build}"
[ -z "${CTEST_PARALLEL_LEVEL}" ] && parallel="-j${parallel_jobs}"
if [ -z "${CTEST_REPEAT_FAIL}" ]; then
repeat_fail=2
else
repeat_fail=${CTEST_REPEAT_FAIL}
fi
ctest $parallel --timeout 1000 \
-O "${arch}_${build}.txt" \
-T test --no-compress-output \
--test-output-size-passed 4194304 \
--test-output-size-failed 4194304 \
--output-on-failure \
--repeat until-pass:${repeat_fail} \
--output-junit "${arch}_${build}.xml"
popd
}
# Determine repo root and script directory
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
cd "$SCRIPT_DIR"
# Parse build configurations from CMakeLists.txt
result=$(sed -n "/(BUILD_CONFIGURATIONS/,/)/p" CMakeLists.txt | sed ':label;N;s/\n/ /;b label' | grep -Pzo "[a-zA-Z0-9_]*build[a-zA-Z0-9_]*\s*" | tr -d '\0')
IFS=' '
read -ra build_configurations <<< "$result"
if [ $# -lt 2 ]; then
help
fi
arch=$1
shift
if [ "$arch" != "riscv32" ]; then
echo "Error: arch must be 'riscv32'"
help
fi
command=$1
shift
if [ "$#" == "0" ]; then
builds=${build_configurations[0]}
elif [ "$*" == "all" ]; then
builds=${build_configurations[@]}
else
for item in $*; do
validate "$item"
done
builds=$*
fi
if [ "$command" == "build" ]; then
for item in $builds; do
generate "$arch" "$item"
echo ""
done
for item in $builds; do
echo "Building ${arch} ${item}"
build "$arch" "$item"
echo ""
done
elif [ "$command" == "test" ]; then
cores=$(nproc)
if [ -z "${CTEST_PARALLEL_LEVEL}" ]; then
build_counts=$(echo $builds | wc -w)
parallel_jobs=$(($cores / $build_counts))
parallel_jobs=$(($parallel_jobs + 2))
pids=""
for item in $builds; do
echo "Testing ${arch} ${item}"
run_test "$arch" "$item" "$parallel_jobs" &
pids+=" $!"
done
exit_code=0
for p in $pids; do
wait $p || exit_code=$?
done
exit $exit_code
else
for item in $builds; do
echo "Testing ${arch} ${item}"
run_test "$arch" "$item" "$parallel_jobs"
done
fi
else
help
fi
+159
View File
@@ -0,0 +1,159 @@
/***************************************************************************/
/* Copyright (c) 2026 Eclipse ThreadX contributors */
/* */
/* This program and the accompanying materials are made available under */
/* the terms of the MIT License which is available at */
/* https://opensource.org/licenses/MIT. */
/* */
/* AI Disclosure: This file was largely AI-generated by Claude Code */
/* (Opus 5). The AI-generated portions may be considered public domain */
/* (CC0-1.0) and not subject to the project's licence. The human */
/* contributor has reviewed and verified that the code is correct. */
/* */
/* SPDX-License-Identifier: MIT and CC0-1.0 */
/***************************************************************************/
/* This test is designed to test basic POSIX message queue operation: open a
queue, send a message through it, receive the message back and confirm both
the contents and the priority survived the round trip. */
#include <stdio.h>
#include "tx_api.h"
#include "pthread.h"
#define TEST_MESSAGE_SIZE 40
#define TEST_QUEUE_DEPTH 10
#define TEST_PRIORITY 3
/* Memory the POSIX layer carves its own objects out of. */
static ULONG posix_region[192 * 1024 / sizeof(ULONG)];
static pthread_t worker;
static pthread_attr_t worker_attr;
static mqd_t queue_des;
static struct mq_attr queue_attr;
extern void test_control_return(UINT status);
static VOID *worker_entry(VOID *input);
/* Define what the initial system looks like. */
#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void posix_mq_basic_application_define(void *first_unused_memory)
#endif
{
struct sched_param param;
VOID *storage_ptr;
(void) first_unused_memory;
/* Bring the POSIX layer up. It returns the first address beyond its own
structures, which is where pthread stacks are taken from. */
storage_ptr = posix_initialize(posix_region);
queue_attr.mq_maxmsg = TEST_QUEUE_DEPTH;
queue_attr.mq_msgsize = TEST_MESSAGE_SIZE;
queue_des = mq_open("mq_basic", O_CREAT | O_RDWR, 0, &queue_attr);
if (queue_des == (mqd_t) ERROR)
{
printf("Running POSIX Message Queue Basic Test............................. ERROR #1\n");
test_control_return(1);
}
pthread_attr_init(&worker_attr);
param.sched_priority = 10;
pthread_attr_setschedparam(&worker_attr, &param);
pthread_attr_setstackaddr(&worker_attr, storage_ptr);
if (pthread_create(&worker, &worker_attr, worker_entry, NULL) != OK)
{
printf("Running POSIX Message Queue Basic Test............................. ERROR #2\n");
test_control_return(1);
}
}
static VOID *worker_entry(VOID *input)
{
CHAR sent[TEST_MESSAGE_SIZE];
CHAR received[TEST_MESSAGE_SIZE];
ULONG received_priority;
ssize_t received_length;
INT i;
(void) input;
printf("Running POSIX Message Queue Basic Test............................. ");
/* Build a recognisable payload rather than a constant one, so a message
that is dropped or truncated cannot pass by accident. */
for (i = 0; i < TEST_MESSAGE_SIZE; i++)
{
sent[i] = (CHAR) (i + 1);
received[i] = (CHAR) 0;
}
if (mq_send(queue_des, sent, TEST_MESSAGE_SIZE, TEST_PRIORITY) != OK)
{
printf("ERROR #3\n");
test_control_return(1);
}
received_priority = 0;
received_length = mq_receive(queue_des, received, TEST_MESSAGE_SIZE, &received_priority);
if (received_length != (ssize_t) TEST_MESSAGE_SIZE)
{
printf("ERROR #4\n");
test_control_return(1);
}
if (received_priority != (ULONG) TEST_PRIORITY)
{
printf("ERROR #5\n");
test_control_return(1);
}
for (i = 0; i < TEST_MESSAGE_SIZE; i++)
{
if (received[i] != sent[i])
{
printf("ERROR #6\n");
test_control_return(1);
}
}
if (mq_close(queue_des) != OK)
{
printf("ERROR #7\n");
test_control_return(1);
}
if (mq_unlink("mq_basic") != OK)
{
printf("ERROR #8\n");
test_control_return(1);
}
printf("SUCCESS!\n");
test_control_return(0);
return (NULL);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,192 @@
/***************************************************************************/
/* Copyright (c) 2026 Eclipse ThreadX contributors */
/* */
/* This program and the accompanying materials are made available under */
/* the terms of the MIT License which is available at */
/* https://opensource.org/licenses/MIT. */
/* */
/* AI Disclosure: This file was largely AI-generated by Claude Code */
/* (Opus 5). The AI-generated portions may be considered public domain */
/* (CC0-1.0) and not subject to the project's licence. The human */
/* contributor has reviewed and verified that the code is correct. */
/* */
/* SPDX-License-Identifier: MIT and CC0-1.0 */
/***************************************************************************/
/* This test covers basic pthread and synchronisation operation: a pthread runs
and reports its own identity, a mutex serialises access to shared state, a
semaphore signals between two pthreads, and a join collects the worker's exit
value. */
#include <stdio.h>
#include "tx_api.h"
/* The layer ships no semaphore.h; sem_t and the sem_* calls arrive through
pthread.h, which pulls in tx_posix.h. */
#include "pthread.h"
#define TEST_STACK_BYTES 4096
static ULONG posix_region[192 * 1024 / sizeof(ULONG)];
static pthread_t worker;
static pthread_attr_t worker_attr;
static pthread_t driver;
static pthread_attr_t driver_attr;
static pthread_mutex_t guard;
static sem_t handoff;
static volatile INT shared_counter;
static pthread_t worker_self_id;
static INT worker_result = 77;
extern void test_control_return(UINT status);
static VOID *worker_entry(VOID *input);
static VOID *driver_entry(VOID *input);
/* Define what the initial system looks like. */
#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void posix_pthread_basic_application_define(void *first_unused_memory)
#endif
{
struct sched_param param;
VOID *storage_ptr;
(void) first_unused_memory;
storage_ptr = posix_initialize(posix_region);
if (pthread_mutex_init(&guard, NULL) != OK)
{
printf("Running POSIX Pthread Basic Test.................................. ERROR #1\n");
test_control_return(1);
}
if (sem_init(&handoff, 0, 0) != OK)
{
printf("Running POSIX Pthread Basic Test.................................. ERROR #2\n");
test_control_return(1);
}
/* The driver runs below the worker so that the worker reaches its first
blocking point before the driver inspects anything. */
pthread_attr_init(&worker_attr);
param.sched_priority = 15;
pthread_attr_setschedparam(&worker_attr, &param);
pthread_attr_setstackaddr(&worker_attr, storage_ptr);
storage_ptr = (ULONG *) storage_ptr + (TEST_STACK_BYTES / sizeof(ULONG));
pthread_attr_init(&driver_attr);
param.sched_priority = 10;
pthread_attr_setschedparam(&driver_attr, &param);
pthread_attr_setstackaddr(&driver_attr, storage_ptr);
if (pthread_create(&worker, &worker_attr, worker_entry, NULL) != OK)
{
printf("Running POSIX Pthread Basic Test.................................. ERROR #3\n");
test_control_return(1);
}
if (pthread_create(&driver, &driver_attr, driver_entry, NULL) != OK)
{
printf("Running POSIX Pthread Basic Test.................................. ERROR #4\n");
test_control_return(1);
}
}
static VOID *worker_entry(VOID *input)
{
INT i;
(void) input;
/* Record what this pthread believes its own ID to be, for the driver to
compare against the ID pthread_create() handed back. */
worker_self_id = pthread_self();
for (i = 0; i < 100; i++)
{
pthread_mutex_lock(&guard);
shared_counter++;
pthread_mutex_unlock(&guard);
}
/* Hand control to the driver. */
sem_post(&handoff);
return ((VOID *) &worker_result);
}
static VOID *driver_entry(VOID *input)
{
VOID *exit_value;
(void) input;
printf("Running POSIX Pthread Basic Test.................................. ");
/* Wait for the worker to finish its counting. */
if (sem_wait(&handoff) != OK)
{
printf("ERROR #5\n");
test_control_return(1);
}
if (shared_counter != 100)
{
printf("ERROR #6\n");
test_control_return(1);
}
if (worker_self_id != worker)
{
printf("ERROR #7\n");
test_control_return(1);
}
exit_value = NULL;
if (pthread_join(worker, &exit_value) != OK)
{
printf("ERROR #8\n");
test_control_return(1);
}
if (exit_value != (VOID *) &worker_result)
{
printf("ERROR #9\n");
test_control_return(1);
}
if (pthread_mutex_destroy(&guard) != OK)
{
printf("ERROR #10\n");
test_control_return(1);
}
printf("SUCCESS!\n");
test_control_return(0);
return (NULL);
}
@@ -0,0 +1,129 @@
# POSIX compatibility layer for ThreadX.
#
# This target is EXCLUDE_FROM_ALL: it is not part of the default build and is
# only produced when something asks for it, either by name or by linking it.
#
# The include directory is deliberately PUBLIC. The layer ships replacements for
# errno.h, fcntl.h, pthread.h, sched.h, signal.h and time.h, and application code
# has to see those rather than a toolchain's own copies. That is also why the
# layer cannot be used on the hosted simulation ports, where the C library
# already supplies them; the top-level CMakeLists.txt skips it there.
add_library(posix-threadx EXCLUDE_FROM_ALL)
target_include_directories(posix-threadx
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
target_sources(posix-threadx
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/px_abs_time_to_rel_ticks.c
${CMAKE_CURRENT_LIST_DIR}/px_clock_getres.c
${CMAKE_CURRENT_LIST_DIR}/px_clock_gettime.c
${CMAKE_CURRENT_LIST_DIR}/px_clock_settime.c
${CMAKE_CURRENT_LIST_DIR}/px_cond_broadcast.c
${CMAKE_CURRENT_LIST_DIR}/px_cond_destroy.c
${CMAKE_CURRENT_LIST_DIR}/px_cond_init.c
${CMAKE_CURRENT_LIST_DIR}/px_cond_signal.c
${CMAKE_CURRENT_LIST_DIR}/px_cond_timedwait.c
${CMAKE_CURRENT_LIST_DIR}/px_cond_wait.c
${CMAKE_CURRENT_LIST_DIR}/px_error.c
${CMAKE_CURRENT_LIST_DIR}/px_in_thread_context.c
${CMAKE_CURRENT_LIST_DIR}/px_internal_signal_dispatch.c
${CMAKE_CURRENT_LIST_DIR}/px_memory_allocate.c
${CMAKE_CURRENT_LIST_DIR}/px_memory_release.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_arrange_msg.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_attr_init.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_close.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_create.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_find_queue.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_get_new_queue.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_get_queue_desc.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_open.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_priority_search.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_putback_queue.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_queue_delete.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_queue_init.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_receive.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_reset_queue.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_send.c
${CMAKE_CURRENT_LIST_DIR}/px_mq_unlink.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_destroy.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_getprotocol.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_getpshared.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_gettype.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_initi.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_setprotocol.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_setpshared.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_attr_settype.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_destroy.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_init.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_lock.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_set_default_mutexattr.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_timedlock.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_trylock.c
${CMAKE_CURRENT_LIST_DIR}/px_mx_unlock.c
${CMAKE_CURRENT_LIST_DIR}/px_nanosleep.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_destroy.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_getdetachstate.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_getinheritsched.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_getschedparam.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_getschedpolicy.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_getstack.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_getstackaddr.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_getstacksize.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_init.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_setdetachstate.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_setinheritsched.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_setschedparam.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_setschedpolicyl.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_setstack.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_setstackaddr.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_attr_setstacksize.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_cancel.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_create.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_detach.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_equal.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_exit.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_getcanceltype.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_getschedparam.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_init.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_join.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_kill.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_once.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_self.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_set_default_pthread_attr.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_setcancelstate.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_setcanceltype.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_setschedparam.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_sigmask.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_testcancel.c
${CMAKE_CURRENT_LIST_DIR}/px_pth_yield.c
${CMAKE_CURRENT_LIST_DIR}/px_px_initialize.c
${CMAKE_CURRENT_LIST_DIR}/px_sched_get_prio.c
${CMAKE_CURRENT_LIST_DIR}/px_sched_yield.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_close.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_destroy.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_find_sem.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_get_new_sem.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_getvalue.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_init.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_open.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_post.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_reset.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_set_sem_name.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_trywait.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_unlink.c
${CMAKE_CURRENT_LIST_DIR}/px_sem_wait.c
${CMAKE_CURRENT_LIST_DIR}/px_sig_addset.c
${CMAKE_CURRENT_LIST_DIR}/px_sig_delset.c
${CMAKE_CURRENT_LIST_DIR}/px_sig_emptyset.c
${CMAKE_CURRENT_LIST_DIR}/px_sig_fillset.c
${CMAKE_CURRENT_LIST_DIR}/px_sig_signal.c
${CMAKE_CURRENT_LIST_DIR}/px_sig_wait.c
${CMAKE_CURRENT_LIST_DIR}/px_sleep.c
${CMAKE_CURRENT_LIST_DIR}/px_system_manager.c
)
target_link_libraries(posix-threadx PUBLIC threadx)
@@ -278,9 +278,22 @@ typedef struct pthread_attr_obj
} pthread_attr_t;
typedef INT ssize_t ; /* this should be pulled in from sys\types.h */
/* ssize_t and mode_t belong to <sys/types.h>. A C library that has already
declared them wins: it knows the target's real widths, and on an LP64 target
ssize_t is 64 bits while INT is not. newlib guards each type with its own
_*_DECLARED macro, so honour those and only define what is missing. A port
whose library uses different guards can predefine them to the same effect. */
#ifndef _SSIZE_T_DECLARED
typedef INT ssize_t ;
#define _SSIZE_T_DECLARED
#endif
typedef ALIGN_TYPE pthread_t;
#ifndef _MODE_T_DECLARED
typedef ULONG mode_t;
#define _MODE_T_DECLARED
#endif