From 30b3d22adceb10221de7ac3b4c4fd7ebcd3117e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Tue, 15 Sep 2026 16:24:58 -0400 Subject: [PATCH] Restored the random stack fill value cleared during thread creation (#732) Fixes #723 With `TX_ENABLE_STACK_CHECKING` and `TX_ENABLE_RANDOM_NUMBER_STACK_FILLING` both enabled, `_tx_thread_create` picked a random byte, stored it in `tx_thread_stack_fill_value`, filled the stack with it -- and then cleared the whole control block with `TX_MEMSET`. The stack held the pattern while the control block claimed zero, so `TX_THREAD_STACK_CHECK` and `_tx_thread_stack_analyze` compared against the wrong value for the entire life of the thread. The value is now computed into a local and written back after the clear. The clear stays where it is, because the module manager's error checking walks the created list before the control block may be touched. Fixed in `common/src/tx_thread_create.c`, `common_smp/src/tx_thread_create.c` and `txm_module_manager_thread_create.c`, where it additionally left a user-mode module thread's kernel stack filled with zeros. `threadx_thread_stack_fill_value_test` creates sixteen unstarted threads and checks each control block against the pattern in its stack, tolerating a random zero byte without letting that hide the defect. Added to both suites: `ERROR #3` on `dev` in `stack_checking_rand_fill_build`, green with the fix. Five tx configurations at 104 tests, five SMP at 117, and all three sources clean under `-Wall -Wextra` across every combination of the four stack-filling switches. Assisted-by: Copilot (Opus 5) --- common/src/tx_thread_create.c | 28 ++- .../src/txm_module_manager_thread_create.c | 26 ++- common_smp/src/tx_thread_create.c | 28 ++- test/smp/cmake/regression/CMakeLists.txt | 1 + test/smp/regression/testcontrol.c | 2 + .../threadx_thread_stack_fill_value_test.c | 190 ++++++++++++++++++ test/tx/cmake/regression/CMakeLists.txt | 1 + test/tx/regression/testcontrol.c | 2 + .../threadx_thread_stack_fill_value_test.c | 190 ++++++++++++++++++ 9 files changed, 453 insertions(+), 15 deletions(-) create mode 100644 test/smp/regression/threadx_thread_stack_fill_value_test.c create mode 100644 test/tx/regression/threadx_thread_stack_fill_value_test.c diff --git a/common/src/tx_thread_create.c b/common/src/tx_thread_create.c index 026b0ada..37b4d3e2 100644 --- a/common/src/tx_thread_create.c +++ b/common/src/tx_thread_create.c @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Copilot (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -93,18 +95,26 @@ UCHAR *temp_ptr; ALIGN_TYPE new_stack_start; ALIGN_TYPE updated_stack_start; #endif +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING) +ULONG stack_fill_value; +#endif #ifndef TX_DISABLE_STACK_FILLING #if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) /* Initialize the stack fill value to a 8-bit random value. */ - thread_ptr -> tx_thread_stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL; + stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL; /* Duplicate the random value in each of the 4 bytes of the stack fill value. */ - thread_ptr -> tx_thread_stack_fill_value = thread_ptr -> tx_thread_stack_fill_value | - (thread_ptr -> tx_thread_stack_fill_value << 8) | - (thread_ptr -> tx_thread_stack_fill_value << 16) | - (thread_ptr -> tx_thread_stack_fill_value << 24); + stack_fill_value = stack_fill_value | + (stack_fill_value << 8) | + (stack_fill_value << 16) | + (stack_fill_value << 24); + + /* Store the fill value in the control block so that the stack fill below picks it up + through the TX_STACK_FILL macro. The control block is cleared further down in this + function, so the value is stored again once that has been done. */ + thread_ptr -> tx_thread_stack_fill_value = stack_fill_value; #endif /* Set the thread stack to a pattern prior to creating the initial @@ -150,6 +160,14 @@ ALIGN_TYPE updated_stack_start; /* Initialize thread control block to all zeros. */ TX_MEMSET(thread_ptr, 0, (sizeof(TX_THREAD))); +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING) + + /* Clearing the control block reset the stack fill value, so store the value that was + actually used to fill the stack again. Otherwise the stack checking and stack analyze + routines would look for a pattern that is not the one present in the stack. */ + thread_ptr -> tx_thread_stack_fill_value = stack_fill_value; +#endif + /* Place the supplied parameters into the thread's control block. */ thread_ptr -> tx_thread_name = name_ptr; thread_ptr -> tx_thread_entry = entry_function; diff --git a/common_modules/module_manager/src/txm_module_manager_thread_create.c b/common_modules/module_manager/src/txm_module_manager_thread_create.c index a178cc35..fdb0e768 100644 --- a/common_modules/module_manager/src/txm_module_manager_thread_create.c +++ b/common_modules/module_manager/src/txm_module_manager_thread_create.c @@ -106,6 +106,9 @@ UCHAR *temp_ptr; ALIGN_TYPE new_stack_start; ALIGN_TYPE updated_stack_start; #endif +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING) +ULONG stack_fill_value; +#endif TXM_MODULE_THREAD_ENTRY_INFO *thread_entry_info; VOID *stack_end; ULONG i; @@ -263,13 +266,18 @@ ULONG i; #if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) /* Initialize the stack fill value to a 8-bit random value. */ - thread_ptr -> tx_thread_stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL; + stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL; /* Duplicate the random value in each of the 4 bytes of the stack fill value. */ - thread_ptr -> tx_thread_stack_fill_value = thread_ptr -> tx_thread_stack_fill_value | - (thread_ptr -> tx_thread_stack_fill_value << 8) | - (thread_ptr -> tx_thread_stack_fill_value << 16) | - (thread_ptr -> tx_thread_stack_fill_value << 24); + stack_fill_value = stack_fill_value | + (stack_fill_value << 8) | + (stack_fill_value << 16) | + (stack_fill_value << 24); + + /* Store the fill value in the control block so that the stack fill below picks it up + through the TX_STACK_FILL macro. The control block is cleared further down in this + function, so the value is stored again once that has been done. */ + thread_ptr -> tx_thread_stack_fill_value = stack_fill_value; #endif /* Set the thread stack to a pattern prior to creating the initial @@ -311,6 +319,14 @@ ULONG i; /* Initialize thread control block to all zeros. */ TX_MEMSET(thread_ptr, 0, sizeof(TX_THREAD)); +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING) + + /* Clearing the control block reset the stack fill value, so store the value that was + actually used to fill the stack again. Otherwise the stack checking and stack analyze + routines would look for a pattern that is not the one present in the stack. */ + thread_ptr -> tx_thread_stack_fill_value = stack_fill_value; +#endif + /* Note that TX_ENABLE_STACK_CHECKING is not supported for module threads. A user mode module thread owns two stacks and the scheduler swaps tx_thread_stack_start, tx_thread_stack_end and tx_thread_stack_size over to the kernel stack whenever the diff --git a/common_smp/src/tx_thread_create.c b/common_smp/src/tx_thread_create.c index 28a1dae1..05019ab8 100644 --- a/common_smp/src/tx_thread_create.c +++ b/common_smp/src/tx_thread_create.c @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Copilot (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -93,19 +95,27 @@ UCHAR *temp_ptr; ALIGN_TYPE new_stack_start; ALIGN_TYPE updated_stack_start; #endif +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING) +ULONG stack_fill_value; +#endif #ifndef TX_DISABLE_STACK_FILLING #if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) /* Initialize the stack fill value to a 8-bit random value. */ - thread_ptr -> tx_thread_stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL; + stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL; /* Duplicate the random value in each of the 4 bytes of the stack fill value. */ - thread_ptr -> tx_thread_stack_fill_value = thread_ptr -> tx_thread_stack_fill_value | - (thread_ptr -> tx_thread_stack_fill_value << 8) | - (thread_ptr -> tx_thread_stack_fill_value << 16) | - (thread_ptr -> tx_thread_stack_fill_value << 24); + stack_fill_value = stack_fill_value | + (stack_fill_value << 8) | + (stack_fill_value << 16) | + (stack_fill_value << 24); + + /* Store the fill value in the control block so that the stack fill below picks it up + through the TX_STACK_FILL macro. The control block is cleared further down in this + function, so the value is stored again once that has been done. */ + thread_ptr -> tx_thread_stack_fill_value = stack_fill_value; #endif /* Set the thread stack to a pattern prior to creating the initial @@ -143,6 +153,14 @@ ALIGN_TYPE updated_stack_start; /* Initialize thread control block to all zeros. */ TX_MEMSET(thread_ptr, 0, sizeof(TX_THREAD)); +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING) + + /* Clearing the control block reset the stack fill value, so store the value that was + actually used to fill the stack again. Otherwise the stack checking and stack analyze + routines would look for a pattern that is not the one present in the stack. */ + thread_ptr -> tx_thread_stack_fill_value = stack_fill_value; +#endif + /* Place the supplied parameters into the thread's control block. */ thread_ptr -> tx_thread_name = name_ptr; thread_ptr -> tx_thread_entry = entry_function; diff --git a/test/smp/cmake/regression/CMakeLists.txt b/test/smp/cmake/regression/CMakeLists.txt index dd1d48a3..771fb44d 100644 --- a/test/smp/cmake/regression/CMakeLists.txt +++ b/test/smp/cmake/regression/CMakeLists.txt @@ -114,6 +114,7 @@ set(regression_test_cases ${SOURCE_DIR}/threadx_thread_sleep_for_100ticks_test.c ${SOURCE_DIR}/threadx_thread_sleep_terminate_test.c ${SOURCE_DIR}/threadx_thread_stack_checking_test.c + ${SOURCE_DIR}/threadx_thread_stack_fill_value_test.c ${SOURCE_DIR}/threadx_thread_terminate_delete_test.c ${SOURCE_DIR}/threadx_thread_exit_callback_transition_test.c ${SOURCE_DIR}/threadx_smp_thread_exit_callback_remote_test.c diff --git a/test/smp/regression/testcontrol.c b/test/smp/regression/testcontrol.c index 632ae921..816d1508 100644 --- a/test/smp/regression/testcontrol.c +++ b/test/smp/regression/testcontrol.c @@ -255,6 +255,7 @@ void threadx_thread_create_preemption_threshold_application_define(void *); void threadx_thread_information_application_define(void *); void threadx_thread_reset_application_define(void *); void threadx_thread_stack_checking_application_define(void *); +void threadx_thread_stack_fill_value_application_define(void *); void threadx_time_get_set_application_define(void *); @@ -403,6 +404,7 @@ TEST_ENTRY test_control_tests[] = threadx_thread_information_application_define, threadx_thread_reset_application_define, threadx_thread_stack_checking_application_define, + threadx_thread_stack_fill_value_application_define, threadx_time_get_set_application_define, diff --git a/test/smp/regression/threadx_thread_stack_fill_value_test.c b/test/smp/regression/threadx_thread_stack_fill_value_test.c new file mode 100644 index 00000000..896c0c7f --- /dev/null +++ b/test/smp/regression/threadx_thread_stack_fill_value_test.c @@ -0,0 +1,190 @@ +/***************************************************************************/ +/* 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. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + +// Some portions generated by Copilot (Opus 5). + +/* This test checks that the stack fill value recorded in a thread control block is the + value that was actually written into that thread's stack. The two used to disagree + when TX_ENABLE_RANDOM_NUMBER_STACK_FILLING was enabled, because the control block was + cleared after the random value had been chosen and the stack had been filled with it, + which left the stack checking and stack analyze routines looking for a pattern that is + not the one present in the stack. */ + +#include +#include "tx_api.h" + + +/* Number of probe threads created. A random fill value can legitimately come out as zero, + which hides the defect for that one thread, so several threads are created and at least + one of them is required to have produced a non-zero pattern. */ + +#define TEST_PROBE_THREADS 16 + + +static unsigned long thread_0_counter = 0; +static TX_THREAD thread_0; + +static TX_THREAD probe_thread; +static ULONG probe_stack[TX_MINIMUM_STACK / sizeof(ULONG)]; + + +/* Define task prototypes. */ + +static void thread_0_entry(ULONG task_input); + + +/* Prototype for test control return. */ + +void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void threadx_thread_stack_fill_value_application_define(void *first_unused_memory) +#endif +{ + +UINT status; +CHAR *pointer; + + pointer = (CHAR *) first_unused_memory; + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + status = tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, TEST_STACK_SIZE_PRINTF, + 16, 16, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check for status. */ + if (status != TX_SUCCESS) + { + + printf("Running Thread Stack Fill Value Test................................ ERROR #1\n"); + test_control_return(1); + } +} + + +/* Define the test threads. */ + +static void thread_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +ULONG stack_pattern; +UINT non_zero_seen = TX_FALSE; + + + /* Increment thread 0 counter. */ + thread_0_counter++; + + /* Inform user of success getting to this test. */ + printf("Running Thread Stack Fill Value Test................................ "); + + for (i = 0; i < TEST_PROBE_THREADS; i++) + { + + /* Create a thread that is never started, so that its stack still holds nothing but + the fill pattern and the initial stack frame built at the top of the stack. */ + status = tx_thread_create(&probe_thread, "probe thread", thread_0_entry, 0, + probe_stack, sizeof(probe_stack), + 17, 17, TX_NO_TIME_SLICE, TX_DONT_START); + + /* Check for status. */ + if (status != TX_SUCCESS) + { + + printf("ERROR #2\n"); + test_control_return(1); + } + + /* Pick up the pattern the create service wrote into the unused part of the stack. */ + stack_pattern = *(((ULONG *) probe_thread.tx_thread_stack_start) + 4); + +#ifndef TX_DISABLE_STACK_FILLING +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) + + /* The control block must hold the value that was written into the stack. */ + if (probe_thread.tx_thread_stack_fill_value != stack_pattern) + { + + printf("ERROR #3\n"); + test_control_return(1); + } + + /* The random value is duplicated into all four bytes of the fill value. */ + if ((stack_pattern & 0xFFUL) != + ((stack_pattern >> 24) & 0xFFUL)) + { + + printf("ERROR #4\n"); + test_control_return(1); + } + + if (stack_pattern != ((ULONG) 0)) + { + + non_zero_seen = TX_TRUE; + } +#else + + /* Without random filling, the pattern is the fixed one. */ + if (stack_pattern != ((ULONG) 0xEFEFEFEFUL)) + { + + printf("ERROR #5\n"); + test_control_return(1); + } + + non_zero_seen = TX_TRUE; +#endif +#else + + /* Stack filling is disabled, so there is nothing to compare. */ + non_zero_seen = TX_TRUE; +#endif + + /* Terminate and delete the probe thread so that the next pass starts from a clean + slate. A thread created with TX_DONT_START is suspended rather than completed, + so it must be terminated before it can be deleted. */ + status = tx_thread_terminate(&probe_thread); + + if (status == TX_SUCCESS) + { + + status = tx_thread_delete(&probe_thread); + } + + /* Check for status. */ + if (status != TX_SUCCESS) + { + + printf("ERROR #6\n"); + test_control_return(1); + } + } + + /* A run in which every fill value came out as zero would prove nothing. */ + if (non_zero_seen != TX_TRUE) + { + + printf("ERROR #7\n"); + test_control_return(1); + } + + /* Success! */ + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/tx/cmake/regression/CMakeLists.txt b/test/tx/cmake/regression/CMakeLists.txt index 1479a93e..6b94a857 100644 --- a/test/tx/cmake/regression/CMakeLists.txt +++ b/test/tx/cmake/regression/CMakeLists.txt @@ -97,6 +97,7 @@ set(regression_test_cases ${SOURCE_DIR}/threadx_thread_sleep_for_100ticks_test.c ${SOURCE_DIR}/threadx_thread_sleep_terminate_test.c ${SOURCE_DIR}/threadx_thread_stack_checking_test.c + ${SOURCE_DIR}/threadx_thread_stack_fill_value_test.c ${SOURCE_DIR}/threadx_thread_terminate_delete_test.c ${SOURCE_DIR}/threadx_thread_exit_callback_transition_test.c ${SOURCE_DIR}/threadx_thread_time_slice_change_test.c diff --git a/test/tx/regression/testcontrol.c b/test/tx/regression/testcontrol.c index 6b60d79e..4e03dd55 100644 --- a/test/tx/regression/testcontrol.c +++ b/test/tx/regression/testcontrol.c @@ -232,6 +232,7 @@ void threadx_thread_create_preemption_threshold_application_define(void *); void threadx_thread_information_application_define(void *); void threadx_thread_reset_application_define(void *); void threadx_thread_stack_checking_application_define(void *); +void threadx_thread_stack_fill_value_application_define(void *); void threadx_time_get_set_application_define(void *); @@ -352,6 +353,7 @@ TEST_ENTRY test_control_tests[] = threadx_thread_information_application_define, threadx_thread_reset_application_define, threadx_thread_stack_checking_application_define, + threadx_thread_stack_fill_value_application_define, threadx_time_get_set_application_define, diff --git a/test/tx/regression/threadx_thread_stack_fill_value_test.c b/test/tx/regression/threadx_thread_stack_fill_value_test.c new file mode 100644 index 00000000..896c0c7f --- /dev/null +++ b/test/tx/regression/threadx_thread_stack_fill_value_test.c @@ -0,0 +1,190 @@ +/***************************************************************************/ +/* 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. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + +// Some portions generated by Copilot (Opus 5). + +/* This test checks that the stack fill value recorded in a thread control block is the + value that was actually written into that thread's stack. The two used to disagree + when TX_ENABLE_RANDOM_NUMBER_STACK_FILLING was enabled, because the control block was + cleared after the random value had been chosen and the stack had been filled with it, + which left the stack checking and stack analyze routines looking for a pattern that is + not the one present in the stack. */ + +#include +#include "tx_api.h" + + +/* Number of probe threads created. A random fill value can legitimately come out as zero, + which hides the defect for that one thread, so several threads are created and at least + one of them is required to have produced a non-zero pattern. */ + +#define TEST_PROBE_THREADS 16 + + +static unsigned long thread_0_counter = 0; +static TX_THREAD thread_0; + +static TX_THREAD probe_thread; +static ULONG probe_stack[TX_MINIMUM_STACK / sizeof(ULONG)]; + + +/* Define task prototypes. */ + +static void thread_0_entry(ULONG task_input); + + +/* Prototype for test control return. */ + +void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void threadx_thread_stack_fill_value_application_define(void *first_unused_memory) +#endif +{ + +UINT status; +CHAR *pointer; + + pointer = (CHAR *) first_unused_memory; + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + status = tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, TEST_STACK_SIZE_PRINTF, + 16, 16, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check for status. */ + if (status != TX_SUCCESS) + { + + printf("Running Thread Stack Fill Value Test................................ ERROR #1\n"); + test_control_return(1); + } +} + + +/* Define the test threads. */ + +static void thread_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +ULONG stack_pattern; +UINT non_zero_seen = TX_FALSE; + + + /* Increment thread 0 counter. */ + thread_0_counter++; + + /* Inform user of success getting to this test. */ + printf("Running Thread Stack Fill Value Test................................ "); + + for (i = 0; i < TEST_PROBE_THREADS; i++) + { + + /* Create a thread that is never started, so that its stack still holds nothing but + the fill pattern and the initial stack frame built at the top of the stack. */ + status = tx_thread_create(&probe_thread, "probe thread", thread_0_entry, 0, + probe_stack, sizeof(probe_stack), + 17, 17, TX_NO_TIME_SLICE, TX_DONT_START); + + /* Check for status. */ + if (status != TX_SUCCESS) + { + + printf("ERROR #2\n"); + test_control_return(1); + } + + /* Pick up the pattern the create service wrote into the unused part of the stack. */ + stack_pattern = *(((ULONG *) probe_thread.tx_thread_stack_start) + 4); + +#ifndef TX_DISABLE_STACK_FILLING +#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) + + /* The control block must hold the value that was written into the stack. */ + if (probe_thread.tx_thread_stack_fill_value != stack_pattern) + { + + printf("ERROR #3\n"); + test_control_return(1); + } + + /* The random value is duplicated into all four bytes of the fill value. */ + if ((stack_pattern & 0xFFUL) != + ((stack_pattern >> 24) & 0xFFUL)) + { + + printf("ERROR #4\n"); + test_control_return(1); + } + + if (stack_pattern != ((ULONG) 0)) + { + + non_zero_seen = TX_TRUE; + } +#else + + /* Without random filling, the pattern is the fixed one. */ + if (stack_pattern != ((ULONG) 0xEFEFEFEFUL)) + { + + printf("ERROR #5\n"); + test_control_return(1); + } + + non_zero_seen = TX_TRUE; +#endif +#else + + /* Stack filling is disabled, so there is nothing to compare. */ + non_zero_seen = TX_TRUE; +#endif + + /* Terminate and delete the probe thread so that the next pass starts from a clean + slate. A thread created with TX_DONT_START is suspended rather than completed, + so it must be terminated before it can be deleted. */ + status = tx_thread_terminate(&probe_thread); + + if (status == TX_SUCCESS) + { + + status = tx_thread_delete(&probe_thread); + } + + /* Check for status. */ + if (status != TX_SUCCESS) + { + + printf("ERROR #6\n"); + test_control_return(1); + } + } + + /* A run in which every fill value came out as zero would prove nothing. */ + if (non_zero_seen != TX_TRUE) + { + + printf("ERROR #7\n"); + test_control_return(1); + } + + /* Success! */ + printf("SUCCESS!\n"); + test_control_return(0); +}