From f184531e7f90cafb53b0d61293ccc597e7ede226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Sun, 16 Aug 2026 18:24:48 -0400 Subject: [PATCH] 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 . 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) * 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) --- CMakeLists.txt | 18 ++ scripts/build_posix.sh | 17 ++ scripts/test_posix.sh | 17 ++ test/posix/cmake/CMakeLists.txt | 83 ++++++ test/posix/cmake/regression/CMakeLists.txt | 64 ++++ test/posix/cmake/run.sh | 166 +++++++++++ test/posix/regression/posix_mq_basic_test.c | 159 ++++++++++ .../regression/posix_mq_send_abort_test.c | 273 ++++++++++++++++++ .../regression/posix_pthread_basic_test.c | 192 ++++++++++++ .../posix/CMakeLists.txt | 129 +++++++++ .../posix/tx_posix.h | 15 +- 11 files changed, 1132 insertions(+), 1 deletion(-) create mode 100755 scripts/build_posix.sh create mode 100755 scripts/test_posix.sh create mode 100644 test/posix/cmake/CMakeLists.txt create mode 100644 test/posix/cmake/regression/CMakeLists.txt create mode 100755 test/posix/cmake/run.sh create mode 100644 test/posix/regression/posix_mq_basic_test.c create mode 100644 test/posix/regression/posix_mq_send_abort_test.c create mode 100644 test/posix/regression/posix_pthread_basic_test.c create mode 100644 utility/rtos_compatibility_layers/posix/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 546606c1..9fbe6982 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") diff --git a/scripts/build_posix.sh b/scripts/build_posix.sh new file mode 100755 index 00000000..ac81c8ff --- /dev/null +++ b/scripts/build_posix.sh @@ -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 diff --git a/scripts/test_posix.sh b/scripts/test_posix.sh new file mode 100755 index 00000000..ce851cf4 --- /dev/null +++ b/scripts/test_posix.sh @@ -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 diff --git a/test/posix/cmake/CMakeLists.txt b/test/posix/cmake/CMakeLists.txt new file mode 100644 index 00000000..830e69ba --- /dev/null +++ b/test/posix/cmake/CMakeLists.txt @@ -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) diff --git a/test/posix/cmake/regression/CMakeLists.txt b/test/posix/cmake/regression/CMakeLists.txt new file mode 100644 index 00000000..c8320b05 --- /dev/null +++ b/test/posix/cmake/regression/CMakeLists.txt @@ -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 $ + ) + 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_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() diff --git a/test/posix/cmake/run.sh b/test/posix/cmake/run.sh new file mode 100755 index 00000000..aa65f7a7 --- /dev/null +++ b/test/posix/cmake/run.sh @@ -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 [all| ...] +# 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 [all| ...]" + 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 diff --git a/test/posix/regression/posix_mq_basic_test.c b/test/posix/regression/posix_mq_basic_test.c new file mode 100644 index 00000000..2ecb1ac8 --- /dev/null +++ b/test/posix/regression/posix_mq_basic_test.c @@ -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 +#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, ¶m); + 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); +} diff --git a/test/posix/regression/posix_mq_send_abort_test.c b/test/posix/regression/posix_mq_send_abort_test.c new file mode 100644 index 00000000..a4e7e7e6 --- /dev/null +++ b/test/posix/regression/posix_mq_send_abort_test.c @@ -0,0 +1,273 @@ +/***************************************************************************/ +/* 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 the message buffer mq_send() used to leak when the send + failed, reported as issue #568. + + mq_send() allocates a private buffer from the queue's own byte pool, copies + the caller's message into it and passes the address through the queue. The + receiver owns the buffer from then on and releases it. When the send itself + fails the message never reaches a receiver, so mq_send() has to release the + buffer before returning or it is lost. + + The failure is provoked the way an application would meet it: the queue is + filled, a sender blocks on it, and its wait is aborted. The queue survives, + so any buffer that is not released stays lost. Aborting repeatedly and + watching the queue's byte pool separates the two outcomes: with the buffer + released the pool returns to its original size, and without it the pool + shrinks by a message every time. */ + +#include +#include "tx_api.h" +#include "pthread.h" + +/* The queue's pool is sized (mq_maxmsg + 1) * (mq_msgsize + 11), with a floor + of 100 bytes, so it holds barely more than a full queue. Keep the messages + small and the queue shallow: that floor then leaves room for a few leaked + buffers to accumulate and be measured, instead of exhausting the pool on the + first one and hanging the sender inside posix_internal_error(). */ +#define TEST_MESSAGE_SIZE 8 +#define TEST_QUEUE_DEPTH 1 +#define TEST_ABORT_COUNT 2 + +static ULONG posix_region[192 * 1024 / sizeof(ULONG)]; + +static pthread_t sender; +static pthread_attr_t sender_attr; +static pthread_t controller; +static pthread_attr_t controller_attr; + +static mqd_t queue_des; +static struct mq_attr queue_attr; + +/* Written by the sender, read by the controller. */ +static TX_THREAD * volatile sender_thread_ptr; +static volatile INT send_failures; +static volatile INT stop_sending; + +extern void test_control_return(UINT status); + +static VOID *sender_entry(VOID *input); +static VOID *controller_entry(VOID *input); + + +/* Report how many bytes the queue's message pool has free. */ +static ULONG pool_available(void) +{ + +ULONG available; + + available = 0; + tx_byte_pool_info_get(&(queue_des -> f_data -> vq_message_area), + TX_NULL, &available, TX_NULL, TX_NULL, TX_NULL, TX_NULL); + + return (available); +} + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void posix_mq_send_abort_application_define(void *first_unused_memory) +#endif +{ + +struct sched_param param; +VOID *storage_ptr; + + (void) first_unused_memory; + + 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_abort", O_CREAT | O_RDWR, 0, &queue_attr); + if (queue_des == (mqd_t) ERROR) + { + + printf("Running POSIX Message Queue Send Abort Test....................... ERROR #1\n"); + test_control_return(1); + } + + /* The controller drives the test and runs above the sender, so it keeps + control while the sender is being unblocked. */ + pthread_attr_init(&controller_attr); + param.sched_priority = 15; + pthread_attr_setschedparam(&controller_attr, ¶m); + pthread_attr_setstackaddr(&controller_attr, storage_ptr); + storage_ptr = (ULONG *) storage_ptr + (4096 / sizeof(ULONG)); + + pthread_attr_init(&sender_attr); + param.sched_priority = 10; + pthread_attr_setschedparam(&sender_attr, ¶m); + pthread_attr_setstackaddr(&sender_attr, storage_ptr); + + if (pthread_create(&controller, &controller_attr, controller_entry, NULL) != OK) + { + + printf("Running POSIX Message Queue Send Abort Test....................... ERROR #2\n"); + test_control_return(1); + } + + if (pthread_create(&sender, &sender_attr, sender_entry, NULL) != OK) + { + + printf("Running POSIX Message Queue Send Abort Test....................... ERROR #3\n"); + test_control_return(1); + } +} + + +/* Sends until told to stop. Every send blocks, because the controller keeps + the queue full, and every one is released by an aborted wait. */ +static VOID *sender_entry(VOID *input) +{ + +CHAR message[TEST_MESSAGE_SIZE]; +INT i; + + (void) input; + + for (i = 0; i < TEST_MESSAGE_SIZE; i++) + { + + message[i] = (CHAR) i; + } + + /* Publish our own thread pointer so the controller can abort our wait + without casting a pthread ID back to a ThreadX thread. */ + sender_thread_ptr = tx_thread_identify(); + + while (stop_sending == 0) + { + + if (mq_send(queue_des, message, TEST_MESSAGE_SIZE, 1) != OK) + { + + send_failures++; + } + } + + return (NULL); +} + + +static VOID *controller_entry(VOID *input) +{ + +CHAR message[TEST_MESSAGE_SIZE]; +ULONG baseline; +ULONG after; +ULONG lost; +INT i; + + (void) input; + + printf("Running POSIX Message Queue Send Abort Test....................... "); + + for (i = 0; i < TEST_MESSAGE_SIZE; i++) + { + + message[i] = (CHAR) i; + } + + /* Fill the queue so that any further send has to wait. */ + if (mq_send(queue_des, message, TEST_MESSAGE_SIZE, 1) != OK) + { + + printf("ERROR #4\n"); + test_control_return(1); + } + + /* Let the sender reach its first blocking send, then take the baseline. + The sender is holding one allocation at this point, which is the state + every later measurement is compared against. */ + tx_thread_sleep(2); + + if (sender_thread_ptr == TX_NULL) + { + + printf("ERROR #5\n"); + test_control_return(1); + } + + baseline = pool_available(); + + for (i = 0; i < TEST_ABORT_COUNT; i++) + { + + /* Release the sender from its wait. Its send fails, and the buffer it + allocated has to come back to the pool. */ + if (tx_thread_wait_abort(sender_thread_ptr) != TX_SUCCESS) + { + + printf("ERROR #6\n"); + test_control_return(1); + } + + /* Give the sender time to return from mq_send() and block again. */ + tx_thread_sleep(2); + } + + if (send_failures != TEST_ABORT_COUNT) + { + + printf("ERROR #7\n"); + test_control_return(1); + } + + /* The sender is blocked again holding exactly one allocation, the same as + at the baseline, so the pool should read the same. */ + after = pool_available(); + + if (after > baseline) + { + + printf("ERROR #8\n"); + test_control_return(1); + } + + lost = baseline - after; + + /* Anything approaching a message means buffers are not coming back. A + leak would cost TEST_ABORT_COUNT messages, so this separates the two + cases comfortably without depending on byte pool overhead. */ + if (lost >= (ULONG) TEST_MESSAGE_SIZE) + { + + printf("ERROR #9\n"); + test_control_return(1); + } + + /* Shut the sender down and confirm the queue still works. */ + stop_sending = 1; + tx_thread_wait_abort(sender_thread_ptr); + tx_thread_sleep(2); + + if (mq_receive(queue_des, message, TEST_MESSAGE_SIZE, TX_NULL) != (ssize_t) TEST_MESSAGE_SIZE) + { + + printf("ERROR #10\n"); + test_control_return(1); + } + + printf("SUCCESS!\n"); + test_control_return(0); + + return (NULL); +} diff --git a/test/posix/regression/posix_pthread_basic_test.c b/test/posix/regression/posix_pthread_basic_test.c new file mode 100644 index 00000000..afb8d4f3 --- /dev/null +++ b/test/posix/regression/posix_pthread_basic_test.c @@ -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 +#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, ¶m); + 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, ¶m); + 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); +} diff --git a/utility/rtos_compatibility_layers/posix/CMakeLists.txt b/utility/rtos_compatibility_layers/posix/CMakeLists.txt new file mode 100644 index 00000000..6ddcd6a8 --- /dev/null +++ b/utility/rtos_compatibility_layers/posix/CMakeLists.txt @@ -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) diff --git a/utility/rtos_compatibility_layers/posix/tx_posix.h b/utility/rtos_compatibility_layers/posix/tx_posix.h index ce533dd8..3f235cb9 100644 --- a/utility/rtos_compatibility_layers/posix/tx_posix.h +++ b/utility/rtos_compatibility_layers/posix/tx_posix.h @@ -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 . 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