Files
threadx/test/smp/regression/testcontrol.c
T
Frédéric Desbiens 30b3d22adc 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) <noreply@github.com>
2026-09-15 16:24:58 -04:00

1933 lines
80 KiB
C

/***************************************************************************/
/* Copyright (c) 2024 Microsoft Corporation */
/* 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 */
/***************************************************************************/
/* This is the test control routine of the ThreadX kernel. All tests are dispatched from this routine. */
// Some portions generated by Codex (gpt 5.5).
// Some portions generated by Copilot (Opus 5).
#define TX_THREAD_SMP_SOURCE_CODE
#include "tx_api.h"
#include <stdio.h>
#include "tx_initialize.h"
#include "tx_timer.h"
#include "tx_thread.h"
#include "tx_semaphore.h"
#include "tx_queue.h"
#include "tx_mutex.h"
#include "tx_block_pool.h"
#include "tx_byte_pool.h"
#include "tx_event_flags.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#define TEST_STACK_SIZE 6144
/* Define the test control ThreadX objects... */
TX_THREAD test_control_thread;
TX_THREAD test_thread;
TX_THREAD test_thread1;
TX_THREAD test_thread2;
TX_THREAD test_thread3;
ULONG test_thread3_stack[256];
TX_QUEUE test_queue;
ULONG test_thread2_stack[256];
TX_TIMER test_timer;
TX_MUTEX init_mutex;
TX_MUTEX init_mutex_inherit;
TX_MUTEX cleanup_mutex;
TX_EVENT_FLAGS_GROUP cleanup_event_flags;
TX_BLOCK_POOL cleanup_block_pool;
TX_BYTE_POOL cleanup_byte_pool;
TX_QUEUE cleanup_queue;
TX_SEMAPHORE cleanup_semaphore;
TX_SEMAPHORE init_semaphore;
ULONG init_queue_area[20];
TX_QUEUE init_queue;
TX_BYTE_POOL init_byte_pool;
ULONG init_byte_pool_area[50];
TX_BLOCK_POOL init_block_pool;
ULONG init_block_pool_area[50];
TX_EVENT_FLAGS_GROUP init_event_flags;
TX_TIMER init_timer;
TX_TIMER init_timer_change;
TX_THREAD init_test_thread;
TX_THREAD second_test_thread;
#ifndef TX_TIMER_PROCESS_IN_ISR
TEST_FLAG threadx_delete_timer_thread;
#endif
TX_TIMER_INTERNAL **_timer_list_start_backup;
TEST_FLAG test_stack_analyze_flag;
TEST_FLAG test_initialize_flag;
TX_BLOCK_POOL fake_block_pool;
TX_BYTE_POOL fake_byte_pool;
TX_EVENT_FLAGS_GROUP fake_event_flags;
TX_MUTEX fake_mutex;
TX_QUEUE fake_queue;
TX_SEMAPHORE fake_semaphore;
TX_THREAD test_thread4;
TX_THREAD test_thread5;
TX_THREAD test_thread6;
TX_THREAD test_thread7;
TX_THREAD test_thread8;
TX_THREAD test_thread9;
TX_THREAD test_thread10;
TX_THREAD test_thread11;
TX_THREAD test_thread12;
TX_THREAD test_thread13;
TX_THREAD test_thread14;
ULONG test_thread4_stack[256];
ULONG test_thread5_stack[256];
ULONG test_thread6_stack[256];
ULONG test_thread7_stack[256];
ULONG test_thread8_stack[256];
ULONG test_thread9_stack[256];
ULONG test_thread10_stack[256];
ULONG test_thread11_stack[256];
ULONG test_thread12_stack[256];
ULONG test_thread13_stack[256];
ULONG test_thread14_stack[256];
/* Define the test control global variables. */
ULONG test_control_return_status;
ULONG test_control_successful_tests;
ULONG test_control_failed_tests;
ULONG test_control_system_errors;
UINT test_mutex_from_init;
UINT test_semaphore_from_init;
UINT test_queue_from_init;
UINT test_event_flags_from_init;
UINT test_byte_pool_create_init;
UINT test_block_pool_create_init;
UINT test_timer_create_init;
/* Remember the start of free memory. */
UCHAR *test_free_memory_ptr;
/* Define the function pointer for ISR dispatch. */
VOID (*test_isr_dispatch)(void);
UCHAR test_control_memory[0x60000];
UCHAR tests_memory[0x60000];
UINT mutex_priority_change_extension_selection;
UINT priority_change_extension_selection;
TEST_FLAG test_forced_mutex_timeout;
TEST_FLAG threadx_byte_allocate_loop_test;
TEST_FLAG threadx_byte_release_loop_test;
TEST_FLAG threadx_mutex_suspension_put_test;
TEST_FLAG threadx_mutex_suspension_priority_test;
/* Define the external reference for the preempt disable flag. */
extern volatile UINT _tx_thread_preempt_disable;
extern volatile ULONG _tx_thread_system_state[TX_THREAD_SMP_MAX_CORES];
/* Define test entry pointer type. */
typedef struct TEST_ENTRY_STRUCT
{
VOID (*test_entry)(void *);
} TEST_ENTRY;
/* Define the prototypes for the test entry points. */
void threadx_block_memory_basic_application_define(void *);
void threadx_block_memory_error_detection_application_define(void *);
void threadx_block_memory_suspension_application_define(void *);
void threadx_block_memory_suspension_timeout_application_define(void *);
void threadx_block_memory_thread_terminate_application_define(void *);
void threadx_block_memory_prioritize_application_define(void *);
void threadx_block_memory_information_application_define(void *);
void threadx_byte_memory_basic_application_define(void *);
void threadx_byte_memory_thread_contention_application_define(void *);
void threadx_byte_memory_suspension_application_define(void *);
void threadx_byte_memory_suspension_timeout_application_define(void *);
void threadx_byte_memory_thread_terminate_application_define(void *);
void threadx_byte_memory_prioritize_application_define(void *);
void threadx_byte_memory_information_application_define(void *);
void threadx_event_flag_basic_application_define(void *);
void threadx_event_flag_suspension_application_define(void *);
void threadx_event_flag_suspension_consume_application_define(void *);
void threadx_event_flag_suspension_different_bits_application_define(void *);
void threadx_event_flag_suspension_different_bits_consume_application_define(void *);
void threadx_event_flag_suspension_timeout_application_define(void *);
void threadx_event_flag_thread_terminate_application_define(void *);
void threadx_event_flag_single_thread_terminate_application_define(void *);
void threadx_event_flag_isr_set_clear_application_define(void *);
void threadx_event_flag_isr_wait_abort_application_define(void *);
void threadx_event_flag_information_application_define(void *);
void threadx_interrupt_control_application_define(void *);
void threadx_mutex_basic_application_define(void *);
void threadx_mutex_delete_application_define(void *);
void threadx_mutex_preemption_application_define(void *);
void threadx_mutext_no_preemption_application_define(void *);
void threadx_mutex_suspension_timeout_application_define(void *);
void threadx_mutex_thread_terminate_application_define(void *);
void threadx_mutex_priority_inheritance_application_define(void *);
void threadx_mutex_nested_priority_inheritance_application_define(void *);
void threadx_mutex_prioritize_application_define(void *);
void threadx_mutex_information_application_define(void *);
void threadx_queue_basic_application_define(void *);
void threadx_queue_basic_two_word_application_define(void *);
void threadx_queue_basic_four_word_application_define(void *);
void threadx_queue_basic_eight_word_application_define(void *);
void threadx_queue_basic_sixteen_word_application_define(void *);
void threadx_queue_basic_max_message_size_application_define(void *);
void threadx_queue_empty_suspension_application_define(void *);
void threadx_queue_full_suspension_application_define(void *);
void threadx_queue_suspension_timeout_application_define(void *);
void threadx_queue_thread_terminate_application_define(void *);
void threadx_queue_flush_application_define(void *);
void threadx_queue_flush_no_suspension_application_define(void *);
void threadx_queue_front_send_application_define(void *);
void threadx_queue_prioritize_application_define(void *);
void threadx_queue_information_application_define(void *);
void threadx_semaphore_basic_application_define(void *);
void threadx_semaphore_delete_application_define(void *);
void threadx_semaphore_preemption_application_define(void *);
void threadx_semaphore_non_preemption_application_define(void *);
void threadx_semaphore_timeout_application_define(void *);
void threadx_semaphore_thread_terminate_application_define(void *);
void threadx_semaphore_prioritize_application_define(void *);
void threadx_semaphore_ceiling_put_application_define(void *);
void threadx_semaphore_information_application_define(void *);
void threadx_thread_basic_execution_application_define(void *);
void threadx_thread_completed_application_define(void *);
void threadx_thread_relinquish_application_define(void *);
void threadx_thread_simple_suspend_application_define(void *);
void threadx_thread_multiple_suspension_application_define(void *);
void threadx_thread_multiple_non_current_suspension_application_define(void *);
void threadx_thread_multi_level_preemption_threshold_application_define(void *);
void threadx_thread_preemptable_suspension_application_define(void *);
void threadx_thread_basic_time_slice_application_define(void *);
void threadx_thread_multiple_time_slice_application_define(void *);
void threadx_thread_simple_sleep_application_define(void *);
void threadx_thread_simple_sleep_non_clear_application_define(void *);
void threadx_thread_sleep_for_100ticks_application_define(void *);
void threadx_thread_multiple_sleep_application_define(void *);
void threadx_thread_terminate_delete_application_define(void *);
void threadx_thread_exit_callback_transition_application_define(void *);
void threadx_smp_thread_exit_callback_remote_application_define(void *);
void threadx_thread_preemption_change_application_define(void *);
void threadx_thread_priority_change_application_define(void *);
void threadx_thread_time_slice_change_application_define(void *);
void threadx_thread_sleep_terminate_application_define(void *);
void threadx_thread_delayed_suspension_application_define(void *);
void threadx_thread_wait_abort_application_define(void *);
void threadx_thread_wait_abort_and_isr_application_define(void *);
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 *);
void threadx_timer_simple_application_define(void *);
void threadx_timer_activate_deactivate_application_define(void *);
void threadx_timer_deactivate_accuracy_application_define(void *);
void threadx_timer_large_timer_accuracy_application_define(void *);
void threadx_timer_multiple_application_define(void *);
void threadx_timer_multiple_accuracy_application_define(void *);
void threadx_timer_information_application_define(void *);
void threadx_trace_basic_application_define(void *);
void threadx_smp_rebalance_exclustion_test(void *first_unused_memory);
void threadx_smp_two_threads_one_core_test(void *first_unused_memory);
void threadx_smp_multiple_threads_one_core_test(void *first_unused_memory);
void threadx_smp_one_thread_dynamic_exclusion_test(void *first_unused_memory);
void threadx_smp_non_trivial_scheduling_test(void *first_unused_memory);
void threadx_smp_resume_suspend_ascending_order_test(void *first_unused_memory);
void threadx_smp_resume_suspend_descending_order_test(void *first_unused_memory);
void threadx_smp_preemption_threshold_test(void *first_unused_memory);
void threadx_smp_relinquish_test(void *first_unused_memory);
void threadx_smp_time_slice_test(void *first_unused_memory);
void threadx_smp_random_resume_suspend_test(void *first_unused_memory);
void threadx_smp_random_resume_suspend_exclusion_test(void *first_unused_memory);
void threadx_smp_random_resume_suspend_exclusion_pt_test(void *first_unused_memory);
void test_application_define(void *first_unused_memory);
/* Define the array of test entry points. */
TEST_ENTRY test_control_tests[] =
{
#if CTEST
test_application_define,
#else
threadx_smp_rebalance_exclustion_test,
threadx_smp_two_threads_one_core_test,
threadx_smp_multiple_threads_one_core_test,
threadx_smp_one_thread_dynamic_exclusion_test,
threadx_smp_non_trivial_scheduling_test,
threadx_smp_resume_suspend_ascending_order_test,
threadx_smp_resume_suspend_descending_order_test,
threadx_smp_preemption_threshold_test,
threadx_smp_relinquish_test,
threadx_smp_time_slice_test,
threadx_smp_random_resume_suspend_test,
threadx_smp_random_resume_suspend_exclusion_test,
threadx_smp_random_resume_suspend_exclusion_pt_test,
threadx_block_memory_basic_application_define,
threadx_block_memory_error_detection_application_define,
threadx_block_memory_prioritize_application_define,
threadx_block_memory_suspension_application_define,
threadx_block_memory_suspension_timeout_application_define,
threadx_block_memory_thread_terminate_application_define,
threadx_block_memory_information_application_define,
threadx_byte_memory_basic_application_define,
threadx_byte_memory_suspension_application_define,
threadx_byte_memory_suspension_timeout_application_define,
threadx_byte_memory_thread_terminate_application_define,
threadx_byte_memory_prioritize_application_define,
threadx_byte_memory_thread_contention_application_define,
threadx_byte_memory_information_application_define,
threadx_event_flag_basic_application_define,
threadx_event_flag_suspension_application_define,
threadx_event_flag_suspension_consume_application_define,
threadx_event_flag_suspension_different_bits_application_define,
threadx_event_flag_suspension_different_bits_consume_application_define,
threadx_event_flag_suspension_timeout_application_define,
threadx_event_flag_thread_terminate_application_define,
threadx_event_flag_single_thread_terminate_application_define,
threadx_event_flag_isr_set_clear_application_define,
threadx_event_flag_isr_wait_abort_application_define,
threadx_event_flag_information_application_define,
threadx_interrupt_control_application_define,
threadx_mutex_basic_application_define,
threadx_mutex_delete_application_define,
threadx_mutex_preemption_application_define,
threadx_mutext_no_preemption_application_define,
threadx_mutex_suspension_timeout_application_define,
threadx_mutex_thread_terminate_application_define,
threadx_mutex_priority_inheritance_application_define,
threadx_mutex_prioritize_application_define,
threadx_mutex_nested_priority_inheritance_application_define,
threadx_mutex_information_application_define,
threadx_queue_basic_application_define,
threadx_queue_basic_two_word_application_define,
threadx_queue_basic_four_word_application_define,
threadx_queue_basic_eight_word_application_define,
threadx_queue_basic_sixteen_word_application_define,
threadx_queue_basic_max_message_size_application_define,
threadx_queue_empty_suspension_application_define,
threadx_queue_full_suspension_application_define,
threadx_queue_suspension_timeout_application_define,
threadx_queue_thread_terminate_application_define,
threadx_queue_flush_application_define,
threadx_queue_flush_no_suspension_application_define,
threadx_queue_front_send_application_define,
threadx_queue_prioritize_application_define,
threadx_queue_information_application_define,
threadx_semaphore_basic_application_define,
threadx_semaphore_delete_application_define,
threadx_semaphore_preemption_application_define,
threadx_semaphore_non_preemption_application_define,
threadx_semaphore_timeout_application_define,
threadx_semaphore_thread_terminate_application_define,
threadx_semaphore_prioritize_application_define,
threadx_semaphore_ceiling_put_application_define,
threadx_semaphore_information_application_define,
threadx_thread_basic_execution_application_define,
threadx_thread_completed_application_define,
threadx_thread_relinquish_application_define,
threadx_thread_simple_suspend_application_define,
threadx_thread_multiple_suspension_application_define,
threadx_thread_multiple_non_current_suspension_application_define,
threadx_thread_multi_level_preemption_threshold_application_define,
threadx_thread_preemptable_suspension_application_define,
threadx_thread_basic_time_slice_application_define,
threadx_thread_multiple_time_slice_application_define,
threadx_thread_simple_sleep_application_define,
threadx_thread_simple_sleep_non_clear_application_define,
threadx_thread_sleep_for_100ticks_application_define,
threadx_thread_multiple_sleep_application_define,
threadx_thread_terminate_delete_application_define,
threadx_thread_exit_callback_transition_application_define,
threadx_smp_thread_exit_callback_remote_application_define,
threadx_thread_priority_change_application_define,
threadx_thread_time_slice_change_application_define,
threadx_thread_sleep_terminate_application_define,
threadx_thread_delayed_suspension_application_define,
threadx_thread_wait_abort_application_define,
threadx_thread_wait_abort_and_isr_application_define,
threadx_thread_create_preemption_threshold_application_define,
threadx_thread_preemption_change_application_define,
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,
threadx_timer_simple_application_define,
threadx_timer_activate_deactivate_application_define,
threadx_timer_deactivate_accuracy_application_define,
threadx_timer_large_timer_accuracy_application_define,
threadx_timer_multiple_application_define,
threadx_timer_multiple_accuracy_application_define,
threadx_timer_information_application_define,
threadx_trace_basic_application_define,
#endif
TX_NULL,
};
/* Define thread prototypes. */
void test_control_thread_entry(ULONG thread_input);
void test_thread_entry(ULONG thread_input);
void test_thread_entry1(ULONG thread_input);
void test_control_return(UINT status);
void test_control_cleanup(void);
void test_exit_notify(TX_THREAD *thread_ptr, UINT type);
/* Define necessary exernal references. */
#ifdef __ghs
extern TX_MUTEX __ghLockMutex;
#endif
extern TX_TIMER *_tx_timer_created_ptr;
extern ULONG _tx_timer_created_count;
#ifndef TX_TIMER_PROCESS_IN_ISR
extern TX_THREAD _tx_timer_thread;
#endif
extern TX_THREAD *_tx_thread_created_ptr;
extern ULONG _tx_thread_created_count;
extern TX_SEMAPHORE *_tx_semaphore_created_ptr;
extern ULONG _tx_semaphore_created_count;
extern TX_QUEUE *_tx_queue_created_ptr;
extern ULONG _tx_queue_created_count;
extern TX_MUTEX *_tx_mutex_created_ptr;
extern ULONG _tx_mutex_created_count;
extern TX_EVENT_FLAGS_GROUP *_tx_event_flags_created_ptr;
extern ULONG _tx_event_flags_created_count;
extern TX_BYTE_POOL *_tx_byte_pool_created_ptr;
extern ULONG _tx_byte_pool_created_count;
extern TX_BLOCK_POOL *_tx_block_pool_created_ptr;
extern ULONG _tx_block_pool_created_count;
#ifdef EXTERNAL_EXIT
void external_exit(UINT code);
#endif
/* Define the interrupt processing dispatcher. The individual tests will set this up when they desire
asynchrouns processing for testing purposes. */
void test_interrupt_dispatch(void)
{
#ifndef TX_DISABLE_ERROR_CHECKING
/* Test calling tx_thread_relinquish from ISR to see if the error checking throws it out. */
tx_thread_relinquish();
#endif
/* Check for something to run... */
if (test_isr_dispatch)
{
(test_isr_dispatch)();
}
}
/* Define init timer entry. */
static void init_timer_entry(ULONG timer_input)
{
}
#ifndef TX_TIMER_PROCESS_IN_ISR
/* Define the deletion of the system timer thread. */
void delete_timer_thread(void)
{
_tx_thread_terminate(&_tx_timer_thread);
_tx_thread_delete(&_tx_timer_thread);
}
#endif
/* Define main entry point. */
#ifndef EXTERNAL_MAIN
void main()
{
#ifndef TX_MISRA_ENABLE
#ifndef TX_MANUAL_TEST
/* Test the pre-initialize path through _tx_initialize_kernel_enter. */
_tx_thread_system_state[0] = TX_INITIALIZE_ALMOST_DONE;
test_initialize_flag = 1;
/* Call the internal kernel enter function to exercise two paths. */
_tx_initialize_kernel_enter();
_tx_thread_system_state[0] = 0;
#ifndef TX_TIMER_PROCESS_IN_ISR
threadx_delete_timer_thread = 1;
#endif
#endif
#endif
/* Enter the ThreadX kernel. */
tx_kernel_enter();
}
#endif
/* Define what the initial system looks like. */
void tx_application_define(void *first_unused_memory)
{
UCHAR *pointer;
VOID (*temp_mutex_release)(TX_THREAD *thread_ptr);
TX_THREAD *temp_thread;
UINT old_preemption;
ULONG old_time_slice;
UINT status;
ULONG flags;
ULONG temp;
UINT i, j;
#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING)
TX_THREAD *thread_ptr;
#endif
/* Initialize the test error/success counters. */
test_control_successful_tests = 0;
test_control_failed_tests = 0;
test_control_system_errors = 0;
/* Create two equal priority threads. */
status = tx_thread_create(&test_thread4, "test thread 4", test_thread_entry1, 4,
test_thread4_stack, sizeof(test_thread4_stack), 15, 15, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread5, "test thread 5", test_thread_entry1, 5,
test_thread5_stack, sizeof(test_thread5_stack), 15, 15, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread6, "test thread 6", test_thread_entry1, 6,
test_thread6_stack, sizeof(test_thread6_stack), 16, 16, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread7, "test thread 7", test_thread_entry1, 7,
test_thread7_stack, sizeof(test_thread7_stack), 17, 17, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread8, "test thread 8", test_thread_entry1, 8,
test_thread8_stack, sizeof(test_thread8_stack), 18, 18, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread9, "test thread 9", test_thread_entry1, 9,
test_thread9_stack, sizeof(test_thread9_stack), 19, 19, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread10, "test thread 10", test_thread_entry1, 10,
test_thread10_stack, sizeof(test_thread10_stack), 20, 20, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread11, "test thread 11", test_thread_entry1, 11,
test_thread11_stack, sizeof(test_thread11_stack), 20, 20, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread12, "test thread 12", test_thread_entry1, 12,
test_thread12_stack, sizeof(test_thread12_stack), 20, 20, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread13, "test thread 13", test_thread_entry1, 13,
test_thread13_stack, sizeof(test_thread13_stack), 20, 20, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_create(&test_thread14, "test thread 14", test_thread_entry1, 14,
test_thread14_stack, sizeof(test_thread14_stack), 20, 20, TX_NO_TIME_SLICE, TX_DONT_START);
status += tx_thread_smp_core_exclude(&test_thread4, 0xD);
status += tx_thread_smp_core_exclude(&test_thread5, 0xD);
status += tx_thread_resume(&test_thread4);
status += tx_thread_suspend(&test_thread4);
status += tx_thread_resume(&test_thread4);
status += tx_thread_resume(&test_thread5);
status += tx_thread_suspend(&test_thread4);
status += tx_thread_suspend(&test_thread5);
/* Test the fringe cases in tx_thread_system_resume to make sure the behave properly. */
status += tx_thread_smp_core_exclude(&test_thread14, 0);
status += tx_thread_smp_core_exclude(&test_thread13, 0);
status += tx_thread_smp_core_exclude(&test_thread12, 0);
status += tx_thread_smp_core_exclude(&test_thread11, 0);
status += tx_thread_smp_core_exclude(&test_thread10, 0);
status += tx_thread_smp_core_exclude(&test_thread9, 0xC);
status += tx_thread_smp_core_exclude(&test_thread8, 0x3);
status += tx_thread_smp_core_exclude(&test_thread7, 0xE);
status += tx_thread_smp_core_exclude(&test_thread6, 0xD);
status += tx_thread_smp_core_exclude(&test_thread5, 0xB);
status += tx_thread_smp_core_exclude(&test_thread4, 0x7);
test_thread13.tx_thread_timer.tx_timer_internal_active_next = &test_thread13.tx_thread_timer;
test_thread13.tx_thread_timer.tx_timer_internal_list_head = (TX_TIMER_INTERNAL **) &test_thread13.tx_thread_timer;
status += tx_thread_resume(&test_thread14);
status += tx_thread_resume(&test_thread13);
status += tx_thread_resume(&test_thread12);
status += tx_thread_resume(&test_thread11);
status += tx_thread_resume(&test_thread10);
status += tx_thread_resume(&test_thread9);
status += tx_thread_resume(&test_thread8);
status += tx_thread_resume(&test_thread7);
status += tx_thread_resume(&test_thread6);
status += tx_thread_resume(&test_thread5);
status += tx_thread_resume(&test_thread4);
status += tx_thread_suspend(&test_thread14);
status += tx_thread_suspend(&test_thread13);
status += tx_thread_suspend(&test_thread12);
status += tx_thread_suspend(&test_thread11);
status += tx_thread_suspend(&test_thread10);
status += tx_thread_suspend(&test_thread9);
status += tx_thread_suspend(&test_thread8);
status += _tx_thread_preemption_change(&test_thread8, 17, &old_preemption);
status += tx_thread_resume(&test_thread8);
status += tx_thread_suspend(&test_thread8);
status += tx_thread_suspend(&test_thread7);
status += tx_thread_suspend(&test_thread6);
status += tx_thread_suspend(&test_thread5);
status += tx_thread_suspend(&test_thread4);
/* Setup a pointer to the first unused memory. */
pointer = (UCHAR *) &test_control_memory[0]; //first_unused_memory;
/* Create the test control thread. */
tx_thread_create(&test_control_thread, "test control thread", test_control_thread_entry, 0,
pointer, TEST_STACK_SIZE,
17, 15, TX_NO_TIME_SLICE, TX_AUTO_START);
pointer = pointer + TEST_STACK_SIZE;
/* Create the test thread. */
tx_thread_create(&test_thread, "test thread", test_thread_entry, 0,
pointer, TEST_STACK_SIZE,
15, 15, TX_NO_TIME_SLICE, TX_AUTO_START);
pointer = pointer + TEST_STACK_SIZE;
/* Create the second test thread. */
tx_thread_create(&test_thread1, "test thread 1", test_thread_entry1, 0,
pointer, TEST_STACK_SIZE,
15, 15, TX_NO_TIME_SLICE, TX_DONT_START);
pointer = pointer + TEST_STACK_SIZE;
/* Suspend the test thread temporarily. */
tx_thread_suspend(&test_thread);
/* Resume the test thread again to exercise the resume code fully. */
tx_thread_resume(&test_thread);
/* Timer change just to exercise the code... an error from initialization! */
test_timer.tx_timer_id = TX_TIMER_ID;
tx_timer_change(&test_timer, 1, 1);
/* Test mutex created and used in initialization. */
test_mutex_from_init = tx_mutex_create(&init_mutex, "init mutex", TX_INHERIT);
test_mutex_from_init += tx_mutex_get(&init_mutex, TX_NO_WAIT);
test_mutex_from_init += tx_mutex_get(&init_mutex, TX_NO_WAIT);
test_mutex_from_init += tx_mutex_put(&init_mutex);
test_mutex_from_init += tx_mutex_put(&init_mutex);
test_mutex_from_init = tx_mutex_create(&init_mutex_inherit, "init mutex", TX_INHERIT);
test_mutex_from_init += tx_mutex_get(&init_mutex_inherit, TX_NO_WAIT);
test_mutex_from_init += tx_mutex_get(&init_mutex_inherit, TX_NO_WAIT);
test_mutex_from_init += tx_mutex_put(&init_mutex_inherit);
test_mutex_from_init += tx_mutex_put(&init_mutex_inherit);
#ifndef TX_DISABLE_ERROR_CHECKING
/* Test timer create from initialization. */
test_block_pool_create_init = tx_block_pool_create(&init_block_pool, "init block pool", 10, init_block_pool_area, sizeof(init_block_pool_area));
/* Test byte pool create from initialization. */
test_byte_pool_create_init = tx_byte_pool_create(&init_byte_pool, "init byte pool", init_byte_pool_area, sizeof(init_byte_pool_area));
test_byte_pool_create_init += tx_byte_allocate(&init_byte_pool, (VOID **) &pointer, 20, TX_NO_WAIT);
test_byte_pool_create_init += tx_byte_release(pointer);
/* Test event flag create from initialization. */
test_event_flags_from_init = tx_event_flags_create(&init_event_flags, "init events");
/* Test queue create from initialization. */
test_queue_from_init = tx_queue_create(&init_queue, "init queue", TX_1_ULONG, init_queue_area, sizeof(init_queue_area));
/* Test semaphore create from initialization. */
test_semaphore_from_init = tx_semaphore_create(&init_semaphore, "init semaphore", 0);
/* Test timer creat from initialization. */
test_timer_create_init = tx_timer_create(&init_timer, "init timer", init_timer_entry, 0x5678,
100, 200, TX_AUTO_ACTIVATE);
/* Test timer change from initialization. */
test_timer_create_init += tx_timer_create(&init_timer_change, "init timer change", init_timer_entry, 0x5678,
100, 200, TX_NO_ACTIVATE);
test_timer_create_init += tx_timer_change(&init_timer_change, 50, 100);
/* Test calling tx_thread_relinquish to see if the error checking throws it out. */
tx_thread_relinquish();
#endif
/* Remember the free memory pointer. */
test_free_memory_ptr = &tests_memory[0]; //pointer;
/* Clear the ISR dispatch. */
test_isr_dispatch = TX_NULL;
/* Ensure that _tx_thread_time_slice can handle NULL thread, note that current thread pointer is NULL at this point. */
_tx_thread_time_slice();
/* Test to make sure _tx_thread_time_slice can handle a none-ready thread. */
init_test_thread.tx_thread_state = TX_IO_DRIVER;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_TRUE;
_tx_thread_current_ptr[0] = &init_test_thread;
_tx_thread_time_slice();
/* Test to make sure _tx_thread_time_slice can handle preemption-threshold set. */
init_test_thread.tx_thread_state = TX_READY;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_priority = 10;
init_test_thread.tx_thread_preempt_threshold = 9;
init_test_thread.tx_thread_ready_next = &init_test_thread;
init_test_thread.tx_thread_ready_previous = &init_test_thread;
_tx_thread_time_slice();
_tx_thread_current_ptr[0] = TX_NULL;
/* Test to make sure _tx_thread_shell_entry can handle a NULL mutex release function pointer. */
temp_mutex_release = _tx_thread_mutex_release;
temp_thread = _tx_thread_execute_ptr[0];
_tx_thread_mutex_release = TX_NULL;
init_test_thread.tx_thread_state = TX_READY;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_FALSE;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_entry = test_thread_entry1;
_tx_thread_current_ptr[0] = &init_test_thread;
_tx_thread_execute_ptr[0] = &init_test_thread;
_tx_thread_entry_exit_notify(&init_test_thread, test_exit_notify);
_tx_thread_shell_entry();
_tx_thread_current_ptr[0] = TX_NULL;
_tx_thread_execute_ptr[0] = temp_thread;
_tx_thread_mutex_release = temp_mutex_release; /* Recover Mutex release pointer. */
/* Test _tx_thread_system_suspend when not current, preemption is needed but disabled. */
temp_thread = _tx_thread_execute_ptr[0];
init_test_thread.tx_thread_state = TX_READY;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_FALSE;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_entry = test_thread_entry1;
_tx_thread_execute_ptr[0] = &init_test_thread;
#ifndef TX_NOT_INTERRUPTABLE
_tx_thread_preempt_disable++;
#endif
_tx_thread_system_suspend(&init_test_thread);
_tx_thread_execute_ptr[0] = temp_thread;
/* Test _tx_thread_system_resume when not current, suspending and in a COMPLETED state. */
temp_thread = _tx_thread_execute_ptr[0];
init_test_thread.tx_thread_state = TX_COMPLETED;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_TRUE;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_entry = test_thread_entry1;
#ifndef TX_NOT_INTERRUPTABLE
_tx_thread_preempt_disable++;
#endif
_tx_thread_execute_ptr[0] = &init_test_thread;
_tx_thread_system_resume(&init_test_thread);
_tx_thread_execute_ptr[0] = temp_thread;
/* Test _tx_thread_system_resume when not current, not suspending and already in a TX_READY state. */
temp_thread = _tx_thread_execute_ptr[0];
init_test_thread.tx_thread_state = TX_READY;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_FALSE;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_entry = test_thread_entry1;
#ifndef TX_NOT_INTERRUPTABLE
_tx_thread_preempt_disable++;
#endif
_tx_thread_execute_ptr[0] = &init_test_thread;
_tx_thread_system_resume(&init_test_thread);
_tx_thread_execute_ptr[0] = temp_thread;
/* Test _tx_thread_system_resume when not current, suspending and in a TERMINATED state. */
temp_thread = _tx_thread_execute_ptr[0];
init_test_thread.tx_thread_state = TX_TERMINATED;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_TRUE;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_entry = test_thread_entry1;
#ifndef TX_NOT_INTERRUPTABLE
_tx_thread_preempt_disable++;
#endif
_tx_thread_execute_ptr[0] = &init_test_thread;
_tx_thread_system_resume(&init_test_thread);
_tx_thread_execute_ptr[0] = temp_thread;
/* Test tx_thread_resume to test the saved_thread_ptr being NULL. */
temp_thread = _tx_thread_execute_ptr[0];
_tx_thread_execute_ptr[0] = TX_NULL;
tx_thread_resume(&test_thread1);
tx_thread_suspend(&test_thread1);
_tx_thread_execute_ptr[0] = temp_thread;
/* Test preemption change when the new priority is the same as the threshold. */
init_test_thread.tx_thread_state = TX_SUSPENDED;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_FALSE;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_user_priority = 10;
init_test_thread.tx_thread_user_preempt_threshold = 10;
init_test_thread.tx_thread_priority = 10;
init_test_thread.tx_thread_preempt_threshold = 10;
init_test_thread.tx_thread_entry = test_thread_entry1;
_tx_thread_preemption_change(&init_test_thread, 10, &old_preemption);
#ifndef TX_NOT_INTERRUPTABLE
/* Test semaphore cleanup with an invalid semaphore ID. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_semaphore;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_semaphore_cleanup);
cleanup_semaphore.tx_semaphore_id = 0;
cleanup_semaphore.tx_semaphore_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_semaphore_cleanup(&init_test_thread, 0);
/* Test semaphore cleanup with an invalid suspension sequence. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_semaphore;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_semaphore_cleanup);
cleanup_semaphore.tx_semaphore_id = 0;
cleanup_semaphore.tx_semaphore_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_semaphore_cleanup(&init_test_thread, 1);
/* Test semaphore cleanup with a NULL semaphore pointer. */
init_test_thread.tx_thread_suspend_control_block = TX_NULL;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_semaphore_cleanup);
cleanup_semaphore.tx_semaphore_id = TX_SEMAPHORE_ID;
cleanup_semaphore.tx_semaphore_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_semaphore_cleanup(&init_test_thread, 0);
/* Test semaphore cleanup with an valid semaphore ID but a suspension count of 0. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_semaphore;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_semaphore_cleanup);
cleanup_semaphore.tx_semaphore_id = TX_SEMAPHORE_ID;
cleanup_semaphore.tx_semaphore_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_semaphore_cleanup(&init_test_thread, 0);
/* Test queue cleanup with a NULL cleanup pointer. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_queue;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
cleanup_queue.tx_queue_id = 0;
cleanup_queue.tx_queue_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_queue_cleanup(&init_test_thread, 0);
/* Test queue cleanup with a NULL queue pointer. */
init_test_thread.tx_thread_suspend_control_block = TX_NULL;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_queue_cleanup);
cleanup_queue.tx_queue_id = 0;
cleanup_queue.tx_queue_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_queue_cleanup(&init_test_thread, 0);
/* Test queue cleanup with an invalid queue ID. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_queue;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_queue_cleanup);
cleanup_queue.tx_queue_id = 0;
cleanup_queue.tx_queue_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_queue_cleanup(&init_test_thread, 0);
/* Test queue cleanup with an invalid suspension sequence. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_queue;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_queue_cleanup);
cleanup_queue.tx_queue_id = 0;
cleanup_queue.tx_queue_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_queue_cleanup(&init_test_thread, 1);
/* Test queue cleanup with an valid queue ID but a suspension count of 0. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_queue;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_queue_cleanup);
cleanup_queue.tx_queue_id = TX_QUEUE_ID;
cleanup_queue.tx_queue_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_queue_cleanup(&init_test_thread, 0);
/* Test mutex cleanup with a NULL cleanup pointer. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_mutex;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
cleanup_mutex.tx_mutex_id = 0;
cleanup_mutex.tx_mutex_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_mutex_cleanup(&init_test_thread, 0);
/* Test mutex cleanup with a NULL mutex pointer. */
init_test_thread.tx_thread_suspend_control_block = TX_NULL;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_mutex_cleanup);
cleanup_mutex.tx_mutex_id = 0;
cleanup_mutex.tx_mutex_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_mutex_cleanup(&init_test_thread, 0);
/* Test mutex cleanup with an invalid mutex ID. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_mutex;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_mutex_cleanup);
cleanup_mutex.tx_mutex_id = 0;
cleanup_mutex.tx_mutex_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_mutex_cleanup(&init_test_thread, 0);
/* Test mutex cleanup with an invalid suspension sequence. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_mutex;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_mutex_cleanup);
cleanup_mutex.tx_mutex_id = 0;
cleanup_mutex.tx_mutex_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_mutex_cleanup(&init_test_thread, 1);
/* Test mutex cleanup with an valid mutex ID but a suspension count of 0. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_mutex;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_mutex_cleanup);
cleanup_mutex.tx_mutex_id = TX_MUTEX_ID;
cleanup_mutex.tx_mutex_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_mutex_cleanup(&init_test_thread, 0);
/* Test event flag cleanup with a NULL cleanup pointer. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_event_flags;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
cleanup_event_flags.tx_event_flags_group_id = 0;
cleanup_event_flags.tx_event_flags_group_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_event_flags_cleanup(&init_test_thread, 0);
/* Test event flag cleanup with a NULL event flag pointer. */
init_test_thread.tx_thread_suspend_control_block = TX_NULL;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_event_flags_cleanup);
cleanup_event_flags.tx_event_flags_group_id = 0;
cleanup_event_flags.tx_event_flags_group_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_event_flags_cleanup(&init_test_thread, 0);
/* Test event flag cleanup with an invalid ID. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_event_flags;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_event_flags_cleanup);
cleanup_event_flags.tx_event_flags_group_id = 0;
cleanup_event_flags.tx_event_flags_group_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_event_flags_cleanup(&init_test_thread, 0);
/* Test event flag cleanup with an invalid suspension sequence. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_event_flags;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_event_flags_cleanup);
cleanup_event_flags.tx_event_flags_group_id = 0;
cleanup_event_flags.tx_event_flags_group_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_event_flags_cleanup(&init_test_thread, 1);
/* Test event flag cleanup with an valid ID but a suspension count of 0. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_event_flags;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_event_flags_cleanup);
cleanup_event_flags.tx_event_flags_group_id = TX_EVENT_FLAGS_ID;
cleanup_event_flags.tx_event_flags_group_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_event_flags_cleanup(&init_test_thread, 0);
/* Test block pool cleanup with a NULL cleanup pointer. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_block_pool;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
cleanup_block_pool.tx_block_pool_id = 0;
cleanup_block_pool.tx_block_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_block_pool_cleanup(&init_test_thread, 0);
/* Test block pool cleanup with a NULL block pool pointer. */
init_test_thread.tx_thread_suspend_control_block = TX_NULL;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_block_pool_cleanup);
cleanup_block_pool.tx_block_pool_id = 0;
cleanup_block_pool.tx_block_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_block_pool_cleanup(&init_test_thread, 0);
/* Test block pool cleanup with an invalid ID. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_block_pool;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_block_pool_cleanup);
cleanup_block_pool.tx_block_pool_id = 0;
cleanup_block_pool.tx_block_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_block_pool_cleanup(&init_test_thread, 0);
/* Test block pool cleanup with an invalid suspension sequence. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_block_pool;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_block_pool_cleanup);
cleanup_block_pool.tx_block_pool_id = 0;
cleanup_block_pool.tx_block_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_block_pool_cleanup(&init_test_thread, 1);
/* Test block pool cleanup with an valid ID but a suspension count of 0. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_block_pool;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_block_pool_cleanup);
cleanup_block_pool.tx_block_pool_id = TX_BLOCK_POOL_ID;
cleanup_block_pool.tx_block_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_block_pool_cleanup(&init_test_thread, 0);
/* Test byte pool cleanup with a NULL cleanup pointer. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_byte_pool;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
cleanup_byte_pool.tx_byte_pool_id = 0;
cleanup_byte_pool.tx_byte_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_byte_pool_cleanup(&init_test_thread, 0);
/* Test byte pool cleanup with a NULL byte pool pointer. */
init_test_thread.tx_thread_suspend_control_block = TX_NULL;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_byte_pool_cleanup);
cleanup_byte_pool.tx_byte_pool_id = 0;
cleanup_byte_pool.tx_byte_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_byte_pool_cleanup(&init_test_thread, 0);
/* Test byte pool cleanup with an invalid ID. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_byte_pool;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_byte_pool_cleanup);
cleanup_byte_pool.tx_byte_pool_id = 0;
cleanup_byte_pool.tx_byte_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_byte_pool_cleanup(&init_test_thread, 0);
/* Test byte pool cleanup with an invalid suspension sequence. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_byte_pool;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_byte_pool_cleanup);
cleanup_byte_pool.tx_byte_pool_id = 0;
cleanup_byte_pool.tx_byte_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_byte_pool_cleanup(&init_test_thread, 1);
/* Test byte pool cleanup with an valid ID but a suspension count of 0. */
init_test_thread.tx_thread_suspend_control_block = (VOID *) &cleanup_byte_pool;
init_test_thread.tx_thread_suspend_cleanup = &(_tx_byte_pool_cleanup);
cleanup_byte_pool.tx_byte_pool_id = TX_BYTE_POOL_ID;
cleanup_byte_pool.tx_byte_pool_suspended_count = 0;
init_test_thread.tx_thread_suspension_sequence = 0;
_tx_byte_pool_cleanup(&init_test_thread, 0);
#endif
#ifndef TX_ENABLE_EVENT_TRACE
/* Call ISR trace events when trace is not enabled. */
/* Call trace ISR enter event insert. */
tx_trace_isr_enter_insert(1);
/* Call trace ISR exit event insert. */
tx_trace_isr_exit_insert(1);
/* Call trace ISR enter event insert. */
tx_trace_isr_enter_insert(0);
/* Call trace ISR exit event insert. */
tx_trace_isr_exit_insert(0);
#endif
/* Test the list start path in timer info get and timer deactivate. */
test_timer.tx_timer_id = 0;
_tx_timer_create(&test_timer, "init timer", init_timer_entry, 0x5678, 100, 200, TX_AUTO_ACTIVATE);
_timer_list_start_backup = _tx_timer_list_start;
_tx_timer_list_start = _tx_timer_list_end;
_tx_timer_info_get(&test_timer, TX_NULL, TX_NULL, TX_NULL, TX_NULL, TX_NULL);
_tx_timer_deactivate(&test_timer);
_tx_timer_list_start = _timer_list_start_backup;
_tx_timer_deactivate(&test_timer);
_tx_timer_delete(&test_timer);
/* Test the stack analyze function with a dummy thread. */
/* Clear the test stack analyze flag. */
test_stack_analyze_flag = 0;
/* Make a fake thread with a fake stack. */
test_thread2.tx_thread_id = TX_THREAD_ID;
#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING)
/* Set the thread pointer. */
thread_ptr = &(test_thread2);
/* Initialize the stack fill value. */
thread_ptr -> tx_thread_stack_fill_value = (0xFEFEFEFEUL);
#endif
for (i = 0; i < (sizeof(test_thread2_stack)/sizeof(ULONG)); i++)
{
/* Set the fake thread stack to the fill pattern. */
test_thread2_stack[i] = TX_STACK_FILL;
}
/* Setup index to last point. */
i = (sizeof(test_thread2_stack)/sizeof(ULONG)) - 1;
/* Setup the stack start and end pointers. */
test_thread2.tx_thread_stack_start = &(test_thread2_stack[0]);
test_thread2.tx_thread_stack_end = &(test_thread2_stack[i]);
test_thread2.tx_thread_stack_size = sizeof(test_thread2_stack);
test_thread2.tx_thread_stack_highest_ptr = test_thread2.tx_thread_stack_end;
test_thread2.tx_thread_stack_ptr = test_thread2.tx_thread_stack_start;
/* Fill 20 words of stack. */
for (j = 0; j < 20; j++)
{
/* Fill the stack with 0s. */
test_thread2_stack[i--] = 0;
}
/* Call the analyze stack function. */
_tx_thread_stack_analyze(&test_thread2);
/* Call it again for no change coverage. */
_tx_thread_stack_analyze(&test_thread2);
/* Fill 99 words of stack. */
for (j = 0; j < 99; j++)
{
/* Fill the stack with 1s. */
test_thread2_stack[i--] = 1;
}
/* Call the analyze stack function. */
_tx_thread_stack_analyze(&test_thread2);
/* Call it again for no change coverage. */
_tx_thread_stack_analyze(&test_thread2);
#ifndef TX_MANUAL_TEST
/* Now set the flag to 1 to cause the thread ID to be cleared. */
test_stack_analyze_flag = 1;
/* Call stack analyze with an ID that is cleared in the middle. */
_tx_thread_stack_analyze(&test_thread2);
/* Restore the ID. */
test_thread2.tx_thread_id = TX_THREAD_ID;
/* Now set the flag to 2 to cause the stack ptr to be equal to the start of the stack. */
test_stack_analyze_flag = 2;
/* Call stack analyze with an ID that is cleared in the middle. */
_tx_thread_stack_analyze(&test_thread2);
test_thread2.tx_thread_stack_highest_ptr = test_thread2.tx_thread_stack_end;
/* Now set the flag to 3 to cause the stack pointer to not have the fill pattern. */
test_stack_analyze_flag = 3;
/* Call stack analyze with an ID that is cleared in the middle. */
_tx_thread_stack_analyze(&test_thread2);
#endif
/* Test error condition on _tx_queue_flush. */
test_queue.tx_queue_enqueued = 1;
test_queue.tx_queue_suspended_count = 1;
test_queue.tx_queue_suspension_list = TX_NULL;
/* Call _tx_queue_flush to test the thread NULL check. */
_tx_queue_flush(&test_queue);
#ifndef TX_NOT_INTERRUPTABLE
/* Make sure the suspension cancelled path is exercised in tx_thread_system_resume... This normally happens,
however, sometimes on less real-time platforms the timing is not guaranteed. */
/* Increment the preempt disable flag. */
_tx_thread_preempt_disable++;
/* Build a thread control block with fake info. */
test_thread3.tx_thread_suspending = TX_TRUE;
test_thread3.tx_thread_state = TX_SUSPENDED;
test_thread3.tx_thread_delayed_suspend = TX_FALSE;
test_thread3.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
test_thread3.tx_thread_stack_start = (VOID *) &test_thread2_stack[0];
test_thread3.tx_thread_stack_end = (VOID *) &test_thread2_stack[255];
test_thread3.tx_thread_stack_ptr = (VOID *) &test_thread2_stack[255];
/* Call the system resume function directly to insure the suspension cancel works properly. */
_tx_thread_system_resume(&test_thread3);
#endif
/* Test preempt-disable safeguard. */
/* Increment the preempt disable flag. */
_tx_thread_preempt_disable++;
/* Test block pool suspenson safeguard. */
fake_block_pool.tx_block_pool_available = 0;
status = _tx_block_allocate(&fake_block_pool, (VOID **) &pointer, TX_WAIT_FOREVER);
if (status != TX_NO_MEMORY)
test_control_system_errors++;
/* Test byte pool suspension safeguard. */
fake_byte_pool.tx_byte_pool_fragments = 2;
fake_byte_pool.tx_byte_pool_available = 0;
status = _tx_byte_allocate(&fake_byte_pool, (VOID **) &pointer, 1000, TX_WAIT_FOREVER);
if (status != TX_NO_MEMORY)
test_control_system_errors++;
/* Test event flags suspension safeguard. */
fake_event_flags.tx_event_flags_group_current = 0;
status = _tx_event_flags_get(&fake_event_flags, 1, TX_AND, &flags, TX_WAIT_FOREVER);
if (status != TX_NO_EVENTS)
test_control_system_errors++;
/* Test mutex suspension safeguard. */
fake_mutex.tx_mutex_ownership_count = 1;
fake_mutex.tx_mutex_inherit = 0;
fake_mutex.tx_mutex_owner = &init_test_thread;
status = _tx_mutex_get(&fake_mutex, TX_WAIT_FOREVER);
if (status != TX_NOT_AVAILABLE)
test_control_system_errors++;
/* Test queue front send suspension safeguard. */
fake_queue.tx_queue_available_storage = 0;
status = _tx_queue_front_send(&fake_queue, (VOID *) pointer, TX_WAIT_FOREVER);
if (status != TX_QUEUE_FULL)
test_control_system_errors++;
/* Test queue receive suspension safeguard. */
fake_queue.tx_queue_enqueued = 0;
status = _tx_queue_receive(&fake_queue, (VOID **) &pointer, TX_WAIT_FOREVER);
if (status != TX_QUEUE_EMPTY)
test_control_system_errors++;
/* Test queue send suspension safeguard. */
fake_queue.tx_queue_available_storage = 0;
status = _tx_queue_send(&fake_queue, (VOID *) pointer, TX_WAIT_FOREVER);
if (status != TX_QUEUE_FULL)
test_control_system_errors++;
/* Test semaphore suspension safeguard. */
fake_semaphore.tx_semaphore_count = 0;
status = _tx_semaphore_get(&fake_semaphore, TX_WAIT_FOREVER);
if (status != TX_NO_INSTANCE)
test_control_system_errors++;
/* Test thread sleep suspension safeguard. */
_tx_thread_current_ptr[0] = &init_test_thread;
temp = _tx_thread_system_state[0];
_tx_thread_system_state[0] = 0;
status = _tx_thread_sleep(10);
if (status != TX_CALLER_ERROR)
test_control_system_errors++;
/* Test thread suspend suspension safeguard. */
init_test_thread.tx_thread_state = TX_READY;
status = _tx_thread_suspend(&init_test_thread);
if (status != TX_SUSPEND_ERROR)
test_control_system_errors++;
_tx_thread_system_state[0] = temp;
_tx_thread_current_ptr[0] = TX_NULL;
/* Pickup the current core exclusion. */
tx_thread_smp_core_exclude_get(&test_thread, &temp);
/* Move the test thread form core 0. */
tx_thread_smp_core_exclude(&test_thread, 0x1);
/* Restore the core exclusion for the test thread. */
tx_thread_smp_core_exclude(&test_thread, temp);
/* Test some code paths in the tx_thread_smp_utilities file. */
temp_thread = _tx_thread_current_ptr[3];
_tx_thread_current_ptr[3] = &init_test_thread;
_tx_thread_smp_core_interrupt(&test_thread, 0, 3);
_tx_thread_current_ptr[3] = temp_thread;
temp_thread = _tx_thread_execute_ptr[0];
_tx_thread_execute_ptr[0] = TX_NULL;
_tx_thread_smp_lowest_priority_get();
_tx_thread_execute_ptr[0] = temp_thread;
_tx_thread_smp_schedule_list[0] = TX_NULL;
_tx_thread_smp_remap_solution_find(&init_test_thread, 1, 1, 1);
/* Test a corner case in time-slice change. */
init_test_thread.tx_thread_smp_core_mapped = TX_THREAD_SMP_MAX_CORES;
_tx_thread_time_slice_change(&init_test_thread, 3, &old_time_slice);
/* Test time-slice paths. */
_tx_timer_time_slice[0] = 0;
_tx_timer_time_slice[1] = 1;
_tx_timer_time_slice[2] = 0;
_tx_timer_time_slice[3] = 0;
_tx_thread_time_slice();
_tx_timer_time_slice[0] = 0;
_tx_timer_time_slice[1] = 0;
_tx_timer_time_slice[2] = 1;
_tx_timer_time_slice[3] = 0;
_tx_thread_time_slice();
_tx_timer_time_slice[0] = 0;
_tx_timer_time_slice[1] = 0;
_tx_timer_time_slice[2] = 0;
_tx_timer_time_slice[3] = 1;
_tx_thread_time_slice();
_tx_timer_time_slice[0] = 1;
_tx_timer_time_slice[1] = 0;
_tx_timer_time_slice[2] = 0;
_tx_timer_time_slice[3] = 0;
temp_thread -> tx_thread_state = TX_COMPLETED;
_tx_thread_current_ptr[0] = temp_thread;
_tx_thread_time_slice();
temp_thread -> tx_thread_state = TX_READY;
_tx_thread_current_ptr[0] = TX_NULL;
_tx_timer_time_slice[0] = 1;
_tx_timer_time_slice[1] = 0;
_tx_timer_time_slice[2] = 0;
_tx_timer_time_slice[3] = 0;
temp_thread -> tx_thread_time_slice = 0;
_tx_thread_current_ptr[0] = temp_thread;
_tx_thread_time_slice();
_tx_thread_current_ptr[0] = TX_NULL;
/* Decrement the preempt disable flag. */
_tx_thread_preempt_disable--;
}
/* ---------------------------------------------------------------------------
Teardown diagnostics.
When this suite hangs, CI reports nothing but a ctest timeout. The last
flush of stdout happens on entry to test_control_return(), so a test that
wedges after that point leaves the rest of its output sitting in stdout's
block buffer, which is discarded when ctest kills the process. The log
stops at the test's own result line and says nothing about where the
process stopped.
Two things close that gap. A stage variable records how far teardown has
progressed, and a watchdog thread, armed on entry to test_control_return()
and stood down once the next test is about to be dispatched, reports the
last stage reached along with the scheduler state, then exits non-zero.
The watchdog covers teardown only, never the test body. Several tests here
wait on a probabilistic interrupt window and have no bounded runtime:
threadx_thread_wait_abort_and_isr_test has been observed taking anywhere
from 0.34 to 439 seconds while passing. Teardown is a fixed amount of work
that takes milliseconds, so bounding it cannot turn a slow pass into a
failure.
The report is written with write() rather than printf() deliberately. A
wedged thread may hold the stdio lock, and a watchdog that blocked on that
lock would reproduce the silent timeout it exists to replace.
TX_TEST_TEARDOWN_TIMEOUT overrides the bound, in seconds; zero disables the
watchdog. TX_TEST_TEARDOWN_TRACE echoes every stage as it is reached and
puts stdout in line-buffered mode so the surrounding output survives too.
--------------------------------------------------------------------------- */
#define TEST_TEARDOWN_TIMEOUT_DEFAULT 60 /* Seconds allowed for teardown. */
#define TEST_TEARDOWN_POLL_MS 250 /* Watchdog poll interval. */
#define TEST_TEARDOWN_WALK_LIMIT 100000 /* Cap on the unbounded walk in cleanup. */
static const char * volatile test_teardown_stage_name = "test body";
static volatile UINT test_teardown_armed = TX_FALSE;
static UINT test_teardown_timeout = TEST_TEARDOWN_TIMEOUT_DEFAULT;
static UINT test_teardown_trace = TX_FALSE;
/* Record how far teardown has progressed, and echo it when tracing is on. */
static void test_teardown_stage(const char *stage)
{
test_teardown_stage_name = stage;
if (test_teardown_trace != TX_FALSE)
{
(void) write(2, "[teardown] ", 11);
(void) write(2, stage, strlen(stage));
(void) write(2, "\n", 1);
}
}
/* Name a thread for the report, without dereferencing a null pointer. */
static const char *test_teardown_thread_name(TX_THREAD *thread_ptr)
{
if (thread_ptr == TX_NULL)
return "(none)";
if (thread_ptr -> tx_thread_name == TX_NULL)
return "(unnamed)";
return thread_ptr -> tx_thread_name;
}
/* Write out the state that matters for a teardown hang. */
static void test_teardown_report(const char *reason)
{
char buffer[512];
int length;
UINT core;
UINT count;
TX_THREAD *thread_ptr;
length = snprintf(buffer, sizeof(buffer),
"\n**** TEARDOWN WATCHDOG: %s ****\n"
" last stage reached: %s\n"
" _tx_thread_preempt_disable: %u\n"
" _tx_thread_created_count: %lu\n",
reason, test_teardown_stage_name,
(UINT) _tx_thread_preempt_disable,
(ULONG) _tx_thread_created_count);
if (length > 0)
(void) write(2, buffer, (size_t) length);
for (core = 0; core < ((UINT) TX_THREAD_SMP_MAX_CORES); core++)
{
length = snprintf(buffer, sizeof(buffer),
" core %u: system_state=%lu time_slice=%lu current=%s execute=%s\n",
core, (ULONG) _tx_thread_system_state[core],
(ULONG) _tx_timer_time_slice[core],
test_teardown_thread_name(_tx_thread_current_ptr[core]),
test_teardown_thread_name(_tx_thread_execute_ptr[core]));
if (length > 0)
(void) write(2, buffer, (size_t) length);
}
/* Walk the created list, stopping at the head, and after a fixed number of
entries, so a corrupt list cannot trap us in here. */
thread_ptr = _tx_thread_created_ptr;
for (count = 0; (thread_ptr != TX_NULL) && (count < ((UINT) 64)); count++)
{
length = snprintf(buffer, sizeof(buffer),
" thread %-24s state=%u priority=%u threshold=%u inherit=%u "
"core_mapped=%u core_control=%lu deferred_preempt=%u suspension=%u\n",
test_teardown_thread_name(thread_ptr),
thread_ptr -> tx_thread_state,
thread_ptr -> tx_thread_priority,
thread_ptr -> tx_thread_preempt_threshold,
thread_ptr -> tx_thread_inherit_priority,
thread_ptr -> tx_thread_smp_core_mapped,
(ULONG) thread_ptr -> tx_thread_smp_core_control,
thread_ptr -> tx_thread_linux_deferred_preempt,
thread_ptr -> tx_thread_linux_suspension_type);
if (length > 0)
(void) write(2, buffer, (size_t) length);
thread_ptr = thread_ptr -> tx_thread_created_next;
if (thread_ptr == _tx_thread_created_ptr)
break;
}
}
/* Report and leave. Used by the watchdog and by the bounded walk in cleanup. */
static void test_teardown_abort(const char *reason)
{
test_teardown_report(reason);
_exit(99);
}
/* Watch an armed teardown, and only an armed one. */
static void *test_teardown_watchdog(void *input)
{
sigset_t mask;
struct timespec interval;
ULONG waited_ms;
ULONG limit_ms;
/* The port drives thread suspend and resume with SIGUSR1 and SIGUSR2.
Block both here so this thread never takes one. */
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
pthread_sigmask(SIG_BLOCK, &mask, TX_NULL);
limit_ms = ((ULONG) test_teardown_timeout) * ((ULONG) 1000);
waited_ms = ((ULONG) 0);
while (1)
{
interval.tv_sec = 0;
interval.tv_nsec = ((long) TEST_TEARDOWN_POLL_MS) * 1000000L;
while (nanosleep(&interval, &interval) != 0)
{
if (errno != EINTR)
break;
}
/* Nothing to watch while teardown is stood down. */
if (test_teardown_armed == TX_FALSE)
{
waited_ms = ((ULONG) 0);
continue;
}
waited_ms = waited_ms + ((ULONG) TEST_TEARDOWN_POLL_MS);
if (waited_ms >= limit_ms)
test_teardown_abort("teardown did not complete");
}
return input;
}
/* Start the watchdog, stood down. Called before any test runs, so that no
thread is created inside the window under investigation. */
static void test_teardown_watchdog_start(void)
{
pthread_t watchdog_id;
char *value;
value = getenv("TX_TEST_TEARDOWN_TIMEOUT");
if (value != TX_NULL)
test_teardown_timeout = (UINT) atoi(value);
if (getenv("TX_TEST_TEARDOWN_TRACE") != TX_NULL)
{
/* Tracing wants the stages in the log next to the output around them,
which block buffering would otherwise discard on a kill. */
test_teardown_trace = TX_TRUE;
setvbuf(stdout, TX_NULL, _IOLBF, 0);
}
/* A timeout of zero turns the watchdog off. */
if (test_teardown_timeout == ((UINT) 0))
return;
if (pthread_create(&watchdog_id, TX_NULL, test_teardown_watchdog, TX_NULL) == 0)
pthread_detach(watchdog_id);
}
static void test_teardown_arm(const char *stage)
{
test_teardown_stage(stage);
test_teardown_armed = TX_TRUE;
}
static void test_teardown_stand_down(void)
{
test_teardown_armed = TX_FALSE;
test_teardown_stage("test body");
}
/* Define the test control thread. This thread is responsible for dispatching all of the
tests in the ThreadX test suite. */
void test_control_thread_entry(ULONG thread_input)
{
UINT i;
/* Raise the priority of the control thread to 0. */
tx_thread_priority_change(&test_control_thread, 0, &i);
/* Bring up the teardown watchdog, stood down for now. */
test_teardown_watchdog_start();
#ifdef CTEST
test_control_cleanup();
#endif
/* Print out banner. */
printf("********************** ThreadX Validation/Regression Test Suite *********************************\n\n");
/* Print version id. */
printf("Version: %s\n\n", _tx_version_id);
/* Print out the tests... */
printf("Running validation/regression test:\n\n");
/* Loop to process all tests... */
i = 0;
while (test_control_tests[i].test_entry != TX_NULL)
{
/* Teardown of the previous test is done. The watchdog covers teardown
only, so stand it down before dispatching the next test. */
test_teardown_stand_down();
/* Clear the ISR dispatch. */
test_isr_dispatch = TX_NULL;
#ifdef TX_WIN32_PROFILE_ENABLE
_tx_win32_profile_reset();
#endif
/* Dispatch the test. */
(test_control_tests[i++].test_entry)(test_free_memory_ptr);
/* Clear the ISR dispatch. */
test_isr_dispatch = TX_NULL;
/* Suspend control test to allow test to run. */
tx_thread_suspend(&test_control_thread);
/* Test finished, cleanup in preparation for the next test. */
test_teardown_stage("control thread resumed after test");
test_control_cleanup();
}
/* Finished with all tests, print results and return! */
test_teardown_stage("all tests dispatched, printing summary");
printf("**** Testing Complete ****\n");
printf("**** Test Summary: Tests Passed: %lu Tests Failed: %lu System Errors: %lu\n", test_control_successful_tests, test_control_failed_tests, test_control_system_errors);
test_teardown_stage("calling exit()");
#ifndef EXTERNAL_EXIT
exit(test_control_failed_tests + test_control_system_errors);
#else
external_exit(test_control_failed_tests + test_control_system_errors);
#endif
}
void test_control_return(UINT status)
{
TX_INTERRUPT_SAVE_AREA
UINT old_posture = TX_INT_ENABLE;
fflush(stdout);
/* This is the last flush before the test either finishes or wedges, so
everything from here on is invisible in the log. Arm the watchdog. */
test_teardown_arm("test_control_return entered");
/* Save the status in a global. */
test_control_return_status = status;
/* Ensure interrupts are enabled. */
old_posture = tx_interrupt_control(TX_INT_ENABLE);
/* Determine if it was successful or not. */
if (status)
test_control_failed_tests++;
else
test_control_successful_tests++;
/* Now check for system errors. */
/* Get protection for examination of preempt disable and system state variables. */
TX_DISABLE
/* Is preempt disable flag set? */
if (_tx_thread_preempt_disable)
{
/* System error - preempt disable should never be set inside of a thread! */
printf(" ***** SYSTEM ERROR ***** _tx_thread_preempt_disable is non-zero!\n");
test_control_system_errors++;
}
/* Is system state set? */
if (_tx_thread_system_state[0])
{
/* System error - system state should never be set inside of a thread! */
printf(" ***** SYSTEM ERROR ***** _tx_thread_system_state is non-zero!\n");
test_control_system_errors++;
}
/* Release protection. */
TX_RESTORE
/* Are interrupts disabled? */
if (old_posture == TX_INT_DISABLE)
{
/* System error - interrupts should always be enabled in our test threads! */
printf(" ***** SYSTEM ERROR ***** test returned with interrupts disabled!\n");
test_control_system_errors++;
}
#ifdef TX_WIN32_PROFILE_ENABLE
_tx_win32_profile_report("smp_test");
#endif
/* Resume the control thread to fully exit the test. */
test_teardown_stage("test_control_return: resuming control thread");
tx_thread_resume(&test_control_thread);
test_teardown_stage("test_control_return: control thread resume returned");
}
void test_control_cleanup(void)
{
TX_MUTEX *mutex_ptr;
TX_THREAD *thread_ptr;
UINT walk_guard;
test_teardown_stage("cleanup: deleting threads");
/* Delete all threads, except for timer thread, and test control thread.
This ensures application-owned objects are no longer referenced before
the object cleanup loops below attempt to delete them. */
while (_tx_thread_created_ptr)
{
/* Setup working pointer. */
thread_ptr = _tx_thread_created_ptr;
#ifdef TX_TIMER_PROCESS_IN_ISR
/* Determine if there are more threads to delete. */
if (_tx_thread_created_count == 1)
break;
/* Determine if this thread is the test control thread. */
if (thread_ptr == &test_control_thread)
{
/* Move to the next thread pointer. */
thread_ptr = thread_ptr -> tx_thread_created_next;
}
#else
/* Determine if there are more threads to delete. */
if (_tx_thread_created_count == 2)
break;
/* Move to the thread not protected. */
walk_guard = ((UINT) 0);
while ((thread_ptr == &_tx_timer_thread) || (thread_ptr == &test_control_thread))
{
/* This walk has no natural bound. Should the created count and the
created list ever disagree, it spins here for good, which is one
of the shapes the CI timeout could be taking. Say so and stop
instead of hanging. */
walk_guard = walk_guard + ((UINT) 1);
if (walk_guard > ((UINT) TEST_TEARDOWN_WALK_LIMIT))
test_teardown_abort("cleanup thread walk did not terminate");
/* Yes, move to the next thread. */
thread_ptr = thread_ptr -> tx_thread_created_next;
}
#endif
/* First terminate the thread to ensure it is ready for deletion. */
tx_thread_terminate(thread_ptr);
/* Delete the thread. */
tx_thread_delete(thread_ptr);
}
test_teardown_stage("cleanup: deleting queues");
/* Delete all queues. */
while(_tx_queue_created_ptr)
{
/* Delete queue. */
tx_queue_delete(_tx_queue_created_ptr);
}
test_teardown_stage("cleanup: deleting semaphores");
/* Delete all semaphores. */
while(_tx_semaphore_created_ptr)
{
/* Delete semaphore. */
tx_semaphore_delete(_tx_semaphore_created_ptr);
}
test_teardown_stage("cleanup: deleting event flag groups");
/* Delete all event flag groups. */
while(_tx_event_flags_created_ptr)
{
/* Delete event flag group. */
tx_event_flags_delete(_tx_event_flags_created_ptr);
}
test_teardown_stage("cleanup: deleting byte pools");
/* Delete all byte pools. */
while(_tx_byte_pool_created_ptr)
{
/* Delete byte pool. */
tx_byte_pool_delete(_tx_byte_pool_created_ptr);
}
test_teardown_stage("cleanup: deleting block pools");
/* Delete all block pools. */
while(_tx_block_pool_created_ptr)
{
/* Delete block pool. */
tx_block_pool_delete(_tx_block_pool_created_ptr);
}
test_teardown_stage("cleanup: deleting timers");
/* Delete all timers. */
while(_tx_timer_created_ptr)
{
/* Deactivate timer. */
tx_timer_deactivate(_tx_timer_created_ptr);
/* Delete timer. */
tx_timer_delete(_tx_timer_created_ptr);
}
test_teardown_stage("cleanup: deleting mutexes");
/* Delete all mutexes (except for system mutex). */
while(_tx_mutex_created_ptr)
{
/* Setup working mutex pointer. */
mutex_ptr = _tx_mutex_created_ptr;
#ifdef __ghs
/* Determine if the mutex is the GHS system mutex. If so, don't delete! */
if (mutex_ptr == &__ghLockMutex)
{
/* Move to next mutex. */
mutex_ptr = mutex_ptr -> tx_mutex_created_next;
}
/* Determine if there are no more mutexes to delete. */
if (_tx_mutex_created_count == 1)
break;
#endif
/* Delete mutex. */
tx_mutex_delete(mutex_ptr);
}
test_teardown_stage("cleanup: complete");
/* At this point, only the test control thread and the system timer thread and/or mutex should still be
in the system. */
}
void test_thread_entry(ULONG thread_input)
{
/* Resume the next test thread. */
tx_thread_resume(&test_thread1);
/* Suspend this thread but with preemption disabled, so we will actually return. */
_tx_thread_preempt_disable++;
tx_thread_suspend(&test_thread);
/* Now perform a fake thread resume to cause preemption and exercise the path in _tx_thread_system_resume that returns to the scheduler. */
init_test_thread.tx_thread_state = TX_TERMINATED;
init_test_thread.tx_thread_suspend_cleanup = TX_NULL;
init_test_thread.tx_thread_new_time_slice = 0;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_suspending = TX_TRUE;
init_test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL;
init_test_thread.tx_thread_entry = test_thread_entry1;
_tx_thread_system_resume(&init_test_thread);
#ifdef TX_NOT_INTERRUPTABLE
_tx_thread_preempt_disable--;
#endif
}
void test_thread_entry1(ULONG thread_input)
{
/* Do nothing, just return! */
}
void test_exit_notify(TX_THREAD *thread_ptr, UINT type)
{
/* Clear the suspending flag to short-circuit the suspension. */
thread_ptr -> tx_thread_suspending = TX_FALSE;
}