summaryrefslogtreecommitdiff
path: root/test/posix
diff options
context:
space:
mode:
Diffstat (limited to 'test/posix')
-rw-r--r--test/posix/cmake/CMakeLists.txt83
-rw-r--r--test/posix/cmake/regression/CMakeLists.txt65
-rwxr-xr-xtest/posix/cmake/run.sh189
-rw-r--r--test/posix/regression/posix_mq_basic_test.c159
-rw-r--r--test/posix/regression/posix_mq_send_abort_test.c273
-rw-r--r--test/posix/regression/posix_pthread_basic_test.c192
-rw-r--r--test/posix/regression/posix_pthread_self_test.c177
7 files changed, 1138 insertions, 0 deletions
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..8f433bcb
--- /dev/null
+++ b/test/posix/cmake/regression/CMakeLists.txt
@@ -0,0 +1,65 @@
+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
+ ${SOURCE_DIR}/posix_pthread_self_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()
diff --git a/test/posix/cmake/run.sh b/test/posix/cmake/run.sh
new file mode 100755
index 00000000..76536cf7
--- /dev/null
+++ b/test/posix/cmake/run.sh
@@ -0,0 +1,189 @@
+#!/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
+ # -k 0 keeps Ninja going after a target fails, so one broken target no
+ # longer decides whether the targets after it exist. ctest reports a
+ # missing binary as a failing test, which turned a single link error into
+ # a failure count that varied with build scheduling order.
+ cmake --build "build/${arch}_${build}" -- -k 0
+}
+
+function run_test() {
+ local arch=$1
+ local build=$2
+ local parallel_jobs=$3
+
+ # Guard the pushd: with the caller capturing this function's status, set -e
+ # no longer aborts here, so a missing build directory would otherwise let
+ # ctest run in the source tree and report "no tests" as success.
+ pushd "build/${arch}_${build}" || return 1
+ [ -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's status is captured and returned past the popd and the summary
+ # steps, which all succeed. Without this the function would return 0 and a
+ # failing configuration would be reported as a pass.
+ local status=0
+ 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" || status=$?
+ popd
+ return $status
+}
+
+# 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
+
+ # A failing configuration must not stop the ones after it: under set -e
+ # the loop would abort and leave them unbuilt, which then reads as a wall
+ # of missing-binary test failures. The status is accumulated and returned.
+ build_status=0
+ for item in $builds; do
+ echo "Building ${arch} ${item}"
+ build "$arch" "$item" || build_status=$?
+ echo ""
+ done
+ [ $build_status -eq 0 ] || exit $build_status
+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
+ # ctest's status is accumulated rather than allowed to abort the loop.
+ # set -e would otherwise stop at the first failing configuration and
+ # leave every configuration after it untested. This mirrors what the
+ # parallel branch above already does with wait.
+ exit_code=0
+ for item in $builds; do
+ echo "Testing ${arch} ${item}"
+ run_test "$arch" "$item" "$parallel_jobs" || exit_code=$?
+ done
+ exit $exit_code
+ 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 <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);
+}
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 <stdio.h>
+#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, &param);
+ 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, &param);
+ 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 <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);
+}
diff --git a/test/posix/regression/posix_pthread_self_test.c b/test/posix/regression/posix_pthread_self_test.c
new file mode 100644
index 00000000..7cbb6450
--- /dev/null
+++ b/test/posix/regression/posix_pthread_self_test.c
@@ -0,0 +1,177 @@
+/***************************************************************************/
+/* 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 pthread_self() being called from a thread that is not a
+ pthread.
+
+ Nothing stops an application mixing tx_thread_create() with the POSIX layer,
+ and a ThreadX thread created that way has no POSIX control block.
+ posix_thread2tcb() returns NULL for it, and pthread_self() used to read the
+ pthread ID through that null pointer, then read the signal fields of a
+ POSIX_TCB out of a plain TX_THREAD that never had any.
+
+ The test calls pthread_self() from both kinds of thread: a real pthread,
+ which must get the ID pthread_create() handed back, and a plain ThreadX
+ thread, which must get zero rather than a fault or a value read out of
+ whatever follows its control block. */
+
+#include <stdio.h>
+#include "tx_api.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;
+
+/* A plain ThreadX thread, deliberately not created through pthread_create(). */
+static TX_THREAD plain_thread;
+
+static volatile INT worker_done;
+static volatile INT plain_done;
+static pthread_t worker_reported_id;
+static pthread_t plain_reported_id;
+
+extern void test_control_return(UINT status);
+
+static VOID *worker_entry(VOID *input);
+static VOID plain_entry(ULONG input);
+static VOID checker_entry(ULONG input);
+
+static TX_THREAD checker_thread;
+
+
+/* Define what the initial system looks like. */
+
+#ifdef CTEST
+void test_application_define(void *first_unused_memory)
+#else
+void posix_pthread_self_application_define(void *first_unused_memory)
+#endif
+{
+
+struct sched_param param;
+VOID *storage_ptr;
+CHAR *pointer;
+
+ pointer = (CHAR *) first_unused_memory;
+
+ storage_ptr = posix_initialize(posix_region);
+
+ /* A genuine pthread. */
+ pthread_attr_init(&worker_attr);
+ param.sched_priority = 15;
+ 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 Pthread Self Test.................................. ERROR #1\n");
+ test_control_return(1);
+ }
+
+ /* A ThreadX thread that the POSIX layer knows nothing about. */
+ if (tx_thread_create(&plain_thread, "plain threadx thread", plain_entry, 0,
+ pointer, TEST_STACK_BYTES,
+ 20, 20, TX_NO_TIME_SLICE, TX_AUTO_START) != TX_SUCCESS)
+ {
+
+ printf("Running POSIX Pthread Self Test.................................. ERROR #2\n");
+ test_control_return(1);
+ }
+ pointer = pointer + TEST_STACK_BYTES;
+
+ /* Runs last and inspects what the other two reported. */
+ if (tx_thread_create(&checker_thread, "checker", checker_entry, 0,
+ pointer, TEST_STACK_BYTES,
+ 25, 25, TX_NO_TIME_SLICE, TX_AUTO_START) != TX_SUCCESS)
+ {
+
+ printf("Running POSIX Pthread Self Test.................................. ERROR #3\n");
+ test_control_return(1);
+ }
+}
+
+
+static VOID *worker_entry(VOID *input)
+{
+
+ (void) input;
+
+ worker_reported_id = pthread_self();
+ worker_done = 1;
+
+ return (NULL);
+}
+
+
+/* The case that used to fault: a ThreadX thread asking for its pthread ID. */
+static VOID plain_entry(ULONG input)
+{
+
+ (void) input;
+
+ plain_reported_id = pthread_self();
+ plain_done = 1;
+}
+
+
+static VOID checker_entry(ULONG input)
+{
+
+ (void) input;
+
+ printf("Running POSIX Pthread Self Test.................................. ");
+
+ /* Both threads run above this one, so they have finished by now. */
+ if ((worker_done == 0) || (plain_done == 0))
+ {
+
+ printf("ERROR #4\n");
+ test_control_return(1);
+ }
+
+ /* A pthread must report the ID pthread_create() produced. */
+ if (worker_reported_id != worker)
+ {
+
+ printf("ERROR #5\n");
+ test_control_return(1);
+ }
+
+ /* A pthread ID is the address of its control block, so a real one is never
+ zero. */
+ if (worker_reported_id == (pthread_t) 0)
+ {
+
+ printf("ERROR #6\n");
+ test_control_return(1);
+ }
+
+ /* The plain ThreadX thread has no pthread ID, and must be told so rather
+ than handed something read out of memory that follows its TX_THREAD. */
+ if (plain_reported_id != (pthread_t) 0)
+ {
+
+ printf("ERROR #7\n");
+ test_control_return(1);
+ }
+
+ printf("SUCCESS!\n");
+ test_control_return(0);
+}