diff options
| author | Frédéric Desbiens <[email protected]> | 2026-10-02 10:42:44 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-10-02 10:42:44 -0400 |
| commit | 93387b0a60382a29579bbad5045c68328e3751df (patch) | |
| tree | 42be790d602cb76f55090e5a7c418df7ea2b3428 /test/posix/regression | |
| parent | 44d7c95c582d415c4ad84527180b29c93c3bf664 (diff) | |
| parent | 28281cd5647d4671236ca08d19bd18ed15b2265a (diff) | |
Merge pull request #783 from eclipse-threadx/devv6.5.2.202603_rel
Merged changes ahead of the v6.5.2.202603 release
Diffstat (limited to 'test/posix/regression')
| -rw-r--r-- | test/posix/regression/posix_mq_basic_test.c | 159 | ||||
| -rw-r--r-- | test/posix/regression/posix_mq_send_abort_test.c | 273 | ||||
| -rw-r--r-- | test/posix/regression/posix_pthread_basic_test.c | 192 | ||||
| -rw-r--r-- | test/posix/regression/posix_pthread_self_test.c | 177 |
4 files changed, 801 insertions, 0 deletions
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, ¶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 <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, ¶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 <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, ¶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/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, ¶m); + 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); +} |
