diff --git a/CMakeLists.txt b/CMakeLists.txt index e988d3a60..c02a178b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,5 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) -# Set up the project -project(threadx - LANGUAGES C ASM -) - if(NOT DEFINED THREADX_ARCH) message(FATAL_ERROR "Error: THREADX_ARCH not defined") endif() @@ -12,6 +7,23 @@ if(NOT DEFINED THREADX_TOOLCHAIN) message(FATAL_ERROR "Error: THREADX_TOOLCHAIN not defined") endif() +# The Windows simulation ports build cleanly without executable try-compiles. +if((THREADX_ARCH STREQUAL "win32") OR (THREADX_ARCH STREQUAL "win64")) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() + +# Set up the project. The Windows simulation ports do not use assembly and +# avoiding ASM language enablement keeps MSVC configuration on the CLI path +# deterministic. +if((THREADX_ARCH STREQUAL "win32") OR (THREADX_ARCH STREQUAL "win64")) + project(threadx + LANGUAGES C + ) +else() + project(threadx + LANGUAGES C ASM + ) + option(THREADX_SMP "Build ThreadX SMP version" OFF) if(THREADX_SMP) @@ -87,4 +99,4 @@ set(CPACK_SOURCE_IGNORE_FILES ".*~$" ) set(CPACK_VERBATIM_VARIABLES YES) -include(CPack) \ No newline at end of file +include(CPack) diff --git a/cmake/win32.cmake b/cmake/win32.cmake index 974107ad8..fb9d5086a 100644 --- a/cmake/win32.cmake +++ b/cmake/win32.cmake @@ -1,15 +1,9 @@ set(CMAKE_SYSTEM_NAME Windows) -set(CMAKE_SYSTEM_PROCESSOR x86_64) +set(CMAKE_SYSTEM_PROCESSOR x86) set(THREADX_ARCH "win32") set(THREADX_TOOLCHAIN "vs_2019") -set(WIN32_FLAGS "") - -set(CMAKE_C_FLAGS "${WIN32_FLAGS} " CACHE INTERNAL "c compiler flags") -set(CMAKE_CXX_FLAGS "${WIN32_FLAGS} -fno-rtti -fno-exceptions" CACHE INTERNAL "cxx compiler flags") -set(CMAKE_ASM_FLAGS "${WIN32_FLAGS} -x assembler-with-cpp" CACHE INTERNAL "asm compiler flags") -set(CMAKE_EXE_LINKER_FLAGS "${WIN32_FLAGS} ${LD_FLAGS}" CACHE INTERNAL "exe link flags") - -# this makes the test compiles use static library option so that we don't need to pre-set linker flags and scripts +# This makes the test compiles use the static library option so that the +# compiler environment can be discovered from the active MSVC shell. set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) diff --git a/cmake/win64.cmake b/cmake/win64.cmake new file mode 100644 index 000000000..5ba9988c5 --- /dev/null +++ b/cmake/win64.cmake @@ -0,0 +1,9 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR AMD64) + +set(THREADX_ARCH "win64") +set(THREADX_TOOLCHAIN "vs_2022") + +# This makes the test compiles use the static library option so that the +# compiler environment can be discovered from the active MSVC shell. +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) diff --git a/common/src/tx_initialize_high_level.c b/common/src/tx_initialize_high_level.c index 77bc3ed78..6de5eed52 100644 --- a/common/src/tx_initialize_high_level.c +++ b/common/src/tx_initialize_high_level.c @@ -111,17 +111,22 @@ VOID _tx_initialize_high_level(VOID) /* Initialize the event log, if enabled. */ TX_EL_INITIALIZE + /* Call the thread control initialization function. */ _tx_thread_initialize(); + #ifndef TX_NO_TIMER + /* Call the timer control initialization function. */ _tx_timer_initialize(); + #endif #ifndef TX_DISABLE_REDUNDANT_CLEARING + /* Call the semaphore initialization function. */ _tx_semaphore_initialize(); @@ -139,6 +144,6 @@ VOID _tx_initialize_high_level(VOID) /* Call the mutex initialization function. */ _tx_mutex_initialize(); + #endif } - diff --git a/common/src/tx_initialize_kernel_enter.c b/common/src/tx_initialize_kernel_enter.c index 20dc3017a..bbb073326 100644 --- a/common/src/tx_initialize_kernel_enter.c +++ b/common/src/tx_initialize_kernel_enter.c @@ -101,15 +101,18 @@ VOID _tx_initialize_kernel_enter(VOID) /* Call any port specific preprocessing. */ TX_PORT_SPECIFIC_PRE_INITIALIZATION + /* Invoke the low-level initialization to handle all processor specific initialization issues. */ _tx_initialize_low_level(); + /* Invoke the high-level initialization to exercise all of the ThreadX components and the application's initialization function. */ _tx_initialize_high_level(); + /* Call any port specific post-processing. */ TX_PORT_SPECIFIC_POST_INITIALIZATION } @@ -150,4 +153,3 @@ VOID _tx_initialize_kernel_enter(VOID) TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0); #endif } - diff --git a/common/src/tx_timer_initialize.c b/common/src/tx_timer_initialize.c index 2172b4a64..210888a1e 100644 --- a/common/src/tx_timer_initialize.c +++ b/common/src/tx_timer_initialize.c @@ -245,6 +245,7 @@ UINT status; do { + /* Create the system timer thread. */ status = _tx_thread_create(&_tx_timer_thread, TX_CONST_CHAR_TO_CHAR_POINTER_CONVERT("System Timer Thread"), @@ -253,6 +254,7 @@ UINT status; _tx_timer_stack_start, _tx_timer_stack_size, _tx_timer_priority, _tx_timer_priority, TX_NO_TIME_SLICE, TX_DONT_START); + #ifdef TX_SAFETY_CRITICAL /* Check return from thread create - if an error is detected throw an exception. */ @@ -295,4 +297,3 @@ UINT status; #endif #endif } - diff --git a/common_smp/src/tx_thread_system_resume.c b/common_smp/src/tx_thread_system_resume.c index 256c06f2a..476af380a 100644 --- a/common_smp/src/tx_thread_system_resume.c +++ b/common_smp/src/tx_thread_system_resume.c @@ -960,4 +960,3 @@ VOID _tx_thread_system_ni_resume(TX_THREAD *thread_ptr) _tx_thread_system_resume(thread_ptr); } #endif - diff --git a/common_smp/src/tx_thread_system_suspend.c b/common_smp/src/tx_thread_system_suspend.c index d5bf5cc76..a64b4efa7 100644 --- a/common_smp/src/tx_thread_system_suspend.c +++ b/common_smp/src/tx_thread_system_suspend.c @@ -984,4 +984,3 @@ VOID _tx_thread_system_ni_suspend(TX_THREAD *thread_ptr, ULONG timeout) _tx_thread_system_suspend(thread_ptr); } #endif - diff --git a/ports/win32/vs_2019/inc/tx_port.h b/ports/win32/vs_2019/inc/tx_port.h index 2cd61b434..167da692b 100644 --- a/ports/win32/vs_2019/inc/tx_port.h +++ b/ports/win32/vs_2019/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Win32/Visual */ -/* 6.1 */ +/* 6.5.1.202602 */ /* */ /* AUTHOR */ /* */ @@ -66,6 +66,95 @@ #include #include +/* Define automated coverage test extensions required by the regression tests. */ + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; + +#ifdef TX_REGRESSION_TEST + +#define TX_BYTE_ALLOCATE_EXTENSION if (threadx_byte_allocate_loop_test == ((TEST_FLAG) 1)) \ + { \ + pool_ptr -> tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG) 0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION if (threadx_byte_release_loop_test == ((TEST_FLAG) 1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG) 0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 if (threadx_mutex_suspension_put_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG) 0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 if (test_forced_mutex_timeout == ((TEST_FLAG) 1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG) 0); \ + _tx_thread_wait_abort(mutex_ptr -> tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION if (threadx_mutex_suspension_priority_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG) 0); \ + suspend_lowest_priority(); \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR +#define TX_TIMER_INITIALIZE_EXTENSION(a) if (threadx_delete_timer_thread == ((TEST_FLAG) 1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG) 0); \ + delete_timer_thread(); \ + (a) = ((UINT) 1); \ + } +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION if (test_stack_analyze_flag == ((TEST_FLAG) 1)) \ + { \ + thread_ptr -> tx_thread_id = ((TEST_FLAG) 0); \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 2)) \ + { \ + stack_ptr = thread_ptr -> tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION if (test_initialize_flag == ((TEST_FLAG) 1)) \ + { \ + test_initialize_flag = ((TEST_FLAG) 0); \ + return; \ + } + +#endif + /* Define performance metric symbols. */ @@ -101,7 +190,6 @@ #define TX_TIMER_ENABLE_PERFORMANCE_INFO #endif - /* Enable trace info. */ #ifndef TX_ENABLE_EVENT_TRACE @@ -198,7 +286,7 @@ void _tx_win32_debug_entry_insert(char *action, char *file, unsigned long lin #define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) #define TX_TRACE_TIME_MASK 0x0000FFFFUL -*/ + */ #ifndef TX_TRACE_TIME_SOURCE #define TX_TRACE_TIME_SOURCE ((ULONG) (_tx_win32_time_stamp.LowPart)); @@ -232,7 +320,6 @@ void _tx_initialize_start_interrupts(void); #define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION _tx_initialize_start_interrupts(); - /* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING @@ -413,7 +500,7 @@ VOID _tx_thread_interrupt_restore(UINT previous_posture); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX Win32/Visual Studio Version 6.5.0.202601 *"; + "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX Win32/Visual Studio Version 6.5.1.202602 *"; #else extern CHAR _tx_version_id[]; #endif @@ -437,11 +524,11 @@ extern LARGE_INTEGER _tx_win32_time_stamp; #endif #ifndef TX_TIMER_PERIODIC +#ifdef TX_WIN32_SLOW_TIMER +#define TX_TIMER_PERIODIC TX_WIN32_SLOW_TIMER +#else #define TX_TIMER_PERIODIC 10 #endif - #endif - - - +#endif diff --git a/ports/win32/vs_2019/src/tx_initialize_low_level.c b/ports/win32/vs_2019/src/tx_initialize_low_level.c index dd364d06d..c362b3e73 100644 --- a/ports/win32/vs_2019/src/tx_initialize_low_level.c +++ b/ports/win32/vs_2019/src/tx_initialize_low_level.c @@ -9,6 +9,7 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Codex (gpt 5.4). /**************************************************************************/ /**************************************************************************/ @@ -280,4 +281,4 @@ VOID CALLBACK _tx_win32_timer_interrupt(UINT wTimerID, UINT msg, DWORD dwUser, D /* Call ThreadX context restore for interrupt completion. */ _tx_thread_context_restore(); -} \ No newline at end of file +} diff --git a/ports/win64/vs_2022/CMakeLists.txt b/ports/win64/vs_2022/CMakeLists.txt new file mode 100644 index 000000000..328b89d19 --- /dev/null +++ b/ports/win64/vs_2022/CMakeLists.txt @@ -0,0 +1,19 @@ +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/win64/vs_2022/inc/tx_port.h b/ports/win64/vs_2022/inc/tx_port.h new file mode 100644 index 000000000..4cc4e8e07 --- /dev/null +++ b/ports/win64/vs_2022/inc/tx_port.h @@ -0,0 +1,630 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Win64/Visual */ +/* 6.5.1.202602 */ +/* */ +/* AUTHOR */ +/* */ +/* Eclipse ThreadX contributors */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include +#include + +/* Define automated coverage test extensions required by the regression tests. */ + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; + +#ifdef TX_REGRESSION_TEST + +#define TX_BYTE_ALLOCATE_EXTENSION if (threadx_byte_allocate_loop_test == ((TEST_FLAG) 1)) \ + { \ + pool_ptr -> tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG) 0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION if (threadx_byte_release_loop_test == ((TEST_FLAG) 1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG) 0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 if (threadx_mutex_suspension_put_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG) 0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 if (test_forced_mutex_timeout == ((TEST_FLAG) 1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG) 0); \ + _tx_thread_wait_abort(mutex_ptr -> tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION if (threadx_mutex_suspension_priority_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG) 0); \ + suspend_lowest_priority(); \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR +#define TX_TIMER_INITIALIZE_EXTENSION(a) if (threadx_delete_timer_thread == ((TEST_FLAG) 1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG) 0); \ + delete_timer_thread(); \ + (a) = ((UINT) 1); \ + } +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION if (test_stack_analyze_flag == ((TEST_FLAG) 1)) \ + { \ + thread_ptr -> tx_thread_id = ((TEST_FLAG) 0); \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 2)) \ + { \ + stack_ptr = thread_ptr -> tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION if (test_initialize_flag == ((TEST_FLAG) 1)) \ + { \ + test_initialize_flag = ((TEST_FLAG) 0); \ + return; \ + } + +#endif + + +/* Define performance metric symbols. */ + +#ifndef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO +#define TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO +#endif + +#ifndef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO +#define TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO +#endif + +#ifndef TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO +#define TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO +#endif + +#ifndef TX_MUTEX_ENABLE_PERFORMANCE_INFO +#define TX_MUTEX_ENABLE_PERFORMANCE_INFO +#endif + +#ifndef TX_QUEUE_ENABLE_PERFORMANCE_INFO +#define TX_QUEUE_ENABLE_PERFORMANCE_INFO +#endif + +#ifndef TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO +#define TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO +#endif + +#ifndef TX_THREAD_ENABLE_PERFORMANCE_INFO +#define TX_THREAD_ENABLE_PERFORMANCE_INFO +#endif + +#ifndef TX_TIMER_ENABLE_PERFORMANCE_INFO +#define TX_TIMER_ENABLE_PERFORMANCE_INFO +#endif + + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; +typedef uint64_t ULONG64; +#define ULONG64_DEFINED + + +/* Override the alignment type to preserve pointer-sized alignment on LLP64. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEEULL) + + +/* Add Win32 debug insert prototype. */ + +void _tx_win32_debug_entry_insert(char *action, char *file, unsigned long line); + +#ifndef TX_WIN32_DEBUG_ENABLE + +/* If Win32 debug is not enabled, turn logging into white-space. */ + +#define _tx_win32_debug_entry_insert(a, b, c) + +#endif + + +/* Define the TX_MEMSET macro to remove library reference. */ + +#define TX_MEMSET(a,b,c) { \ + UCHAR *ptr; \ + UCHAR value; \ + UINT i, size; \ + ptr = (UCHAR *) ((VOID *) a); \ + value = (UCHAR) b; \ + size = (UINT) c; \ + for (i = 0; i < size; i++) \ + { \ + *ptr++ = value; \ + } \ + } + + +/* Include windows include file. */ + +#include + +#ifndef TX_WIN32_USE_HIGH_RESOLUTION_TIMER +#define TX_WIN32_USE_HIGH_RESOLUTION_TIMER 1 +#endif + +#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION +#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002UL +#endif + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 400 /* Default timer thread stack size - Not used in Win64 port! */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX port. */ + +#define TX_INT_DISABLE 1 /* Disable interrupts */ +#define TX_INT_ENABLE 0 /* Enable interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + + */ + +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE ((ULONG) (_tx_win32_time_stamp.LowPart)) +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port-specific trace extension to pickup the Windows timer. */ + +#define TX_TRACE_PORT_EXTENSION QueryPerformanceCounter((LARGE_INTEGER *)&_tx_win32_time_stamp); + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#define TX_INLINE_INITIALIZATION + + +/* Define the Win32-specific initialization code that is expanded in the generic source. */ + +void _tx_initialize_start_interrupts(void); + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION _tx_initialize_start_interrupts(); + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 HANDLE tx_thread_win32_thread_handle; \ + DWORD tx_thread_win32_thread_id; \ + HANDLE tx_thread_win32_thread_run_semaphore; \ + HANDLE tx_thread_win32_thread_start_semaphore; \ + UINT tx_thread_win32_suspension_type; \ + UINT tx_thread_win32_int_disabled_flag; +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Preserve the thread timeout pointer on LLP64 targets. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +struct TX_THREAD_STRUCT; + + +/* Define the Win32 critical section data structure. */ + +typedef struct TX_WIN32_CRITICAL_SECTION_STRUCT +{ + HANDLE tx_win32_critical_section_mutex_handle; + DWORD tx_win32_critical_section_owner; + ULONG tx_win32_critical_section_nested_count; +} TX_WIN32_CRITICAL_SECTION; + + +/* Define Win32-specific critical section APIs. */ + +void _tx_win32_critical_section_obtain(TX_WIN32_CRITICAL_SECTION *critical_section); +void _tx_win32_critical_section_release(TX_WIN32_CRITICAL_SECTION *critical_section); +void _tx_win32_critical_section_release_all(TX_WIN32_CRITICAL_SECTION *critical_section); + + +/* Define post completion processing for tx_thread_delete, so that the Win32 thread resources are properly removed. */ + +#define TX_THREAD_DELETE_PORT_COMPLETION(thread_ptr) \ +{ \ +BOOL win32_status; \ +DWORD exitcode; \ +HANDLE threadrunsemaphore; \ +HANDLE threadhandle; \ +ULONG wait_count; \ + threadhandle = thread_ptr -> tx_thread_win32_thread_handle; \ + threadrunsemaphore = thread_ptr -> tx_thread_win32_thread_run_semaphore; \ + if ((threadhandle != ((HANDLE) 0)) || (threadrunsemaphore != ((HANDLE) 0)))\ + { \ + _tx_thread_interrupt_restore(tx_saved_posture); \ + if (threadhandle != ((HANDLE) 0)) \ + { \ + wait_count = ((ULONG) 0); \ + do \ + { \ + win32_status = GetExitCodeThread(threadhandle, &exitcode); \ + if ((win32_status) && (exitcode != STILL_ACTIVE)) \ + { \ + break; \ + } \ + ResumeThread(threadhandle); \ + if (threadrunsemaphore != ((HANDLE) 0)) \ + { \ + ReleaseSemaphore(threadrunsemaphore, 1, NULL); \ + } \ + Sleep(1); \ + wait_count++; \ + } while (wait_count < ((ULONG) 100)); \ + win32_status = GetExitCodeThread(threadhandle, &exitcode); \ + if ((win32_status) && (exitcode == STILL_ACTIVE)) \ + { \ + (void) TerminateThread(threadhandle, 0U); \ + (void) WaitForSingleObject(threadhandle, INFINITE); \ + } \ + CloseHandle(threadhandle); \ + } \ + if (threadrunsemaphore != ((HANDLE) 0)) \ + { \ + CloseHandle(threadrunsemaphore); \ + } \ + tx_saved_posture = _tx_thread_interrupt_disable(); \ + } \ +} + + +/* Define post completion processing for tx_thread_reset, so that the Win32 thread resources are properly removed. */ + +#define TX_THREAD_RESET_PORT_COMPLETION(thread_ptr) \ +{ \ +BOOL win32_status; \ +DWORD exitcode; \ +HANDLE threadrunsemaphore; \ +HANDLE threadhandle; \ +ULONG wait_count; \ + threadhandle = thread_ptr -> tx_thread_win32_thread_handle; \ + threadrunsemaphore = thread_ptr -> tx_thread_win32_thread_run_semaphore; \ + if ((threadhandle != ((HANDLE) 0)) || (threadrunsemaphore != ((HANDLE) 0)))\ + { \ + _tx_thread_interrupt_restore(tx_saved_posture); \ + if (threadhandle != ((HANDLE) 0)) \ + { \ + wait_count = ((ULONG) 0); \ + do \ + { \ + win32_status = GetExitCodeThread(threadhandle, &exitcode); \ + if ((win32_status) && (exitcode != STILL_ACTIVE)) \ + { \ + break; \ + } \ + ResumeThread(threadhandle); \ + if (threadrunsemaphore != ((HANDLE) 0)) \ + { \ + ReleaseSemaphore(threadrunsemaphore, 1, NULL); \ + } \ + Sleep(1); \ + wait_count++; \ + } while (wait_count < ((ULONG) 100)); \ + win32_status = GetExitCodeThread(threadhandle, &exitcode); \ + if ((win32_status) && (exitcode == STILL_ACTIVE)) \ + { \ + (void) TerminateThread(threadhandle, 0U); \ + (void) WaitForSingleObject(threadhandle, INFINITE); \ + } \ + CloseHandle(threadhandle); \ + } \ + if (threadrunsemaphore != ((HANDLE) 0)) \ + { \ + CloseHandle(threadrunsemaphore); \ + } \ + tx_saved_posture = _tx_thread_interrupt_disable(); \ + } \ +} + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +UINT _tx_thread_interrupt_disable(void); +VOID _tx_thread_interrupt_restore(UINT previous_posture); + +#define TX_INTERRUPT_SAVE_AREA UINT tx_saved_posture; + +#define TX_DISABLE tx_saved_posture = _tx_thread_interrupt_disable(); + +#define TX_RESTORE _tx_thread_interrupt_restore(tx_saved_posture); + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "(c) 2026 Eclipse ThreadX contributors. * ThreadX Win64/MSVC Version 6.5.1.202602 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +/* Define externals for the Win32 port of ThreadX. */ + +extern TX_WIN32_CRITICAL_SECTION _tx_win32_critical_section; +extern HANDLE _tx_win32_scheduler_semaphore; +extern HANDLE _tx_win32_scheduler_wake_event; +extern DWORD _tx_win32_scheduler_id; +extern ULONG _tx_win32_global_int_disabled_flag; +extern LARGE_INTEGER _tx_win32_time_stamp; +extern ULONG _tx_win32_system_error; +extern HANDLE _tx_win32_timer_handle; +extern HANDLE _tx_win32_timer_thread_handle; +extern HANDLE _tx_win32_isr_semaphore; +extern UINT _tx_win32_timer_waiting; +extern UINT _tx_win32_timer_id; +extern LARGE_INTEGER _tx_win32_time_stamp; + + +#ifndef TX_WIN32_MEMORY_SIZE +#define TX_WIN32_MEMORY_SIZE 64000 +#endif + +VOID _tx_win32_scheduler_wake(VOID); + +#ifndef TX_TIMER_PERIODIC +#ifdef TX_WIN32_SLOW_TIMER +#define TX_TIMER_PERIODIC TX_WIN32_SLOW_TIMER +#else +#define TX_TIMER_PERIODIC 10 +#endif +#endif + +#endif + + + + + + + + + + + + + + diff --git a/ports/win64/vs_2022/src/tx_initialize_low_level.c b/ports/win64/vs_2022/src/tx_initialize_low_level.c new file mode 100644 index 000000000..b7e965758 --- /dev/null +++ b/ports/win64/vs_2022/src/tx_initialize_low_level.c @@ -0,0 +1,417 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include +#include +#include +#pragma comment (lib, "Winmm.lib") + +/* Define various Win32 objects used by the ThreadX port. */ + +TX_WIN32_CRITICAL_SECTION _tx_win32_critical_section; +HANDLE _tx_win32_scheduler_semaphore; +HANDLE _tx_win32_scheduler_wake_event; +DWORD _tx_win32_scheduler_id; +ULONG _tx_win32_global_int_disabled_flag; +LARGE_INTEGER _tx_win32_time_stamp; +ULONG _tx_win32_system_error; +HANDLE _tx_win32_timer_handle; +HANDLE _tx_win32_timer_thread_handle; +HANDLE _tx_win32_isr_semaphore; +UINT _tx_win32_timer_waiting; +extern TX_THREAD *_tx_thread_current_ptr; + + +/* Define simulated timer interrupt. This is done inside a thread, which is + how other interrupts may be defined as well. See code below for an + example. */ + +UINT _tx_win32_timer_id; +VOID CALLBACK _tx_win32_timer_interrupt(UINT wTimerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); +static VOID _tx_win32_timer_start(VOID); +static DWORD WINAPI _tx_win32_timer_thread_entry(LPVOID thread_input); + + +#ifdef TX_WIN32_DEBUG_ENABLE + +extern ULONG _tx_thread_system_state; +extern UINT _tx_thread_preempt_disable; +extern TX_THREAD *_tx_thread_current_ptr; +extern TX_THREAD *_tx_thread_execute_ptr; + + +/* Define the maximum size of the Win32 debug array. */ + +#ifndef TX_WIN32_DEBUG_EVENT_SIZE +#define TX_WIN32_DEBUG_EVENT_SIZE 400 +#endif + + +/* Define debug log in order to debug Win32 issues with this port. */ + +typedef struct TX_WIN32_DEBUG_ENTRY_STRUCT +{ + char *tx_win32_debug_entry_action; + LARGE_INTEGER tx_win32_debug_entry_timestamp; + char *tx_win32_debug_entry_file; + unsigned long tx_win32_debug_entry_line; + TX_WIN32_CRITICAL_SECTION tx_win32_debug_entry_critical_section; + unsigned long tx_win32_debug_entry_int_disabled_flag; + ULONG tx_win32_debug_entry_system_state; + UINT tx_win32_debug_entry_preempt_disable; + TX_THREAD *tx_win32_debug_entry_current_thread; + DWORD tx_win32_debug_entry_current_thread_id; + TX_THREAD *tx_win32_debug_entry_execute_thread; + DWORD tx_win32_debug_entry_execute_thread_id; + DWORD tx_win32_debug_entry_running_id; +} TX_WIN32_DEBUG_ENTRY; + + +/* Define the circular array of Win32 debug entries. */ + +TX_WIN32_DEBUG_ENTRY _tx_win32_debug_entry_array[TX_WIN32_DEBUG_EVENT_SIZE]; + + +/* Define the Win32 debug index. */ + +unsigned long _tx_win32_debug_entry_index = 0; + + +/* Now define the debug entry function. */ +void _tx_win32_debug_entry_insert(char *action, char *file, unsigned long line) +{ + + + /* Get the time stamp. */ + QueryPerformanceCounter((LARGE_INTEGER *)&_tx_win32_time_stamp); + + /* Setup the debug entry. */ + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_action = action; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_timestamp = _tx_win32_time_stamp; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_file = file; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_line = line; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_critical_section = _tx_win32_critical_section; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_int_disabled_flag = _tx_win32_global_int_disabled_flag; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_system_state = _tx_thread_system_state; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_preempt_disable = _tx_thread_preempt_disable; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread = _tx_thread_current_ptr; + if (_tx_thread_current_ptr) + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id = _tx_thread_current_ptr -> tx_thread_win32_thread_id; + else + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id = 0; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread = _tx_thread_execute_ptr; + if (_tx_thread_execute_ptr) + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id = _tx_thread_execute_ptr -> tx_thread_win32_thread_id; + else + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id = 0; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_running_id = GetCurrentThreadId(); + + /* Now move to the next entry. */ + _tx_win32_debug_entry_index++; + + /* Determine if we need to wrap the list. */ + if (_tx_win32_debug_entry_index >= TX_WIN32_DEBUG_EVENT_SIZE) + { + + /* Yes, wrap the list! */ + _tx_win32_debug_entry_index = 0; + } +} + +#endif + + +/* Define the ThreadX timer interrupt handler. */ + +void _tx_timer_interrupt(void); + + +/* Define other external function references. */ + +VOID _tx_initialize_low_level(VOID); +VOID _tx_thread_context_save(VOID); +VOID _tx_thread_context_restore(VOID); +VOID _tx_win32_scheduler_wake(VOID); + + +/* Define other external variable references. */ + +extern VOID *_tx_initialize_unused_memory; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* CreateMutex Win32 create mutex */ +/* CreateThread Win32 create thread */ +/* CreateSemaphore Win32 create semaphore */ +/* GetCurrentThreadId Win32 get current thread ID */ +/* SetProcessAffinityMask Win32 process affinity set */ +/* SetThreadPriority Win32 set thread priority */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/**************************************************************************/ +VOID _tx_initialize_low_level(VOID) +{ + +/* Deprecate TX_WIN32_MULTI_CORE build option and default to restricting + execution to one core. */ + +#ifndef TX_WIN32_BYPASS_AFFINITY_SETUP + + /* Limit this ThreadX simulation on Win64 to a single core. */ + if (SetProcessAffinityMask(GetCurrentProcess(), ((DWORD_PTR) 1)) == 0) + { + + /* Error restricting the process to one core. */ + printf("ThreadX Win64 error restricting the process to one core!\n"); + while(1) + { + } + } +#endif + + /* Pickup the first available memory address. */ + + /* Save the first available memory address. */ + _tx_initialize_unused_memory = malloc(TX_WIN32_MEMORY_SIZE); + + /* Pickup the unique Id of the current thread, which will also be the Id of the scheduler. */ + _tx_win32_scheduler_id = GetCurrentThreadId(); + + /* Create the system critical section mutex. This is used by the system to block all other access, + analogous to an interrupt lockout on an embedded target. */ + _tx_win32_critical_section.tx_win32_critical_section_mutex_handle = CreateMutex(NULL, FALSE, NULL); + _tx_win32_critical_section.tx_win32_critical_section_nested_count = 0; + _tx_win32_critical_section.tx_win32_critical_section_owner = 0; + if (_tx_win32_critical_section.tx_win32_critical_section_mutex_handle == NULL) + { + printf("ThreadX Win64 error creating critical section mutex!\n"); + while(1) + { + } + } + + /* Create the semaphore that regulates when the scheduler executes. */ + _tx_win32_scheduler_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + _tx_win32_isr_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + if ((_tx_win32_scheduler_semaphore == NULL) || (_tx_win32_isr_semaphore == NULL)) + { + printf("ThreadX Win64 error creating semaphores!\n"); + while(1) + { + } + } + + /* Create the event that wakes the scheduler whenever the ready state changes. */ + _tx_win32_scheduler_wake_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (_tx_win32_scheduler_wake_event == NULL) + { + printf("ThreadX Win64 error creating scheduler wake event!\n"); + while(1) + { + } + } + + /* Initialize the global interrupt disabled flag. */ + _tx_win32_global_int_disabled_flag = TX_FALSE; + _tx_win32_timer_waiting = TX_FALSE; + + /* Done, return to caller. */ +} + + +/* This routine is called after initialization is complete in order to start + all interrupt threads. Interrupt threads in addition to the timer may + be added to this routine as well. */ + +void _tx_initialize_start_interrupts(void) +{ + TIMECAPS tc; + UINT wTimerRes; + + /* Queries the timer device to determine its resolution. */ + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) + { + /* Error; application can't continue. */ + printf("Query timer device error."); + while (1) + { + } + } + + wTimerRes = min(max(tc.wPeriodMin, TX_TIMER_PERIODIC), tc.wPeriodMax); + + /* Request the best available timer resolution for the simulation. */ + if (timeBeginPeriod(wTimerRes) != TIMERR_NOERROR) + { + printf("ThreadX Win64 error configuring timer resolution!\n"); + while (1) + { + } + } + + /* Create the periodic waitable timer used to drive simulated interrupts. */ +#if (TX_WIN32_USE_HIGH_RESOLUTION_TIMER != 0) + _tx_win32_timer_handle = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); + if (_tx_win32_timer_handle == NULL) +#endif + { + _tx_win32_timer_handle = CreateWaitableTimer(NULL, FALSE, NULL); + } + + if (_tx_win32_timer_handle == NULL) + { + printf("ThreadX Win64 error creating timer handle!\n"); + while (1) + { + } + } + + /* Create the timer thread so interrupts are serialized through one execution context. */ + _tx_win32_timer_thread_handle = CreateThread(NULL, 0, _tx_win32_timer_thread_entry, NULL, 0, NULL); + if (_tx_win32_timer_thread_handle == NULL) + { + printf("ThreadX Win64 error creating timer thread!\n"); + while (1) + { + } + } + + SetThreadPriority(_tx_win32_timer_thread_handle, THREAD_PRIORITY_HIGHEST); + + _tx_win32_timer_id = 1; + + /* Start the first simulated tick. */ + _tx_win32_timer_start(); +} + +/* Define the ThreadX system timer interrupt. Other interrupts may be simulated + in a similar way. */ + +VOID CALLBACK _tx_win32_timer_interrupt(UINT wTimerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) +{ + TX_PARAMETER_NOT_USED(wTimerID); + TX_PARAMETER_NOT_USED(msg); + TX_PARAMETER_NOT_USED(dwUser); + TX_PARAMETER_NOT_USED(dw1); + TX_PARAMETER_NOT_USED(dw2); + + /* Call ThreadX context save for interrupt preparation. */ + _tx_thread_context_save(); + + /* Call the ThreadX system timer interrupt processing. */ + _tx_timer_interrupt(); + + /* Call ThreadX context restore for interrupt completion. */ + _tx_thread_context_restore(); + + /* Wake the scheduler so it can promptly observe timer-driven work. */ + _tx_win32_scheduler_wake(); +} + + +static DWORD WINAPI _tx_win32_timer_thread_entry(LPVOID thread_input) +{ + TX_PARAMETER_NOT_USED(thread_input); + + /* Drive periodic simulated interrupts from a single thread. */ + while (1) + { + WaitForSingleObject(_tx_win32_timer_handle, INFINITE); + _tx_win32_timer_interrupt(0, 0, 0, 0, 0); + _tx_win32_timer_start(); + } +} + + +VOID _tx_win32_scheduler_wake(VOID) +{ + + /* Wake the scheduler if it is waiting for a state change. */ + if (_tx_win32_scheduler_wake_event != NULL) + { + SetEvent(_tx_win32_scheduler_wake_event); + } +} + + +static VOID _tx_win32_timer_start(VOID) +{ + +LARGE_INTEGER due_time; + + + /* Rearm the host timer relative to "now" to avoid burst catch-up ticks. */ + due_time.QuadPart = -(((LONGLONG) TX_TIMER_PERIODIC) * 10000LL); +#if (TX_WIN32_USE_HIGH_RESOLUTION_TIMER != 0) + if (SetWaitableTimerEx(_tx_win32_timer_handle, &due_time, 0, NULL, NULL, NULL, 0) == 0) +#else + if (SetWaitableTimer(_tx_win32_timer_handle, &due_time, 0, NULL, NULL, FALSE) == 0) +#endif + { + printf("ThreadX Win64 error starting timer!\n"); + while (1) + { + } + } +} + diff --git a/ports/win64/vs_2022/src/tx_thread_context_restore.c b/ports/win64/vs_2022/src/tx_thread_context_restore.c new file mode 100644 index 000000000..9786e7cbe --- /dev/null +++ b/ports/win64/vs_2022/src/tx_thread_context_restore.c @@ -0,0 +1,180 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* ReleaseSemaphore Win32 release semaphore */ +/* ResumeThread Win32 resume thread */ +/* _tx_win32_critical_section_obtain Obtain critical section */ +/* _tx_win32_critical_section_release Release critical section */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/**************************************************************************/ +VOID _tx_thread_context_restore(VOID) +{ +TX_THREAD *execute_thread; + + /* Enter critical section to ensure other threads are not playing with + the core ThreadX data structures. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + /* Debug entry. */ + _tx_win32_debug_entry_insert("CONTEXT_RESTORE", __FILE__, __LINE__); + + /* Decrement the nested interrupt count. */ + _tx_thread_system_state--; + + /* Pickup the execute thread pointer. */ + execute_thread = _tx_thread_execute_ptr; + + /* Determine if this is the first nested interrupt and if a ThreadX + application thread was running at the time. */ + if ((!_tx_thread_system_state) && (_tx_thread_current_ptr)) + { + + /* Yes, this is the first and last interrupt processed. */ + + /* Check to see if preemption is required. */ + if ((_tx_thread_preempt_disable == 0) && (_tx_thread_current_ptr != _tx_thread_execute_ptr)) + { + + /* Preempt the running application thread. We don't need to suspend the + application thread since that is done in the context save processing. */ + + /* Indicate that this thread was suspended asynchronously. */ + _tx_thread_current_ptr -> tx_thread_win32_suspension_type = 1; + + /* Save the remaining time-slice and disable it. */ + if (_tx_timer_time_slice) + { + + _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + _tx_timer_time_slice = 0; + } + + /* Clear the current thread pointer. */ + _tx_thread_current_ptr = TX_NULL; + + /* Block the timer ISR until the resumed thread has observed the wakeup. */ + _tx_win32_timer_waiting = TX_TRUE; + + /* Wakeup the system thread by setting the system semaphore. */ + ReleaseSemaphore(_tx_win32_scheduler_semaphore, 1, NULL); + _tx_win32_scheduler_wake(); + + /* If the timer made a solicited wakeup ready, let that thread run before + the host timer ISR continues. */ + if ((execute_thread != TX_NULL) && + (execute_thread -> tx_thread_win32_suspension_type == 0)) + { + + /* Release the critical section while the scheduler runs. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + WaitForSingleObject(_tx_win32_isr_semaphore, INFINITE); + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + while (WaitForSingleObject(_tx_win32_isr_semaphore, 0) == WAIT_OBJECT_0) + { + } + } + + /* The timer ISR no longer needs to hold off future ticks. */ + _tx_win32_timer_waiting = TX_FALSE; + } + else + { + + /* Since preemption is not required, resume the interrupted thread. */ + ResumeThread(_tx_thread_current_ptr -> tx_thread_win32_thread_handle); + } + } + else if ((!_tx_thread_system_state) && (execute_thread != TX_NULL)) + { + + /* The timer made a thread ready while the scheduler was idle. Keep the + timer ISR blocked until the solicited wakeup has started running. */ + _tx_win32_timer_waiting = TX_TRUE; + _tx_win32_scheduler_wake(); + + if (execute_thread -> tx_thread_win32_suspension_type == 0) + { + + /* Release the critical section while the scheduler runs. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + WaitForSingleObject(_tx_win32_isr_semaphore, INFINITE); + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + while (WaitForSingleObject(_tx_win32_isr_semaphore, 0) == WAIT_OBJECT_0) + { + } + } + + _tx_win32_timer_waiting = TX_FALSE; + } + + /* Leave Win32 critical section. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); +} + + diff --git a/ports/win64/vs_2022/src/tx_thread_context_save.c b/ports/win64/vs_2022/src/tx_thread_context_save.c new file mode 100644 index 000000000..84f2d2902 --- /dev/null +++ b/ports/win64/vs_2022/src/tx_thread_context_save.c @@ -0,0 +1,111 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* SuspendThread Win32 thread suspend */ +/* _tx_win32_critical_section_obtain Obtain critical section */ +/* _tx_win32_critical_section_release Release critical section */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/**************************************************************************/ +VOID _tx_thread_context_save(VOID) +{ + +TX_THREAD *thread_ptr; + + + /* Enter critical section to ensure other threads are not playing with + the core ThreadX data structures. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + /* Debug entry. */ + _tx_win32_debug_entry_insert("CONTEXT_SAVE", __FILE__, __LINE__); + + /* Pickup the current thread pointer. */ + thread_ptr = _tx_thread_current_ptr; + + /* If an application thread is running, suspend it to simulate preemption. */ + if ((thread_ptr) && (_tx_thread_system_state == 0)) + { + + /* Yes, this is the first interrupt and an application thread is running... + suspend it! */ + + /* Suspend the thread to simulate preemption. Note that the thread is suspended BEFORE the protection get + flag is checked to ensure there is not a race condition between this thread and the update of that flag. */ + SuspendThread(thread_ptr -> tx_thread_win32_thread_handle); + + /* Debug entry. */ + _tx_win32_debug_entry_insert("CONTEXT_SAVE-suspend_thread", __FILE__, __LINE__); + + } + + /* Increment the nested interrupt condition. */ + _tx_thread_system_state++; + + /* Exit Win32 critical section. */ + _tx_win32_critical_section_release(&_tx_win32_critical_section); +} + + diff --git a/ports/win64/vs_2022/src/tx_thread_interrupt_control.c b/ports/win64/vs_2022/src/tx_thread_interrupt_control.c new file mode 100644 index 000000000..ab50a3c9e --- /dev/null +++ b/ports/win64/vs_2022/src/tx_thread_interrupt_control.c @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +#include + +/* Define small routines used for the TX_DISABLE/TX_RESTORE macros. */ + +UINT _tx_thread_interrupt_disable(void) +{ + +UINT previous_value; + + + previous_value = _tx_thread_interrupt_control(TX_INT_DISABLE); + return(previous_value); +} + + +VOID _tx_thread_interrupt_restore(UINT previous_posture) +{ + + previous_posture = _tx_thread_interrupt_control(previous_posture); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* ExitThread Win32 thread exit */ +/* GetCurrentThread Win32 get current thread */ +/* GetCurrentThreadId Win32 get current thread ID */ +/* GetThreadPriority Win32 get thread priority */ +/* _tx_win32_critical_section_obtain Obtain critical section */ +/* _tx_win32_critical_section_release Release critical section */ +/* _tx_win32_critical_section_release_all */ +/* Release critical section */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/**************************************************************************/ +UINT _tx_thread_interrupt_control(UINT new_posture) +{ + +UINT old_posture; +HANDLE threadhandle; +int threadpriority; +DWORD threadid; +TX_THREAD *thread_ptr; + + + /* Enter Win32 critical section. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + +#ifdef TX_WIN32_DEBUG_ENABLE + + /* Determine if this is a disable or enable request. */ + if (new_posture == TX_INT_ENABLE) + { + /* Enable. */ + _tx_win32_debug_entry_insert("RESTORE", __FILE__, __LINE__); + } + else + { + /* Disable. */ + _tx_win32_debug_entry_insert("DISABLE", __FILE__, __LINE__); + } +#endif + + /* Determine if the thread was terminated. */ + + /* Pickup the handle of the current thread. */ + threadhandle = GetCurrentThread(); + + /* Pickup the current thread pointer. */ + thread_ptr = _tx_thread_current_ptr; + + /* Pickup the priority of the current thread. */ + threadpriority = GetThreadPriority(threadhandle); + + /* Pickup the ID of the current thread. */ + threadid = GetCurrentThreadId(); + + /* Determine if this is a thread (THREAD_PRIORITY_LOWEST) and it does not + match the current thread pointer. */ + if ((threadpriority == THREAD_PRIORITY_LOWEST) && + ((!thread_ptr) || (thread_ptr -> tx_thread_win32_thread_id != threadid))) + { + + /* This indicates the Win32 thread was actually terminated by ThreadX is only + being allowed to run in order to cleanup its resources. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + + /* Exit this thread. */ + ExitThread(0); + } + + /* Determine the current interrupt lockout condition. */ + if (_tx_win32_critical_section.tx_win32_critical_section_nested_count == 1) + { + + /* First pass through, interrupts are enabled. */ + old_posture = TX_INT_ENABLE; + } + else + { + + /* Interrupts are disabled. */ + old_posture = TX_INT_DISABLE; + } + + /* First, determine if this call is from a non-thread. */ + if (_tx_thread_system_state) + { + + /* Determine how to apply the new posture. */ + if (new_posture == TX_INT_ENABLE) + { + + /* Clear the disabled flag. */ + _tx_win32_global_int_disabled_flag = TX_FALSE; + + /* Determine if the critical section is locked. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + } + else if (new_posture == TX_INT_DISABLE) + { + + /* Set the disabled flag. */ + _tx_win32_global_int_disabled_flag = TX_TRUE; + } + } + else if (thread_ptr) + { + + /* Determine how to apply the new posture. */ + if (new_posture == TX_INT_ENABLE) + { + + /* Clear the disabled flag. */ + _tx_thread_current_ptr -> tx_thread_win32_int_disabled_flag = TX_FALSE; + + /* Determine if the critical section is locked. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + } + else if (new_posture == TX_INT_DISABLE) + { + + /* Set the disabled flag. */ + _tx_thread_current_ptr -> tx_thread_win32_int_disabled_flag = TX_TRUE; + } + } + + /* Return the previous interrupt disable posture. */ + return(old_posture); +} + + diff --git a/ports/win64/vs_2022/src/tx_thread_schedule.c b/ports/win64/vs_2022/src/tx_thread_schedule.c new file mode 100644 index 000000000..d08305ee8 --- /dev/null +++ b/ports/win64/vs_2022/src/tx_thread_schedule.c @@ -0,0 +1,331 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + + +static VOID _tx_win32_semaphore_reset(HANDLE semaphore_handle); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* ReleaseSemaphore Win32 release semaphore */ +/* ResumeThread Win32 resume thread */ +/* _tx_win32_scheduler_wake Wake scheduler waiters */ +/* WaitForSingleObject Win32 wait on a semaphore */ +/* _tx_win32_critical_section_obtain Obtain critical section */ +/* _tx_win32_critical_section_release Release critical section */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/**************************************************************************/ +VOID _tx_thread_schedule(VOID) +{ +DWORD wait_status; + + + /* Loop forever. */ + while(1) + { + + /* Wait for a thread to execute and all ISRs to complete. */ + while(1) + { + + + /* Enter Win32 critical section. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SCHEDULE-wake_up", __FILE__, __LINE__); + + /* Determine if there is a thread ready to execute AND all ISRs + are complete. */ + if ((_tx_thread_execute_ptr != TX_NULL) && (_tx_thread_system_state == 0)) + { + + /* Get out of this loop and schedule the thread! */ + break; + } + else + { + + /* Leave the critical section. */ + _tx_win32_critical_section_release(&_tx_win32_critical_section); + + /* Wait for the next scheduling state change. */ + WaitForSingleObject(_tx_win32_scheduler_wake_event, INFINITE); + } + } + + /* Yes! We have a thread to execute. Note that the critical section is already + active from the scheduling loop above. */ + + /* Setup the current thread pointer. */ + _tx_thread_current_ptr = _tx_thread_execute_ptr; + + /* Increment the run count for this thread. */ + _tx_thread_current_ptr -> tx_thread_run_count++; + + /* Setup time-slice, if present. */ + _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + /* Determine how the thread was suspended. */ + if (_tx_thread_current_ptr -> tx_thread_win32_suspension_type) + { + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SCHEDULE-resume_thread", __FILE__, __LINE__); + + /* Pseudo interrupt suspension. The thread is not waiting on + its run semaphore. */ + ResumeThread(_tx_thread_current_ptr -> tx_thread_win32_thread_handle); + } + else + { + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SCHEDULE-release_sem", __FILE__, __LINE__); + + /* Clear any stale wakeup acknowledgements before this solicited resume. */ + _tx_win32_semaphore_reset(_tx_thread_current_ptr -> tx_thread_win32_thread_start_semaphore); + _tx_win32_semaphore_reset(_tx_thread_current_ptr -> tx_thread_win32_thread_run_semaphore); + + /* Let the thread run again by releasing its run semaphore. */ + if (ReleaseSemaphore(_tx_thread_current_ptr -> tx_thread_win32_thread_run_semaphore, 1, NULL) == 0) + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } + + /* Let the solicited wakeup reach ThreadX before the timer ISR advances again. */ + if (_tx_win32_timer_waiting) + { + + /* Wait for the thread to acknowledge the wakeup and then release the ISR. */ + wait_status = WaitForSingleObject(_tx_thread_current_ptr -> tx_thread_win32_thread_start_semaphore, INFINITE); + if (ReleaseSemaphore(_tx_win32_isr_semaphore, 1, NULL) == 0) + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } + + if (wait_status != WAIT_OBJECT_0) + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } + } + } + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SCHEDULE-self_suspend_sem", __FILE__, __LINE__); + + /* Exit Win32 critical section. */ + _tx_win32_critical_section_release(&_tx_win32_critical_section); + + /* Now suspend the main thread so the application thread can run. */ + WaitForSingleObject(_tx_win32_scheduler_semaphore, INFINITE); + } +} + + +static VOID _tx_win32_semaphore_reset(HANDLE semaphore_handle) +{ + + /* Drain any stale semaphore count from a previous host-side wakeup. */ + while (WaitForSingleObject(semaphore_handle, 0) == WAIT_OBJECT_0) + { + } +} + + +/* Define the ThreadX Win32 critical section get, release, and release all functions. */ + +void _tx_win32_critical_section_obtain(TX_WIN32_CRITICAL_SECTION *critical_section) +{ + /* Is the protection owned? */ + if (critical_section -> tx_win32_critical_section_owner == GetCurrentThreadId()) + { + + /* Simply increment the nested counter. */ + critical_section -> tx_win32_critical_section_nested_count++; + } + else + { + + /* Get the Win32 critical section. */ + if (WaitForSingleObject(critical_section -> tx_win32_critical_section_mutex_handle, INFINITE) != WAIT_OBJECT_0) + { + + /* Increment the system error counter and stop when the mutex cannot be acquired. */ + _tx_win32_system_error++; + while(1) + { + } + } + + /* At this point we have the mutex. */ + + /* Increment the nesting counter. */ + critical_section -> tx_win32_critical_section_nested_count = 1; + + /* Remember the owner. */ + critical_section -> tx_win32_critical_section_owner = GetCurrentThreadId(); + } +} + + +void _tx_win32_critical_section_release(TX_WIN32_CRITICAL_SECTION *critical_section) +{ + + + /* Ensure the caller is the mutex owner. */ + if (critical_section -> tx_win32_critical_section_owner == GetCurrentThreadId()) + { + + /* Determine if there is protection. */ + if (critical_section -> tx_win32_critical_section_nested_count) + { + + /* Decrement the nesting counter. */ + critical_section -> tx_win32_critical_section_nested_count--; + + /* Determine if the critical section is now being released. */ + if (critical_section -> tx_win32_critical_section_nested_count == 0) + { + + /* Yes, it is being released clear the owner. */ + critical_section -> tx_win32_critical_section_owner = 0; + + /* Finally, release the mutex. */ + if (ReleaseMutex(critical_section -> tx_win32_critical_section_mutex_handle) != TX_TRUE) + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } + + /* Just in case, make sure there the mutex is not owned. */ + while (ReleaseMutex(critical_section -> tx_win32_critical_section_mutex_handle) == TX_TRUE) + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } + } + } + } + else + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } +} + + +void _tx_win32_critical_section_release_all(TX_WIN32_CRITICAL_SECTION *critical_section) +{ + + /* Ensure the caller is the mutex owner. */ + if (critical_section -> tx_win32_critical_section_owner == GetCurrentThreadId()) + { + + /* Determine if there is protection. */ + if (critical_section -> tx_win32_critical_section_nested_count) + { + + /* Clear the nesting counter. */ + critical_section -> tx_win32_critical_section_nested_count = 0; + + /* Yes, it is being release clear the owner. */ + critical_section -> tx_win32_critical_section_owner = 0; + + /* Finally, release the mutex. */ + if (ReleaseMutex(critical_section -> tx_win32_critical_section_mutex_handle) != TX_TRUE) + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } + + /* Just in case, make sure there the mutex is not owned. */ + while (ReleaseMutex(critical_section -> tx_win32_critical_section_mutex_handle) == TX_TRUE) + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } + } + } + else + { + + /* Increment the system error counter. */ + _tx_win32_system_error++; + } +} + + diff --git a/ports/win64/vs_2022/src/tx_thread_stack_build.c b/ports/win64/vs_2022/src/tx_thread_stack_build.c new file mode 100644 index 000000000..c50f7754d --- /dev/null +++ b/ports/win64/vs_2022/src/tx_thread_stack_build.c @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" +#include + + +/* Prototype for new thread entry function. */ + +DWORD WINAPI _tx_win32_thread_entry(LPVOID p); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* CreateThread Win32 create thread */ +/* ResumeThread Win32 resume thread */ +/* SetThreadPriority Win32 set thread priority */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* _tx_thread_reset Reset thread service */ +/* */ +/**************************************************************************/ +VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +{ + + /* Create a Win32 thread for the application thread. */ + thread_ptr -> tx_thread_win32_thread_handle = + CreateThread(NULL, 0, _tx_win32_thread_entry, (LPVOID) thread_ptr, CREATE_SUSPENDED, + &(thread_ptr -> tx_thread_win32_thread_id)); + + /* Check for a good thread create. */ + if (!thread_ptr -> tx_thread_win32_thread_handle) + { + + /* Display an error message. */ + printf("ThreadX Win32 error creating thread!\n"); + while(1) + { + } + } + + /* Otherwise, we have a good thread create. Now set the priority to + a lower level. */ + SetThreadPriority(thread_ptr -> tx_thread_win32_thread_handle, THREAD_PRIORITY_LOWEST); + + /* Create the run semaphore for the thread. This will allow the scheduler + control over when the thread actually runs. */ + thread_ptr -> tx_thread_win32_thread_run_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + + /* Determine if the run semaphore was created successfully. */ + if (!thread_ptr -> tx_thread_win32_thread_run_semaphore) + { + + /* Display an error message. */ + printf("ThreadX Win32 error creating thread running semaphore!\n"); + while(1) + { + } + } + + /* Create the scheduler acknowledgement semaphore for this thread. */ + thread_ptr -> tx_thread_win32_thread_start_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + + /* Determine if the start semaphore was created successfully. */ + if (!thread_ptr -> tx_thread_win32_thread_start_semaphore) + { + + /* Display an error message. */ + printf("ThreadX Win32 error creating thread start semaphore!\n"); + while(1) + { + } + } + + /* Setup the thread suspension type to solicited thread suspension. + Pseudo interrupt handlers will suspend with this field set to 1. */ + thread_ptr -> tx_thread_win32_suspension_type = 0; + + /* Clear the disabled count that will keep track of the + tx_interrupt_control nesting. */ + thread_ptr -> tx_thread_win32_int_disabled_flag = 0; + + /* Setup a fake thread stack pointer. */ + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((CHAR *) thread_ptr -> tx_thread_stack_end) - 8); + + /* Clear the first word of the stack. */ + *(((ULONG *) thread_ptr -> tx_thread_stack_ptr) - 1) = 0; + + /* Make the thread initially ready so it will run to the initial wait on + its run semaphore. */ + ResumeThread(thread_ptr -> tx_thread_win32_thread_handle); + + /* Wait until the host thread is parked at the controlled handoff point + before ThreadX can schedule it. */ + if (WaitForSingleObject(thread_ptr -> tx_thread_win32_thread_start_semaphore, INFINITE) != WAIT_OBJECT_0) + { + + /* Display an error message. */ + printf("ThreadX Win32 error synchronizing thread startup!\n"); + while(1) + { + } + } +} + + +DWORD WINAPI _tx_win32_thread_entry(LPVOID ptr) +{ + +TX_THREAD *thread_ptr; +TX_THREAD *current_thread_ptr; +HANDLE threadhandle; +int threadpriority; +DWORD threadid; + + /* Pickup the current thread pointer. */ + thread_ptr = (TX_THREAD *) ptr; + + /* Tell the creator that this host thread has reached the controlled + handoff point and is ready to be scheduled. */ + ReleaseSemaphore(thread_ptr -> tx_thread_win32_thread_start_semaphore, 1, NULL); + + /* Now suspend the thread initially. If the thread has already + been scheduled, this will return immediately. */ + WaitForSingleObject(thread_ptr -> tx_thread_win32_thread_run_semaphore, INFINITE); + + /* Acknowledge that the host thread is now able to execute ThreadX code. */ + ReleaseSemaphore(thread_ptr -> tx_thread_win32_thread_start_semaphore, 1, NULL); + + /* A deleted host thread can be released only to let it exit. In notify-enabled + builds, the first TX_DISABLE in _tx_thread_shell_entry catches this path. + When callbacks are disabled, perform the same check before the shell calls + the stale ThreadX entry function. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + threadhandle = GetCurrentThread(); + threadpriority = GetThreadPriority(threadhandle); + threadid = GetCurrentThreadId(); + current_thread_ptr = _tx_thread_current_ptr; + if ((threadpriority == THREAD_PRIORITY_LOWEST) && + ((current_thread_ptr == TX_NULL) || (current_thread_ptr -> tx_thread_win32_thread_id != threadid))) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + ExitThread(0); + } + _tx_win32_critical_section_release(&_tx_win32_critical_section); + + /* Call ThreadX thread entry point. */ + _tx_thread_shell_entry(); + + return EXIT_SUCCESS; +} + + diff --git a/ports/win64/vs_2022/src/tx_thread_system_return.c b/ports/win64/vs_2022/src/tx_thread_system_return.c new file mode 100644 index 000000000..7a8fc89c0 --- /dev/null +++ b/ports/win64/vs_2022/src/tx_thread_system_return.c @@ -0,0 +1,216 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" +#include + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the system. Only a minimal context */ +/* is saved since the compiler assumes temp registers are going to get */ +/* slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_win32_critical_section_obtain Obtain critical section */ +/* _tx_win32_critical_section_release Release critical section */ +/* _tx_win32_critical_section_release_all */ +/* Release critical section */ +/* ExitThread Win32 thread exit */ +/* GetCurrentThread Win32 get current thread */ +/* GetCurrentThreadId Win32 get current thread ID */ +/* GetThreadPriority Win32 get thread priority */ +/* ReleaseSemaphore Win32 release semaphore */ +/* WaitForSingleObject Win32 wait on semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/**************************************************************************/ +VOID _tx_thread_system_return(VOID) +{ + +TX_THREAD *temp_thread_ptr; +HANDLE temp_run_semaphore; +UINT temp_thread_state; +HANDLE threadhandle; +int threadpriority; +DWORD threadid; + + + /* Enter Win32 critical section. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + /* Pickup the handle of the current thread. */ + threadhandle = GetCurrentThread(); + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SYSTEM_RETURN", __FILE__, __LINE__); + + /* First, determine if the thread was terminated. */ + + /* Pickup the priority of the current thread. */ + threadpriority = GetThreadPriority(threadhandle); + + /* Pickup the ID of the current thread. */ + threadid = GetCurrentThreadId(); + + /* Pickup the current thread pointer. */ + temp_thread_ptr = _tx_thread_current_ptr; + + /* Determine if this is a thread (THREAD_PRIORITY_LOWEST) and it does not + match the current thread pointer. */ + if ((threadpriority == THREAD_PRIORITY_LOWEST) && + ((!temp_thread_ptr) || (temp_thread_ptr -> tx_thread_win32_thread_id != threadid))) + { + + /* This indicates the Win32 thread was actually terminated by ThreadX and is only + being allowed to run in order to cleanup its resources. */ + + /* Release critical section. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + + /* Exit thread. */ + ExitThread(0); + } + + /* Determine if the time-slice is active. */ + if (_tx_timer_time_slice) + { + + /* Preserve current remaining time-slice for the thread and clear the current time-slice. */ + temp_thread_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + _tx_timer_time_slice = 0; + } + + /* Save the run semaphore into a temporary variable as well. */ + temp_run_semaphore = temp_thread_ptr -> tx_thread_win32_thread_run_semaphore; + + /* Pickup the current thread state. */ + temp_thread_state = temp_thread_ptr -> tx_thread_state; + + /* Setup the suspension type for this thread. */ + temp_thread_ptr -> tx_thread_win32_suspension_type = 0; + + /* Set the current thread pointer to NULL. */ + _tx_thread_current_ptr = TX_NULL; + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SYSTEM_RETURN-release_sem", __FILE__, __LINE__); + + /* Release the semaphore that the main scheduling thread is waiting + on. Note that the main scheduling algorithm will take care of + setting the current thread pointer to NULL. */ + ReleaseSemaphore(_tx_win32_scheduler_semaphore, 1, NULL); + _tx_win32_scheduler_wake(); + + /* Leave Win32 critical section. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + + /* Determine if the thread was self-terminating. */ + if (temp_thread_state == TX_TERMINATED) + { + + /* Exit the thread instead of waiting on the semaphore! */ + ExitThread(0); + } + + /* Wait on the run semaphore for this thread. This won't get set again + until the thread is scheduled. */ + WaitForSingleObject(temp_run_semaphore, INFINITE); + + /* Acknowledge that the thread is once again executing ThreadX code. */ + ReleaseSemaphore(temp_thread_ptr -> tx_thread_win32_thread_start_semaphore, 1, NULL); + + /* Enter Win32 critical section. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SYSTEM_RETURN-wake_up", __FILE__, __LINE__); + + /* Determine if the thread was terminated. */ + + /* Pickup the current thread pointer. */ + temp_thread_ptr = _tx_thread_current_ptr; + + /* Determine if this is a thread (THREAD_PRIORITY_LOWEST) and it does not + match the current thread pointer. */ + if ((threadpriority == THREAD_PRIORITY_LOWEST) && + ((!temp_thread_ptr) || (temp_thread_ptr -> tx_thread_win32_thread_id != threadid))) + { + + /* Leave Win32 critical section. */ + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + + /* This indicates the Win32 thread was actually terminated by ThreadX and is only + being allowed to run in order to cleanup its resources. */ + ExitThread(0); + } + + /* Now determine if the application thread last had interrupts disabled. */ + + /* Debug entry. */ + _tx_win32_debug_entry_insert("SYSTEM_RETURN-finish", __FILE__, __LINE__); + + /* Determine if this thread had interrupts disabled. */ + if (!_tx_thread_current_ptr -> tx_thread_win32_int_disabled_flag) + { + + /* Leave Win32 critical section. */ + _tx_win32_critical_section_release(&_tx_win32_critical_section); + } +} + + diff --git a/ports/win64/vs_2022/src/tx_timer_interrupt.c b/ports/win64/vs_2022/src/tx_timer_interrupt.c new file mode 100644 index 000000000..6814334b5 --- /dev/null +++ b/ports/win64/vs_2022/src/tx_timer_interrupt.c @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_timer.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Win64/MSVC */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_win32_critical_section_obtain Obtain critical section */ +/* _tx_win32_critical_section_release Release critical section */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/**************************************************************************/ +VOID _tx_timer_interrupt(VOID) +{ + + + /* Enter critical section to ensure other threads are not playing with + the core ThreadX data structures. */ + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + /* Debug entry. */ + _tx_win32_debug_entry_insert("TIMER INTERRUPT", __FILE__, __LINE__); + + /* Increment the system clock. */ + _tx_timer_system_clock++; + + /* Test for time-slice expiration. */ + if (_tx_timer_time_slice) + { + + /* Decrement the time_slice. */ + _tx_timer_time_slice--; + + /* Check for expiration. */ + if (_tx_timer_time_slice == 0) + { + + /* Set the time-slice expired flag. */ + _tx_timer_expired_time_slice = TX_TRUE; + } + } + + /* Test for timer expiration. */ + if (*_tx_timer_current_ptr) + { + + /* Set expiration flag. */ + _tx_timer_expired = TX_TRUE; + } + else + { + + /* No timer expired, increment the timer pointer. */ + _tx_timer_current_ptr++; + + /* Check for wrap-around. */ + if (_tx_timer_current_ptr == _tx_timer_list_end) + { + + /* Wrap to beginning of list. */ + _tx_timer_current_ptr = _tx_timer_list_start; + } + } + + /* See if anything has expired. */ + if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + { + + /* Did a timer expire? */ + if (_tx_timer_expired) + { + + /* Process timer expiration. */ + _tx_timer_expiration_process(); + } + + /* Did time slice expire? */ + if (_tx_timer_expired_time_slice) + { + + /* Time slice interrupted thread. */ + _tx_thread_time_slice(); + } + } + + /* Exit Win32 critical section. */ + _tx_win32_critical_section_release(&_tx_win32_critical_section); +} + + diff --git a/ports_smp/win64/vs_2022/CMakeLists.txt b/ports_smp/win64/vs_2022/CMakeLists.txt new file mode 100644 index 000000000..982f9684f --- /dev/null +++ b/ports_smp/win64/vs_2022/CMakeLists.txt @@ -0,0 +1,28 @@ +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_core_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_core_preempt.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_current_state_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_current_thread_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_initialize_wait.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_low_level_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_protect.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_time_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_smp_unprotect.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.c + ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports_smp/win64/vs_2022/inc/tx_port.h b/ports_smp/win64/vs_2022/inc/tx_port.h new file mode 100644 index 000000000..d9790f35e --- /dev/null +++ b/ports_smp/win64/vs_2022/inc/tx_port.h @@ -0,0 +1,532 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + +/************* Define ThreadX SMP constants. *************/ + +#define TX_DISABLE_INLINE + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xFU +#endif + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT +#define TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC +#include "tx_thread_smp_core_wakeup.h" +#else +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + +#define INLINE_DECLARE __inline + +/************* End ThreadX SMP constants. *************/ + + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned long long ULONG64; +#define ULONG64_DEFINED + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEEULL) + + +#include +#include +#include + +#ifndef __GNUC__ +#ifndef __attribute__ +#define __attribute__(a) +#endif +#endif + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; +extern UINT mutex_priority_change_extension_selection; +extern UINT priority_change_extension_selection; + +#ifdef TX_REGRESSION_TEST + +#define TX_PORT_SPECIFIC_MEMORY_SYNCHRONIZATION other_core_status = other_core_status + _tx_thread_system_state[0]; \ + _tx_thread_system_state[0] = 0; + +#define TX_BYTE_ALLOCATE_EXTENSION if (threadx_byte_allocate_loop_test == ((TEST_FLAG) 1)) \ + { \ + pool_ptr -> tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG) 0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION if (threadx_byte_release_loop_test == ((TEST_FLAG) 1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG) 0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 if (threadx_mutex_suspension_put_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG) 0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 if (test_forced_mutex_timeout == ((TEST_FLAG) 1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG) 0); \ + _tx_thread_wait_abort(mutex_ptr -> tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION if (threadx_mutex_suspension_priority_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG) 0); \ + if (mutex_priority_change_extension_selection == 2U) \ + original_priority = new_priority; \ + if (mutex_priority_change_extension_selection == 3U) \ + original_pt_thread = thread_ptr; \ + if (mutex_priority_change_extension_selection == 4U) \ + { \ + execute_ptr = thread_ptr; \ + _tx_thread_preemption__threshold_scheduled = TX_NULL; \ + } \ + suspend_lowest_priority(); \ + } + +#define TX_THREAD_PRIORITY_CHANGE_EXTENSION if (priority_change_extension_selection != ((TEST_FLAG) 0)) \ + { \ + if (priority_change_extension_selection == 1U) \ + thread_ptr -> tx_thread_smp_core_mapped = TX_THREAD_SMP_MAX_CORES; \ + else if (priority_change_extension_selection == 2U) \ + { \ + original_priority = new_priority; \ + _tx_thread_execute_ptr[0] = TX_NULL; \ + } \ + else if (priority_change_extension_selection == 3U) \ + { \ + original_pt_thread = thread_ptr; \ + } \ + else \ + { \ + _tx_thread_preemption__threshold_scheduled = TX_NULL; \ + } \ + priority_change_extension_selection = 0; \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR +#define TX_TIMER_INITIALIZE_EXTENSION(a) if (threadx_delete_timer_thread == ((TEST_FLAG) 1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG) 0); \ + delete_timer_thread(); \ + (a) = ((UINT) 1); \ + } +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION if (test_stack_analyze_flag == ((TEST_FLAG) 1)) \ + { \ + thread_ptr -> tx_thread_id = ((TEST_FLAG) 0); \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 2)) \ + { \ + stack_ptr = thread_ptr -> tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION if (test_initialize_flag == ((TEST_FLAG) 1)) \ + { \ + test_initialize_flag = ((TEST_FLAG) 0); \ + return; \ + } + +#endif + + +void _tx_win32_debug_entry_insert(char *action, char *file, unsigned long line); + +#ifndef TX_WIN32_DEBUG_ENABLE +#define _tx_win32_debug_entry_insert(a, b, c) +#endif + + +#ifndef TX_MISRA_ENABLE +#define TX_MEMSET(a,b,c) { \ + UCHAR *ptr; \ + UCHAR value; \ + UINT i, size; \ + ptr = (UCHAR *) ((VOID *) a); \ + value = (UCHAR) b; \ + size = (UINT) c; \ + for (i = 0; i < size; i++) \ + { \ + *ptr++ = value; \ + } \ + } +#endif + + +#include + +#ifndef TX_WIN32_USE_ADDRESS_WAIT +#define TX_WIN32_USE_ADDRESS_WAIT 1 +#endif + +#ifndef TX_WIN32_USE_HIGH_RESOLUTION_TIMER +#define TX_WIN32_USE_HIGH_RESOLUTION_TIMER 1 +#endif + +#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION +#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002UL +#endif + + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 +#endif + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 400 +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 +#endif + +#define TX_INT_DISABLE 1 +#define TX_INT_ENABLE 0 + +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE ((ULONG) (_tx_win32_time_stamp.LowPart)) +#endif + +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + +#define TX_TRACE_PORT_EXTENSION QueryPerformanceCounter((LARGE_INTEGER *)&_tx_win32_time_stamp); +#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + +void _tx_initialize_start_interrupts(void); + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION _tx_initialize_start_interrupts(); \ + { \ + UINT k; \ + for (k = 1U; k < TX_THREAD_SMP_MAX_CORES; k++) \ + { \ + _tx_thread_system_state[k] = 0; \ + } \ + } + +#ifdef TX_WIN32_PROFILE_ENABLE +#define TX_WIN32_PROFILE_THREAD_EXTENSION ULONG64 tx_thread_win32_profile_run_signal_ticks; \ + ULONG64 tx_thread_win32_profile_run_wake_ticks; +#else +#define TX_WIN32_PROFILE_THREAD_EXTENSION +#endif + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +#define TX_THREAD_EXTENSION_0 HANDLE tx_thread_win32_thread_handle; \ + DWORD tx_thread_win32_thread_id; \ + HANDLE tx_thread_win32_thread_run_semaphore; \ + HANDLE tx_thread_win32_thread_start_semaphore; \ + LONG tx_thread_win32_run_sequence; \ + LONG tx_thread_win32_run_sequence_seen; \ + LONG tx_thread_win32_start_sequence; \ + UINT tx_thread_win32_suspension_type; \ + UINT tx_thread_win32_mutex_access; \ + UINT tx_thread_win32_int_disabled_flag; \ + UINT tx_thread_win32_deferred_preempt; \ + UINT tx_thread_win32_virtual_core; \ + TX_WIN32_PROFILE_THREAD_EXTENSION +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 +#define TX_THREAD_EXTENSION_3 + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +struct TX_THREAD_STRUCT; + +typedef struct TX_WIN32_CRITICAL_SECTION_STRUCT +{ + HANDLE tx_win32_critical_section_mutex_handle; + DWORD tx_win32_critical_section_owner; + ULONG tx_win32_critical_section_nested_count; +} TX_WIN32_CRITICAL_SECTION; + +void _tx_win32_critical_section_obtain(TX_WIN32_CRITICAL_SECTION *critical_section); +void _tx_win32_critical_section_release(TX_WIN32_CRITICAL_SECTION *critical_section); +void _tx_win32_critical_section_release_all(TX_WIN32_CRITICAL_SECTION *critical_section); + +typedef struct TX_THREAD_STRUCT TX_THREAD; + +void _tx_thread_delete_port_completion(TX_THREAD *thread_ptr, UINT tx_interrupt_save); +#define TX_THREAD_DELETE_PORT_COMPLETION(thread_ptr) _tx_thread_delete_port_completion(thread_ptr, tx_interrupt_save); + +void _tx_thread_reset_port_completion(TX_THREAD *thread_ptr, UINT tx_interrupt_save); +#define TX_THREAD_RESET_PORT_COMPLETION(thread_ptr) _tx_thread_reset_port_completion(thread_ptr, tx_interrupt_save); + + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + struct TX_THREAD_STRUCT *tx_thread_smp_protect_thread; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + DWORD tx_thread_smp_protect_thread_id; +} TX_THREAD_SMP_PROTECT; + +typedef struct TX_THREAD_SMP_CORE_MAPPING_STRUCT +{ + HANDLE tx_thread_smp_core_mapping_thread_handle; + DWORD tx_thread_smp_core_mapping_thread_id; + struct TX_THREAD_STRUCT *tx_thread_smp_core_mapping_thread; +} TX_THREAD_SMP_CORE_MAPPING; + +struct TX_THREAD_STRUCT * _tx_thread_smp_current_thread_get(void); +UINT _tx_thread_smp_core_get(void); +void _tx_thread_smp_core_preempt(UINT core); +UINT _tx_thread_smp_protect(void); +void _tx_thread_smp_unprotect(UINT interrupt_save); +ULONG _tx_thread_smp_current_state_get(void); +ULONG _tx_thread_smp_time_get(void); +void _tx_thread_smp_low_level_initialize(UINT number_of_cores); +void _tx_thread_smp_initialize_wait(void); + +#ifdef TX_THREAD_SMP_DEBUG_ENABLE +void _tx_thread_smp_debug_entry_insert(ULONG id, ULONG suspend, VOID *thread_ptr); +#else +#define _tx_thread_smp_debug_entry_insert(a, b, c) +#endif + +#define TX_SMP_CORE_ID _tx_thread_smp_core_get() + + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +UINT _tx_thread_interrupt_disable(void); +VOID _tx_thread_interrupt_restore(UINT previous_posture); +UINT _tx_thread_interrupt_control(UINT new_posture); + +#define TX_INTERRUPT_SAVE_AREA UINT tx_interrupt_save; +#define TX_DISABLE tx_interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(tx_interrupt_save); + + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "(c) 2026 Eclipse ThreadX contributors. * ThreadX SMP/Win64/MSVC Version 6.5.1.202602 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +extern TX_WIN32_CRITICAL_SECTION _tx_win32_critical_section; +extern HANDLE _tx_win32_scheduler_event; +extern DWORD _tx_win32_scheduler_id; +extern ULONG _tx_win32_global_int_disabled_flag; +extern LARGE_INTEGER _tx_win32_time_stamp; +extern ULONG _tx_win32_system_error; +extern HANDLE _tx_win32_timer_handle; +extern HANDLE _tx_win32_timer_thread_handle; +extern DWORD _tx_win32_timer_thread_id; +extern HANDLE _tx_win32_isr_semaphore; +extern UINT _tx_win32_timer_waiting; +extern TX_THREAD_SMP_CORE_MAPPING _tx_win32_virtual_cores[TX_THREAD_SMP_MAX_CORES]; +extern __declspec(thread) int _tx_win32_threadx_thread; +extern __declspec(thread) UINT _tx_win32_current_virtual_core; + +UINT _tx_win32_smp_current_core_get(void); +void _tx_win32_thread_suspend(HANDLE thread_handle); +void _tx_win32_thread_resume(HANDLE thread_handle); +void _tx_win32_thread_sleep(ULONG milliseconds); +void _tx_win32_thread_yield(void); +void _tx_win32_semaphore_reset(HANDLE semaphore_handle); +LONG _tx_win32_thread_start_sequence_get(TX_THREAD *thread_ptr); +void _tx_win32_thread_run_signal(TX_THREAD *thread_ptr); +DWORD _tx_win32_wait_for_thread_run(TX_THREAD *thread_ptr); +void _tx_win32_thread_start_ack_signal(TX_THREAD *thread_ptr); +DWORD _tx_win32_wait_for_scheduler_event(void); +DWORD _tx_win32_wait_for_thread_run_semaphore(HANDLE semaphore_handle); +DWORD _tx_win32_wait_for_thread_start_semaphore(HANDLE semaphore_handle); +DWORD _tx_win32_wait_for_thread_start_ack(TX_THREAD *thread_ptr, LONG start_sequence); +DWORD _tx_win32_wait_for_isr_semaphore(void); +DWORD _tx_win32_wait_for_isr_rendezvous(void); +DWORD _tx_win32_wait_for_timer_object(void); +#ifdef TX_WIN32_PROFILE_ENABLE +void _tx_win32_profile_reset(void); +void _tx_win32_profile_report(CHAR *label); +void _tx_win32_profile_mark_run_signal(TX_THREAD *thread_ptr); +void _tx_win32_profile_mark_run_wake(TX_THREAD *thread_ptr); +void _tx_win32_profile_mark_start_ack(TX_THREAD *thread_ptr); +#endif + +#ifndef TX_WIN32_MEMORY_SIZE +#define TX_WIN32_MEMORY_SIZE 100000 +#endif + +#ifndef TX_TIMER_TICKS_PER_SECOND +#define TX_TIMER_TICKS_PER_SECOND 100UL +#endif + +#ifndef TX_WIN32_THREAD_STACK_SIZE +#define TX_WIN32_THREAD_STACK_SIZE 65536 +#endif + +#ifndef TX_TIMER_PERIODIC +#ifdef TX_WIN32_SLOW_TIMER +#define TX_TIMER_PERIODIC TX_WIN32_SLOW_TIMER +#else +#define TX_TIMER_PERIODIC 10 +#endif +#endif + +#ifndef TX_WIN32_ISR_PERIODIC +#if defined(CTEST) || defined(BATCH_TEST) +#define TX_WIN32_ISR_PERIODIC 1 +#else +#define TX_WIN32_ISR_PERIODIC TX_TIMER_PERIODIC +#endif +#endif + +#if (TX_WIN32_ISR_PERIODIC > TX_TIMER_PERIODIC) +#error "TX_WIN32_ISR_PERIODIC must not exceed TX_TIMER_PERIODIC" +#endif + +#if ((TX_TIMER_PERIODIC % TX_WIN32_ISR_PERIODIC) != 0) +#error "TX_TIMER_PERIODIC must be an integer multiple of TX_WIN32_ISR_PERIODIC" +#endif + +#define TX_WIN32_TIMER_INTERRUPTS_PER_TICK (TX_TIMER_PERIODIC / TX_WIN32_ISR_PERIODIC) + +#define TX_WIN32_PRIORITY_SCHEDULE THREAD_PRIORITY_NORMAL +#define TX_WIN32_PRIORITY_ISR THREAD_PRIORITY_HIGHEST +#define TX_WIN32_PRIORITY_USER_THREAD THREAD_PRIORITY_LOWEST + +#endif diff --git a/ports_smp/win64/vs_2022/readme_threadx.txt b/ports_smp/win64/vs_2022/readme_threadx.txt new file mode 100644 index 000000000..f37fc0567 --- /dev/null +++ b/ports_smp/win64/vs_2022/readme_threadx.txt @@ -0,0 +1,13 @@ +This port provides a Windows-hosted SMP simulation environment for ThreadX. + +The implementation models virtual ThreadX cores on top of Win64 host threads. +It is intended for validation and regression testing only. It is not a +production runtime port for deploying ThreadX applications on Windows. + +Key characteristics: +- ThreadX cores are virtual scheduler-owned mappings, not Windows CPU numbers. +- One Windows host thread is created for each ThreadX thread. +- A dedicated scheduler thread and a dedicated timer/ISR thread coordinate the + simulation. +- SuspendThread and ResumeThread are used only to emulate asynchronous + preemption in the simulator. diff --git a/ports_smp/win64/vs_2022/src/tx_initialize_low_level.c b/ports_smp/win64/vs_2022/src/tx_initialize_low_level.c new file mode 100644 index 000000000..f517fd6ba --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_initialize_low_level.c @@ -0,0 +1,1150 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.5). +// Some portions generated by GitHub Copilot (claude-sonnet-4.6). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +#include +#include + +#pragma comment (lib, "Winmm.lib") +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) +#pragma comment (lib, "Synchronization.lib") +#endif + +TX_WIN32_CRITICAL_SECTION _tx_win32_critical_section; +HANDLE _tx_win32_scheduler_event; +DWORD _tx_win32_scheduler_id; +ULONG _tx_win32_global_int_disabled_flag; +LARGE_INTEGER _tx_win32_time_stamp; +ULONG _tx_win32_system_error; +HANDLE _tx_win32_timer_handle; +HANDLE _tx_win32_timer_thread_handle; +DWORD _tx_win32_timer_thread_id; +HANDLE _tx_win32_isr_semaphore; +UINT _tx_win32_timer_waiting; +TX_THREAD_SMP_CORE_MAPPING _tx_win32_virtual_cores[TX_THREAD_SMP_MAX_CORES]; +__declspec(thread) int _tx_win32_threadx_thread = 0; +__declspec(thread) UINT _tx_win32_current_virtual_core = 0U; + +static VOID _tx_win32_timer_start(VOID); +static DWORD WINAPI _tx_win32_timer_thread_entry(LPVOID thread_input); +static UINT _tx_win32_smp_current_core_get_internal(DWORD thread_id); +static UINT _tx_win32_smp_thread_core_get(DWORD thread_id); + +#ifndef TX_WIN32_CONTENTION_PAUSE_COUNT +#define TX_WIN32_CONTENTION_PAUSE_COUNT 256U +#endif + +#ifdef TX_WIN32_PROFILE_ENABLE +typedef struct TX_WIN32_PROFILE_STRUCT +{ + ULONG64 tx_win32_profile_start_ticks; + ULONG64 tx_win32_profile_scheduler_wait_ticks; + ULONG64 tx_win32_profile_thread_run_wait_ticks; + ULONG64 tx_win32_profile_thread_start_wait_ticks; + ULONG64 tx_win32_profile_thread_start_ack_wait_ticks; + ULONG64 tx_win32_profile_run_signal_to_wake_ticks; + ULONG64 tx_win32_profile_run_wake_to_ack_ticks; + ULONG64 tx_win32_profile_isr_wait_ticks; + ULONG64 tx_win32_profile_isr_rendezvous_wait_ticks; + ULONG64 tx_win32_profile_timer_wait_ticks; + ULONG64 tx_win32_profile_suspend_ticks; + ULONG64 tx_win32_profile_resume_ticks; + ULONG64 tx_win32_profile_semaphore_reset_ticks; + ULONG64 tx_win32_profile_critical_section_spin_ticks; + ULONG64 tx_win32_profile_yield_ticks; + ULONG tx_win32_profile_scheduler_wait_count; + ULONG tx_win32_profile_thread_run_wait_count; + ULONG tx_win32_profile_thread_start_wait_count; + ULONG tx_win32_profile_thread_start_ack_wait_count; + ULONG tx_win32_profile_run_signal_to_wake_count; + ULONG tx_win32_profile_run_wake_to_ack_count; + ULONG tx_win32_profile_isr_wait_count; + ULONG tx_win32_profile_isr_rendezvous_wait_count; + ULONG tx_win32_profile_timer_wait_count; + ULONG tx_win32_profile_suspend_count; + ULONG tx_win32_profile_resume_count; + ULONG tx_win32_profile_semaphore_reset_count; + ULONG tx_win32_profile_semaphore_reset_loops; + ULONG tx_win32_profile_critical_section_spin_count; + ULONG tx_win32_profile_yield_count; +} TX_WIN32_PROFILE; + +static TX_WIN32_PROFILE _tx_win32_profile; +static LARGE_INTEGER _tx_win32_profile_frequency; + +static ULONG64 _tx_win32_profile_time_get(VOID); +static VOID _tx_win32_profile_accumulate(ULONG64 *ticks_total, ULONG *count_total, ULONG64 start_ticks); +static double _tx_win32_profile_ticks_to_ms(ULONG64 ticks); +#endif + +#ifdef TX_WIN32_DEBUG_ENABLE + +#ifndef TX_WIN32_DEBUG_EVENT_SIZE +#define TX_WIN32_DEBUG_EVENT_SIZE 400 +#endif + +typedef struct TX_WIN32_DEBUG_ENTRY_STRUCT +{ + char *tx_win32_debug_entry_action; + DWORD tx_win32_debug_entry_running_id; + UINT tx_win32_debug_entry_core; + LARGE_INTEGER tx_win32_debug_entry_timestamp; + char *tx_win32_debug_entry_file; + unsigned long tx_win32_debug_entry_line; + TX_WIN32_CRITICAL_SECTION tx_win32_debug_entry_critical_section; + TX_THREAD_SMP_PROTECT tx_win32_debug_protection; + unsigned long tx_win32_debug_entry_int_disabled_flag; + UINT tx_win32_debug_entry_preempt_disable; + ULONG tx_win32_debug_entry_system_state[TX_THREAD_SMP_MAX_CORES]; + TX_THREAD *tx_win32_debug_entry_current_thread[TX_THREAD_SMP_MAX_CORES]; + DWORD tx_win32_debug_entry_current_thread_id[TX_THREAD_SMP_MAX_CORES]; + TX_THREAD *tx_win32_debug_entry_execute_thread[TX_THREAD_SMP_MAX_CORES]; + DWORD tx_win32_debug_entry_execute_thread_id[TX_THREAD_SMP_MAX_CORES]; +} TX_WIN32_DEBUG_ENTRY; + +TX_WIN32_DEBUG_ENTRY _tx_win32_debug_entry_array[TX_WIN32_DEBUG_EVENT_SIZE]; +unsigned long _tx_win32_debug_entry_index = 0; + +void _tx_win32_debug_entry_insert(char *action, char *file, unsigned long line) +{ +UINT i; +DWORD thread_id; + + QueryPerformanceCounter((LARGE_INTEGER *) &_tx_win32_time_stamp); + thread_id = GetCurrentThreadId(); + + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_action = action; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_core = _tx_win32_smp_current_core_get_internal(thread_id); + if (_tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_core == TX_THREAD_SMP_MAX_CORES) + { + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_core = 0U; + } + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_timestamp = _tx_win32_time_stamp; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_file = file; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_line = line; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_protection = _tx_thread_smp_protection; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_preempt_disable = _tx_thread_preempt_disable; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_critical_section = _tx_win32_critical_section; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_int_disabled_flag = _tx_win32_global_int_disabled_flag; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_running_id = thread_id; + + for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) + { + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_system_state[i] = _tx_thread_system_state[i]; + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread[i] = _tx_thread_current_ptr[i]; + if (_tx_thread_current_ptr[i] != TX_NULL) + { + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id[i] = + _tx_thread_current_ptr[i] -> tx_thread_win32_thread_id; + } + else + { + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id[i] = 0U; + } + + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread[i] = _tx_thread_execute_ptr[i]; + if (_tx_thread_execute_ptr[i] != TX_NULL) + { + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id[i] = + _tx_thread_execute_ptr[i] -> tx_thread_win32_thread_id; + } + else + { + _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id[i] = 0U; + } + } + + _tx_win32_debug_entry_index++; + if (_tx_win32_debug_entry_index >= TX_WIN32_DEBUG_EVENT_SIZE) + { + _tx_win32_debug_entry_index = 0U; + } +} + +#endif + + +void _tx_timer_interrupt(void); +VOID _tx_thread_context_save(VOID); +VOID _tx_thread_context_restore(VOID); +VOID _tx_win32_timer_interrupt(VOID); +#if defined(CTEST) || defined(BATCH_TEST) +VOID test_interrupt_dispatch(VOID); +extern VOID (*test_isr_dispatch)(void); +#if (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) +static VOID _tx_win32_timer_tick_process(VOID); +static UINT _tx_win32_timer_fast_active; +static UINT _tx_win32_timer_fast_count; +#endif +#endif + + +VOID _tx_initialize_low_level(VOID) +{ +UINT i; +TIMECAPS tc; +UINT timer_resolution; + + _tx_initialize_unused_memory = malloc(TX_WIN32_MEMORY_SIZE); + _tx_win32_scheduler_id = GetCurrentThreadId(); + + _tx_win32_critical_section.tx_win32_critical_section_mutex_handle = CreateMutex(NULL, FALSE, NULL); + _tx_win32_critical_section.tx_win32_critical_section_nested_count = 0U; + _tx_win32_critical_section.tx_win32_critical_section_owner = 0U; + if (_tx_win32_critical_section.tx_win32_critical_section_mutex_handle == NULL) + { + printf("ThreadX SMP Win64 error creating critical section mutex!\n"); + while (1) + { + } + } + + _tx_win32_scheduler_event = CreateEvent(NULL, FALSE, FALSE, NULL); + _tx_win32_isr_semaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); + if ((_tx_win32_scheduler_event == NULL) || + (_tx_win32_isr_semaphore == NULL)) + { + printf("ThreadX SMP Win64 error creating semaphores!\n"); + while (1) + { + } + } + + for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) + { + _tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread_handle = NULL; + _tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread_id = 0U; + _tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread = TX_NULL; + } + + _tx_win32_global_int_disabled_flag = TX_FALSE; + _tx_win32_timer_waiting = 0U; +#if (defined(CTEST) || defined(BATCH_TEST)) && (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) + _tx_win32_timer_fast_active = TX_FALSE; + _tx_win32_timer_fast_count = 0U; +#endif + +#ifdef TX_WIN32_PROFILE_ENABLE + if (_tx_win32_profile_frequency.QuadPart == 0) + { + QueryPerformanceFrequency(&_tx_win32_profile_frequency); + } + _tx_win32_profile_reset(); +#endif + + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) + { + printf("ThreadX SMP Win64 error querying timer resolution!\n"); + while (1) + { + } + } + + timer_resolution = (UINT) min(max(tc.wPeriodMin, TX_WIN32_ISR_PERIODIC), tc.wPeriodMax); + if (timeBeginPeriod(timer_resolution) != TIMERR_NOERROR) + { + printf("ThreadX SMP Win64 error configuring timer resolution!\n"); + while (1) + { + } + } + +#if (TX_WIN32_USE_HIGH_RESOLUTION_TIMER != 0) + _tx_win32_timer_handle = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); + if (_tx_win32_timer_handle == NULL) +#endif + { + _tx_win32_timer_handle = CreateWaitableTimer(NULL, FALSE, NULL); + } + + if (_tx_win32_timer_handle == NULL) + { + printf("ThreadX SMP Win64 error creating timer handle!\n"); + while (1) + { + } + } + + _tx_win32_timer_thread_handle = CreateThread(NULL, 0, _tx_win32_timer_thread_entry, NULL, 0, &_tx_win32_timer_thread_id); + if (_tx_win32_timer_thread_handle == NULL) + { + printf("ThreadX SMP Win64 error creating timer thread!\n"); + while (1) + { + } + } + + SetThreadPriority(GetCurrentThread(), TX_WIN32_PRIORITY_SCHEDULE); + SetThreadPriority(_tx_win32_timer_thread_handle, TX_WIN32_PRIORITY_ISR); +} + +#ifdef TX_WIN32_PROFILE_ENABLE +static ULONG64 _tx_win32_profile_time_get(VOID) +{ +LARGE_INTEGER current_time; + + QueryPerformanceCounter(¤t_time); + return((ULONG64) current_time.QuadPart); +} + + +static VOID _tx_win32_profile_accumulate(ULONG64 *ticks_total, ULONG *count_total, ULONG64 start_ticks) +{ +ULONG64 end_ticks; + + end_ticks = _tx_win32_profile_time_get(); + *ticks_total = *ticks_total + (end_ticks - start_ticks); + *count_total = *count_total + 1UL; +} + + +static double _tx_win32_profile_ticks_to_ms(ULONG64 ticks) +{ +double frequency; + + frequency = (double) _tx_win32_profile_frequency.QuadPart; + if (frequency == 0.0) + { + return(0.0); + } + + return((((double) ticks) * 1000.0) / frequency); +} + + +void _tx_win32_profile_reset(VOID) +{ + TX_MEMSET(&_tx_win32_profile, 0, sizeof(TX_WIN32_PROFILE)); + _tx_win32_profile.tx_win32_profile_start_ticks = _tx_win32_profile_time_get(); +} + + +void _tx_win32_profile_report(CHAR *label) +{ +ULONG64 elapsed_ticks; + + elapsed_ticks = _tx_win32_profile_time_get() - _tx_win32_profile.tx_win32_profile_start_ticks; + + printf("**** Win64 SMP Profile (%s) elapsed_ms=%.3f\n", + label, + _tx_win32_profile_ticks_to_ms(elapsed_ticks)); + printf("**** waits: scheduler=%lu/%.3fms run=%lu/%.3fms start=%lu/%.3fms start_ack=%lu/%.3fms sig_wake=%lu/%.3fms wake_ack=%lu/%.3fms isr=%lu/%.3fms isr_rdv=%lu/%.3fms timer=%lu/%.3fms\n", + _tx_win32_profile.tx_win32_profile_scheduler_wait_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_scheduler_wait_ticks), + _tx_win32_profile.tx_win32_profile_thread_run_wait_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_thread_run_wait_ticks), + _tx_win32_profile.tx_win32_profile_thread_start_wait_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_thread_start_wait_ticks), + _tx_win32_profile.tx_win32_profile_thread_start_ack_wait_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_thread_start_ack_wait_ticks), + _tx_win32_profile.tx_win32_profile_run_signal_to_wake_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_run_signal_to_wake_ticks), + _tx_win32_profile.tx_win32_profile_run_wake_to_ack_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_run_wake_to_ack_ticks), + _tx_win32_profile.tx_win32_profile_isr_wait_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_isr_wait_ticks), + _tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_ticks), + _tx_win32_profile.tx_win32_profile_timer_wait_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_timer_wait_ticks)); + printf("**** host ops: suspend=%lu/%.3fms resume=%lu/%.3fms reset=%lu/%.3fms reset_loops=%lu cs_spin=%lu/%.3fms yield=%lu/%.3fms\n", + _tx_win32_profile.tx_win32_profile_suspend_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_suspend_ticks), + _tx_win32_profile.tx_win32_profile_resume_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_resume_ticks), + _tx_win32_profile.tx_win32_profile_semaphore_reset_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_semaphore_reset_ticks), + _tx_win32_profile.tx_win32_profile_semaphore_reset_loops, + _tx_win32_profile.tx_win32_profile_critical_section_spin_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_critical_section_spin_ticks), + _tx_win32_profile.tx_win32_profile_yield_count, + _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_yield_ticks)); +} + + +void _tx_win32_profile_mark_run_signal(TX_THREAD *thread_ptr) +{ + thread_ptr -> tx_thread_win32_profile_run_signal_ticks = _tx_win32_profile_time_get(); + thread_ptr -> tx_thread_win32_profile_run_wake_ticks = 0ULL; +} + + +void _tx_win32_profile_mark_run_wake(TX_THREAD *thread_ptr) +{ +ULONG64 current_ticks; +ULONG64 signal_ticks; + + current_ticks = _tx_win32_profile_time_get(); + signal_ticks = thread_ptr -> tx_thread_win32_profile_run_signal_ticks; + + if (signal_ticks != 0ULL) + { + _tx_win32_profile.tx_win32_profile_run_signal_to_wake_ticks = + _tx_win32_profile.tx_win32_profile_run_signal_to_wake_ticks + (current_ticks - signal_ticks); + _tx_win32_profile.tx_win32_profile_run_signal_to_wake_count++; + } + + thread_ptr -> tx_thread_win32_profile_run_signal_ticks = 0ULL; + thread_ptr -> tx_thread_win32_profile_run_wake_ticks = current_ticks; +} + + +void _tx_win32_profile_mark_start_ack(TX_THREAD *thread_ptr) +{ +ULONG64 current_ticks; +ULONG64 wake_ticks; + + current_ticks = _tx_win32_profile_time_get(); + wake_ticks = thread_ptr -> tx_thread_win32_profile_run_wake_ticks; + + if (wake_ticks != 0ULL) + { + _tx_win32_profile.tx_win32_profile_run_wake_to_ack_ticks = + _tx_win32_profile.tx_win32_profile_run_wake_to_ack_ticks + (current_ticks - wake_ticks); + _tx_win32_profile.tx_win32_profile_run_wake_to_ack_count++; + } + + thread_ptr -> tx_thread_win32_profile_run_wake_ticks = 0ULL; +} +#endif + + +void _tx_initialize_start_interrupts(void) +{ + _tx_win32_timer_start(); +} + + +void _tx_win32_critical_section_obtain(TX_WIN32_CRITICAL_SECTION *critical_section) +{ +DWORD thread_id; +LONG previous_owner; +UINT contention_count; +TX_THREAD *current_thread; +UINT core_scan; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; +#endif + + thread_id = GetCurrentThreadId(); + + if (critical_section -> tx_win32_critical_section_owner == thread_id) + { + critical_section -> tx_win32_critical_section_nested_count++; + } + else + { + /* If this is a ThreadX application thread, signal that it is about to + * spin waiting for the critical section. context_save checks this flag + * before calling SuspendThread(): a thread that is merely blocked on the + * spinlock does not need to be OS-suspended — it cannot execute protected + * code anyway, and will proceed automatically once the CS is released. + * The flag is cleared below once the CS has been acquired. + * + * Identify the calling thread by scanning _tx_win32_virtual_cores for a + * matching OS thread ID. As a fast path, check the TLS-cached virtual core + * index first; fall back to a full scan only when the TLS value is stale + * (e.g., after a type-1 scheduler hand-off moves the thread to a new core). + * Worst case: the entry is momentarily absent and current_thread stays + * TX_NULL, which simply skips the mutex_access optimisation this cycle. */ + current_thread = TX_NULL; + if (_tx_win32_threadx_thread != 0) + { + core_scan = _tx_win32_current_virtual_core; + if ((core_scan < TX_THREAD_SMP_MAX_CORES) && + (_tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread_id == thread_id)) + { + current_thread = _tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread; + } + else + { + for (core_scan = 0U; core_scan < TX_THREAD_SMP_MAX_CORES; core_scan++) + { + if (_tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread_id == thread_id) + { + current_thread = _tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread; + break; + } + } + } + if (current_thread != TX_NULL) + { + current_thread -> tx_thread_win32_mutex_access = TX_TRUE; + } + } + + contention_count = 0U; +#ifdef TX_WIN32_PROFILE_ENABLE + start_ticks = _tx_win32_profile_time_get(); +#endif + do + { + previous_owner = InterlockedCompareExchange((LONG *) &(critical_section -> tx_win32_critical_section_owner), (LONG) thread_id, 0L); + + if (previous_owner != 0L) + { +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile.tx_win32_profile_critical_section_spin_count++; +#endif + contention_count++; + if (contention_count < TX_WIN32_CONTENTION_PAUSE_COUNT) + { + YieldProcessor(); + } + else + { + contention_count = 0U; + _tx_win32_thread_yield(); + } + } + } while (previous_owner != 0L); + +#ifdef TX_WIN32_PROFILE_ENABLE + if (critical_section -> tx_win32_critical_section_owner == thread_id) + { + _tx_win32_profile.tx_win32_profile_critical_section_spin_ticks = + _tx_win32_profile.tx_win32_profile_critical_section_spin_ticks + + (_tx_win32_profile_time_get() - start_ticks); + } +#endif + /* CS acquired; clear the spinning flag so the next timer ISR will see + * the thread as normally running. The thread is now inside the CS body + * and the ISR cannot reach context_save until we release the CS. */ + if (current_thread != TX_NULL) + { + current_thread -> tx_thread_win32_mutex_access = TX_FALSE; + } + + critical_section -> tx_win32_critical_section_nested_count = 1U; + } +} + + +void _tx_win32_critical_section_release(TX_WIN32_CRITICAL_SECTION *critical_section) +{ +DWORD thread_id; + + thread_id = GetCurrentThreadId(); + + if (critical_section -> tx_win32_critical_section_owner == thread_id) + { + if (critical_section -> tx_win32_critical_section_nested_count != 0U) + { + critical_section -> tx_win32_critical_section_nested_count--; + + if (critical_section -> tx_win32_critical_section_nested_count == 0U) + { + InterlockedExchange((LONG *) &(critical_section -> tx_win32_critical_section_owner), 0L); + } + } + } + else + { + _tx_win32_system_error++; + } +} + + +void _tx_win32_critical_section_release_all(TX_WIN32_CRITICAL_SECTION *critical_section) +{ +DWORD thread_id; + + thread_id = GetCurrentThreadId(); + + if (critical_section -> tx_win32_critical_section_owner == thread_id) + { + if (critical_section -> tx_win32_critical_section_nested_count != 0U) + { + critical_section -> tx_win32_critical_section_nested_count = 0U; + InterlockedExchange((LONG *) &(critical_section -> tx_win32_critical_section_owner), 0L); + } + } + else + { + _tx_win32_system_error++; + } +} + + +UINT _tx_win32_smp_current_core_get(void) +{ +UINT core; +DWORD thread_id; +UINT critical_section_owned; + + thread_id = GetCurrentThreadId(); + critical_section_owned = (UINT) (_tx_win32_critical_section.tx_win32_critical_section_owner == thread_id); + + if (critical_section_owned == TX_FALSE) + { + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + } + + if (_tx_win32_threadx_thread != 0) + { + core = _tx_win32_smp_thread_core_get(thread_id); + + if (core == TX_THREAD_SMP_MAX_CORES) + { + core = _tx_win32_current_virtual_core; + } + else + { + _tx_win32_current_virtual_core = core; + } + } + else + { + core = _tx_win32_smp_current_core_get_internal(thread_id); + if (core == TX_THREAD_SMP_MAX_CORES) + { + core = 0U; + } + } + + if (critical_section_owned == TX_FALSE) + { + _tx_win32_critical_section_release(&_tx_win32_critical_section); + } + + return(core); +} + + +static UINT _tx_win32_smp_thread_core_get(DWORD thread_id) +{ +UINT core; +UINT i; +TX_THREAD *thread_ptr; + + core = TX_THREAD_SMP_MAX_CORES; + + for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) + { + if (_tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread_id == thread_id) + { + core = i; + break; + } + } + + if (core == TX_THREAD_SMP_MAX_CORES) + { + for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) + { + thread_ptr = _tx_thread_current_ptr[i]; + if ((thread_ptr != TX_NULL) && (thread_ptr -> tx_thread_win32_thread_id == thread_id)) + { + core = i; + break; + } + } + } + + return(core); +} + + +static UINT _tx_win32_smp_current_core_get_internal(DWORD thread_id) +{ +UINT core; +UINT i; +TX_THREAD *thread_ptr; + + core = _tx_win32_smp_thread_core_get(thread_id); + + if (core == TX_THREAD_SMP_MAX_CORES) + { + for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) + { + thread_ptr = _tx_thread_execute_ptr[i]; + if ((thread_ptr != TX_NULL) && (thread_ptr -> tx_thread_win32_thread_id == thread_id)) + { + core = i; + break; + } + } + } + + return(core); +} + + +void _tx_win32_thread_suspend(HANDLE thread_handle) +{ +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + if (SuspendThread(thread_handle) == (DWORD) -1) + { + _tx_win32_system_error++; + while (1) + { + } + } +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_suspend_ticks, + &_tx_win32_profile.tx_win32_profile_suspend_count, + start_ticks); +#endif +} + + +void _tx_win32_thread_resume(HANDLE thread_handle) +{ + DWORD suspend_count; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + do + { + suspend_count = ResumeThread(thread_handle); + if (suspend_count == (DWORD) -1) + { + _tx_win32_system_error++; + while (1) + { + } + } + } while (suspend_count > 1U); +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_resume_ticks, + &_tx_win32_profile.tx_win32_profile_resume_count, + start_ticks); +#endif +} + + +void _tx_win32_thread_sleep(ULONG milliseconds) +{ + if (milliseconds == 0U) + { + milliseconds = 1U; + } + + Sleep((DWORD) milliseconds); +} + + +void _tx_win32_thread_yield(VOID) +{ +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + if (SwitchToThread() == 0) + { + Sleep(0); + } + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_yield_ticks, + &_tx_win32_profile.tx_win32_profile_yield_count, + start_ticks); +#endif +} + + +void _tx_win32_semaphore_reset(HANDLE semaphore_handle) +{ +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + while (WaitForSingleObject(semaphore_handle, 0) == WAIT_OBJECT_0) + { +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile.tx_win32_profile_semaphore_reset_loops++; +#endif + } + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_semaphore_reset_ticks, + &_tx_win32_profile.tx_win32_profile_semaphore_reset_count, + start_ticks); +#endif +} + + +LONG _tx_win32_thread_start_sequence_get(TX_THREAD *thread_ptr) +{ +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) + return(thread_ptr -> tx_thread_win32_start_sequence); +#else + TX_PARAMETER_NOT_USED(thread_ptr); + return(0L); +#endif +} + + +void _tx_win32_thread_run_signal(TX_THREAD *thread_ptr) +{ +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) + (void) InterlockedIncrement(&(thread_ptr -> tx_thread_win32_run_sequence)); + WakeByAddressSingle(&(thread_ptr -> tx_thread_win32_run_sequence)); +#else + ReleaseSemaphore(thread_ptr -> tx_thread_win32_thread_run_semaphore, 1, NULL); +#endif +} + + +DWORD _tx_win32_wait_for_thread_run(TX_THREAD *thread_ptr) +{ +DWORD wait_status; +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) +LONG observed_sequence; +LONG current_sequence; +#endif +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) + wait_status = WAIT_OBJECT_0; + observed_sequence = thread_ptr -> tx_thread_win32_run_sequence_seen; + current_sequence = thread_ptr -> tx_thread_win32_run_sequence; + + while (current_sequence == observed_sequence) + { + if (WaitOnAddress(&(thread_ptr -> tx_thread_win32_run_sequence), + &observed_sequence, + sizeof(thread_ptr -> tx_thread_win32_run_sequence), + INFINITE) == 0) + { + _tx_win32_system_error++; + wait_status = WAIT_FAILED; + break; + } + + current_sequence = thread_ptr -> tx_thread_win32_run_sequence; + } + + if (wait_status == WAIT_OBJECT_0) + { + thread_ptr -> tx_thread_win32_run_sequence_seen = current_sequence; + } +#else + wait_status = WaitForSingleObject(thread_ptr -> tx_thread_win32_thread_run_semaphore, INFINITE); +#endif + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_run_wait_ticks, + &_tx_win32_profile.tx_win32_profile_thread_run_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +void _tx_win32_thread_start_ack_signal(TX_THREAD *thread_ptr) +{ +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) + (void) InterlockedIncrement(&(thread_ptr -> tx_thread_win32_start_sequence)); + WakeByAddressSingle(&(thread_ptr -> tx_thread_win32_start_sequence)); +#else + ReleaseSemaphore(thread_ptr -> tx_thread_win32_thread_start_semaphore, 1, NULL); +#endif +} + + +DWORD _tx_win32_wait_for_scheduler_event(VOID) +{ +DWORD wait_status; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + /* Use a 2 ms timeout (matching the Linux SMP port's sem_timedwait interval) so that + * a missed SetEvent() does not stall the scheduler indefinitely and so that the + * timer-tick phase varies slightly between iterations, preventing a systematic + * resonance where the ISR always fires outside the _tx_thread_preempt_disable + * window (which would make tests like threadx_thread_wait_abort_and_isr_test + * take an unreasonably long time to complete). */ + wait_status = WaitForSingleObject(_tx_win32_scheduler_event, 2U); + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_scheduler_wait_ticks, + &_tx_win32_profile.tx_win32_profile_scheduler_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +DWORD _tx_win32_wait_for_thread_run_semaphore(HANDLE semaphore_handle) +{ +DWORD wait_status; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + wait_status = WaitForSingleObject(semaphore_handle, INFINITE); + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_run_wait_ticks, + &_tx_win32_profile.tx_win32_profile_thread_run_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +DWORD _tx_win32_wait_for_thread_start_semaphore(HANDLE semaphore_handle) +{ +DWORD wait_status; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + wait_status = WaitForSingleObject(semaphore_handle, INFINITE); + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_start_wait_ticks, + &_tx_win32_profile.tx_win32_profile_thread_start_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +DWORD _tx_win32_wait_for_thread_start_ack(TX_THREAD *thread_ptr, LONG start_sequence) +{ +DWORD wait_status; +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) +LONG current_sequence; +#endif +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) + wait_status = WAIT_OBJECT_0; + current_sequence = thread_ptr -> tx_thread_win32_start_sequence; + + while (current_sequence == start_sequence) + { + if (WaitOnAddress(&(thread_ptr -> tx_thread_win32_start_sequence), + &start_sequence, + sizeof(thread_ptr -> tx_thread_win32_start_sequence), + INFINITE) == 0) + { + _tx_win32_system_error++; + wait_status = WAIT_FAILED; + break; + } + + current_sequence = thread_ptr -> tx_thread_win32_start_sequence; + } +#else + TX_PARAMETER_NOT_USED(start_sequence); + wait_status = WaitForSingleObject(thread_ptr -> tx_thread_win32_thread_start_semaphore, INFINITE); +#endif + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_start_ack_wait_ticks, + &_tx_win32_profile.tx_win32_profile_thread_start_ack_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +DWORD _tx_win32_wait_for_isr_semaphore(VOID) +{ +DWORD wait_status; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + wait_status = WaitForSingleObject(_tx_win32_isr_semaphore, INFINITE); + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_isr_wait_ticks, + &_tx_win32_profile.tx_win32_profile_isr_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +DWORD _tx_win32_wait_for_isr_rendezvous(VOID) +{ +DWORD wait_status; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + wait_status = WaitForSingleObject(_tx_win32_isr_semaphore, INFINITE); + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_ticks, + &_tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +DWORD _tx_win32_wait_for_timer_object(VOID) +{ +DWORD wait_status; +#ifdef TX_WIN32_PROFILE_ENABLE +ULONG64 start_ticks; + + start_ticks = _tx_win32_profile_time_get(); +#endif + + wait_status = WaitForSingleObject(_tx_win32_timer_handle, INFINITE); + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_timer_wait_ticks, + &_tx_win32_profile.tx_win32_profile_timer_wait_count, + start_ticks); +#endif + + return(wait_status); +} + + +static DWORD WINAPI _tx_win32_timer_thread_entry(LPVOID thread_input) +{ + TX_PARAMETER_NOT_USED(thread_input); + + while (1) + { + _tx_win32_wait_for_timer_object(); + _tx_win32_timer_interrupt(); + _tx_win32_timer_start(); + } +} + + +VOID _tx_win32_timer_interrupt(VOID) +{ + _tx_thread_context_save(); +#if defined(CTEST) || defined(BATCH_TEST) + test_interrupt_dispatch(); +#if (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) + _tx_win32_timer_tick_process(); +#else + _tx_timer_interrupt(); +#endif +#else + _tx_timer_interrupt(); +#endif + _tx_thread_context_restore(); +} + + +#if (defined(CTEST) || defined(BATCH_TEST)) && (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) +static VOID _tx_win32_timer_tick_process(VOID) +{ + if (test_isr_dispatch != TX_NULL) + { + if (_tx_win32_timer_fast_active == TX_FALSE) + { + _tx_win32_timer_fast_active = TX_TRUE; + _tx_win32_timer_fast_count = 0U; + } + else + { + _tx_win32_timer_fast_count++; + if (_tx_win32_timer_fast_count < TX_WIN32_TIMER_INTERRUPTS_PER_TICK) + { + return; + } + + _tx_win32_timer_fast_count = 0U; + } + } + else + { + _tx_win32_timer_fast_active = TX_FALSE; + _tx_win32_timer_fast_count = 0U; + } + + _tx_timer_interrupt(); +} +#endif + + +static VOID _tx_win32_timer_start(VOID) +{ +LARGE_INTEGER due_time; +LONGLONG timer_period; + +#if (defined(CTEST) || defined(BATCH_TEST)) && (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) + if (test_isr_dispatch != TX_NULL) + { + timer_period = (LONGLONG) TX_WIN32_ISR_PERIODIC; + } + else +#endif + { + timer_period = (LONGLONG) TX_TIMER_PERIODIC; + } + + due_time.QuadPart = -(timer_period * 10000LL); +#if (TX_WIN32_USE_HIGH_RESOLUTION_TIMER != 0) + if (SetWaitableTimerEx(_tx_win32_timer_handle, &due_time, 0, NULL, NULL, NULL, 0) == 0) +#else + if (SetWaitableTimer(_tx_win32_timer_handle, &due_time, 0, NULL, NULL, FALSE) == 0) +#endif + { + printf("ThreadX SMP Win64 error starting timer!\n"); + while (1) + { + } + } +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_context_restore.c b/ports_smp/win64/vs_2022/src/tx_thread_context_restore.c new file mode 100644 index 000000000..d1b74c383 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_context_restore.c @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.5). +// Some portions generated by GitHub Copilot (claude-sonnet-4.6). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + + +VOID _tx_thread_context_restore(VOID) +{ +TX_THREAD *current_thread; +TX_THREAD *execute_thread; + + _tx_win32_debug_entry_insert("CONTEXT_RESTORE", __FILE__, __LINE__); + + _tx_thread_system_state[0]--; + current_thread = _tx_thread_current_ptr[0]; + execute_thread = _tx_thread_execute_ptr[0]; + + if (_tx_thread_system_state[0] == 0UL) + { + if (current_thread != TX_NULL) + { + if ((_tx_thread_preempt_disable == 0U) && (current_thread != execute_thread)) + { + /* If context_save skipped SuspendThread because the thread was spinning + * on the Win32 CS (suspension_type == 4), suspend it now. The ISR still + * holds the CS so the spinning thread cannot make forward progress, making + * SuspendThread safe to call here. mutex_access remains TRUE; the thread + * will clear it naturally when it acquires the CS after being resumed. */ + if (current_thread -> tx_thread_win32_suspension_type == 4U) + { + _tx_win32_thread_suspend(current_thread -> tx_thread_win32_thread_handle); + } + + current_thread -> tx_thread_win32_suspension_type = 1U; + + if (_tx_timer_time_slice[0] != 0U) + { + current_thread -> tx_thread_time_slice = _tx_timer_time_slice[0]; + _tx_timer_time_slice[0] = 0U; + } + + _tx_thread_current_ptr[0] = TX_NULL; + _tx_win32_virtual_cores[0].tx_thread_smp_core_mapping_thread_handle = NULL; + _tx_win32_virtual_cores[0].tx_thread_smp_core_mapping_thread_id = 0U; + _tx_win32_virtual_cores[0].tx_thread_smp_core_mapping_thread = TX_NULL; + current_thread -> tx_thread_smp_core_control = 1U; + + /* Signal the scheduler to assign the next execute_thread. Wait for the + * scheduler to signal isr_semaphore before returning: the scheduler holds + * the CS while waiting for start_ack from the execute_thread, which forces + * any thread calling TX_DISABLE to spin with mutex_access=TRUE while + * preempt_disable may be non-zero — exactly the window needed to satisfy + * the condition in tests such as wait_abort_and_isr. */ + _tx_win32_timer_waiting = 1U; + MemoryBarrier(); + if (SetEvent(_tx_win32_scheduler_event) == 0) + { + _tx_win32_system_error++; + } + + if ((execute_thread != TX_NULL) && + (execute_thread -> tx_thread_win32_suspension_type == 2U)) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + _tx_win32_wait_for_isr_rendezvous(); + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + _tx_win32_semaphore_reset(_tx_win32_isr_semaphore); + } + + _tx_win32_timer_waiting = 0U; + } + else + { + if ((current_thread -> tx_thread_win32_suspension_type == 3U) || + (current_thread -> tx_thread_win32_suspension_type == 4U)) + { + /* context_save did not call SuspendThread (type 3: preempt_disable≠0; + * type 4: thread was spinning on Win32 CS). Clear the flag; do not + * issue a matching ResumeThread. The thread will continue + * automatically once the ISR releases the Win32 critical section. */ + current_thread -> tx_thread_win32_suspension_type = 0U; + } + else + { + /* suspension_type 0: context_save called SuspendThread. Always issue + * the matching ResumeThread here, regardless of mutex_access. + * + * Do NOT gate this on mutex_access: a thread on a different virtual core + * that happens to share the same stale TLS slot can momentarily stamp + * mutex_access = TRUE on this thread's struct while it is OS-suspended, + * which would otherwise cause context_restore to skip ResumeThread and + * leave the thread permanently suspended (deadlock). */ + _tx_win32_thread_resume(current_thread -> tx_thread_win32_thread_handle); + } + } + } + else if (execute_thread != TX_NULL) + { + _tx_win32_timer_waiting = 1U; + MemoryBarrier(); + if (SetEvent(_tx_win32_scheduler_event) == 0) + { + _tx_win32_system_error++; + } + + if (execute_thread -> tx_thread_win32_suspension_type == 2U) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + _tx_win32_wait_for_isr_rendezvous(); + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + _tx_win32_semaphore_reset(_tx_win32_isr_semaphore); + } + + _tx_win32_timer_waiting = 0U; + } + } + + _tx_thread_smp_unprotect(TX_INT_ENABLE); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_context_save.c b/ports_smp/win64/vs_2022/src/tx_thread_context_save.c new file mode 100644 index 000000000..89d6cd75c --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_context_save.c @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). +// Some portions generated by GitHub Copilot (claude-sonnet-4.6). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +VOID _tx_thread_context_save(VOID) +{ +TX_THREAD *thread_ptr; +UINT interrupt_posture; + + interrupt_posture = _tx_thread_smp_protect(); + if (interrupt_posture != TX_FALSE) + { + _tx_win32_system_error++; + } + + _tx_win32_debug_entry_insert("CONTEXT_SAVE", __FILE__, __LINE__); + + thread_ptr = _tx_thread_current_ptr[0]; + + if ((thread_ptr != TX_NULL) && (_tx_thread_system_state[0] == 0UL)) + { + if (thread_ptr -> tx_thread_win32_mutex_access == TX_FALSE) + { + if (_tx_thread_preempt_disable == 0U) + { + /* Thread is not in a preempt-disabled section; suspend it normally so + * context_restore can preempt it if a higher-priority thread is ready. */ + _tx_win32_thread_suspend(thread_ptr -> tx_thread_win32_thread_handle); + _tx_win32_debug_entry_insert("CONTEXT_SAVE-suspend_thread", __FILE__, __LINE__); + } + else + { + /* Thread holds the SMP preemption lock (_tx_thread_preempt_disable != 0). + * Issuing SuspendThread/ResumeThread here would cost ~100 µs for zero + * benefit: context_restore will skip preemption anyway because + * _tx_thread_preempt_disable is still non-zero when it runs (the ISR + * cannot lower the count below its value at ISR entry while it holds the + * Win32 critical section). Flag the thread so context_restore knows not + * to issue a matching ResumeThread. + * + * MISRA C 2012 Rule 10.3 deviation: suspension_type is a UINT field used + * as a small state tag; value 3 is a port-local extension of the existing + * 0/1/2 enumeration. */ + thread_ptr -> tx_thread_win32_suspension_type = 3U; + } + } + else + { + /* Thread is spinning on the Win32 critical section (mutex_access TRUE). + * It is blocked waiting to acquire the CS and cannot execute any protected + * ThreadX code, so SuspendThread is unnecessary. Tag the thread with + * suspension_type 4 so context_restore handles it correctly: + * - no-preemption path: just clear the flag (no ResumeThread). + * - preemption path: call SuspendThread retrospectively and proceed. + * + * MISRA C 2012 Rule 10.3 deviation: value 4 is a port-local extension. */ + thread_ptr -> tx_thread_win32_suspension_type = 4U; + } + } + + _tx_thread_system_state[0]++; +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_interrupt_control.c b/ports_smp/win64/vs_2022/src/tx_thread_interrupt_control.c new file mode 100644 index 000000000..b596baeb4 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_interrupt_control.c @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" + +UINT _tx_thread_interrupt_disable(void) +{ +UINT previous_value; + + previous_value = _tx_thread_interrupt_control(TX_INT_DISABLE); + return(previous_value); +} + + +VOID _tx_thread_interrupt_restore(UINT previous_posture) +{ + previous_posture = _tx_thread_interrupt_control(previous_posture); + TX_PARAMETER_NOT_USED(previous_posture); +} + + +UINT _tx_thread_interrupt_control(UINT new_posture) +{ +UINT old_posture; +TX_THREAD *thread_ptr; +DWORD thread_id; +UINT core; + + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + +#ifdef TX_WIN32_DEBUG_ENABLE + if (new_posture == TX_INT_ENABLE) + { + _tx_win32_debug_entry_insert("RESTORE", __FILE__, __LINE__); + } + else + { + _tx_win32_debug_entry_insert("DISABLE", __FILE__, __LINE__); + } +#endif + + thread_id = GetCurrentThreadId(); + core = _tx_thread_smp_core_get(); + thread_ptr = _tx_thread_current_ptr[core]; + + if ((_tx_win32_threadx_thread != 0) && + ((thread_ptr == TX_NULL) || (thread_ptr -> tx_thread_win32_thread_id != thread_id))) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + ExitThread(0); + } + + if (_tx_win32_critical_section.tx_win32_critical_section_nested_count == 1U) + { + old_posture = TX_INT_ENABLE; + } + else + { + old_posture = TX_INT_DISABLE; + } + + if (_tx_thread_system_state[core] != 0UL) + { + if (new_posture == TX_INT_ENABLE) + { + _tx_win32_global_int_disabled_flag = TX_FALSE; + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + } + else if (new_posture == TX_INT_DISABLE) + { + _tx_win32_global_int_disabled_flag = TX_TRUE; + } + } + else if (thread_ptr != TX_NULL) + { + if (new_posture == TX_INT_ENABLE) + { + thread_ptr -> tx_thread_win32_int_disabled_flag = TX_FALSE; + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + } + else if (new_posture == TX_INT_DISABLE) + { + thread_ptr -> tx_thread_win32_int_disabled_flag = TX_TRUE; + } + } + + return(old_posture); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_schedule.c b/ports_smp/win64/vs_2022/src/tx_thread_schedule.c new file mode 100644 index 000000000..f33eb7b07 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_schedule.c @@ -0,0 +1,261 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.5). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +static VOID _tx_win32_thread_cleanup(TX_THREAD *thread_ptr); + +VOID _tx_thread_schedule(VOID) +{ +UINT core; +UINT pending_work; +TX_THREAD *current_thread; +TX_THREAD *execute_thread; +UCHAR preempt_retry; +DWORD wait_status; +LONG start_sequence; + + preempt_retry = TX_FALSE; + + while (1) + { + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + if (_tx_win32_global_int_disabled_flag != TX_FALSE) + { + _tx_win32_system_error++; + } + + _tx_win32_debug_entry_insert("SCHEDULE-wake_up", __FILE__, __LINE__); + + for (core = 0U; core < TX_THREAD_SMP_MAX_CORES; core++) + { + current_thread = _tx_thread_current_ptr[core]; + + if ((current_thread != TX_NULL) && (current_thread -> tx_thread_win32_deferred_preempt != TX_FALSE)) + { + if (_tx_thread_preempt_disable != 0U) + { + preempt_retry = TX_TRUE; + continue; + } + + if (current_thread -> tx_thread_win32_mutex_access != TX_FALSE) + { + preempt_retry = TX_TRUE; + continue; + } + + if (current_thread -> tx_thread_state != TX_TERMINATED) + { + _tx_win32_thread_suspend(current_thread -> tx_thread_win32_thread_handle); + current_thread -> tx_thread_win32_deferred_preempt = TX_FALSE; + current_thread -> tx_thread_win32_suspension_type = 1U; + + if (_tx_timer_time_slice[core] != 0U) + { + current_thread -> tx_thread_time_slice = _tx_timer_time_slice[core]; + _tx_timer_time_slice[core] = 0U; + } + } + + _tx_thread_current_ptr[core] = TX_NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread = TX_NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_handle = NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_id = 0U; + current_thread -> tx_thread_smp_core_control = 1U; + + _tx_win32_debug_entry_insert("SCHEDULE-core_preempt_complete", __FILE__, __LINE__); + } + + if (_tx_thread_current_ptr[core] == TX_NULL) + { + execute_thread = _tx_thread_execute_ptr[core]; + + if ((execute_thread != TX_NULL) && (execute_thread -> tx_thread_smp_core_control != 0U)) + { + _tx_thread_current_ptr[core] = execute_thread; + execute_thread -> tx_thread_win32_virtual_core = core; + execute_thread -> tx_thread_smp_core_mapped = core; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread = execute_thread; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_handle = execute_thread -> tx_thread_win32_thread_handle; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_id = execute_thread -> tx_thread_win32_thread_id; + execute_thread -> tx_thread_smp_core_control = 0U; + execute_thread -> tx_thread_run_count++; + _tx_timer_time_slice[core] = execute_thread -> tx_thread_time_slice; + MemoryBarrier(); + if (execute_thread -> tx_thread_win32_suspension_type == 1U) + { + execute_thread -> tx_thread_win32_suspension_type = 0U; + _tx_win32_debug_entry_insert("SCHEDULE-resume_thread", __FILE__, __LINE__); + _tx_win32_thread_resume(execute_thread -> tx_thread_win32_thread_handle); + } + else if (execute_thread -> tx_thread_win32_suspension_type == 2U) + { + execute_thread -> tx_thread_win32_suspension_type = 0U; + _tx_win32_debug_entry_insert("SCHEDULE-release_sem", __FILE__, __LINE__); + +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_mark_run_signal(execute_thread); +#endif +#if (TX_WIN32_USE_ADDRESS_WAIT == 0) + _tx_win32_semaphore_reset(execute_thread -> tx_thread_win32_thread_start_semaphore); + _tx_win32_semaphore_reset(execute_thread -> tx_thread_win32_thread_run_semaphore); +#endif + start_sequence = _tx_win32_thread_start_sequence_get(execute_thread); + _tx_win32_thread_run_signal(execute_thread); + /* Wait for the execute_thread to signal start_ack before proceeding. + * Holding the CS here means any thread that calls TX_DISABLE will + * spin with mutex_access=TRUE and preempt_disable≠0, creating the + * window the ISR needs to observe preempt_disable!=0 for resonance + * tests such as wait_abort_and_isr. */ + wait_status = _tx_win32_wait_for_thread_start_ack(execute_thread, start_sequence); + if (wait_status != WAIT_OBJECT_0) + { + _tx_win32_system_error++; + } + } + else + { + _tx_win32_system_error++; + } + } + } + } + + /* Signal context_restore's rendezvous wait now that all execute_threads have + * been resumed and their start_ack received. */ + if (_tx_win32_timer_waiting != 0U) + { + ReleaseSemaphore(_tx_win32_isr_semaphore, 1, NULL); + } + + if (preempt_retry != TX_FALSE) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + _tx_win32_thread_yield(); + preempt_retry = TX_FALSE; + continue; + } + + _tx_win32_debug_entry_insert("SCHEDULE-self_suspend_sem", __FILE__, __LINE__); + + pending_work = TX_FALSE; + for (core = 0U; core < TX_THREAD_SMP_MAX_CORES; core++) + { + current_thread = _tx_thread_current_ptr[core]; + execute_thread = _tx_thread_execute_ptr[core]; + + if ((current_thread != TX_NULL) && (current_thread -> tx_thread_win32_deferred_preempt != TX_FALSE)) + { + pending_work = TX_TRUE; + break; + } + + if ((_tx_thread_current_ptr[core] == TX_NULL) && + (execute_thread != TX_NULL) && + (execute_thread -> tx_thread_smp_core_control != 0U)) + { + pending_work = TX_TRUE; + break; + } + } + + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + + if (pending_work != TX_FALSE) + { + continue; + } + + /* Coalesce stale wakeups so the scheduler yields to worker threads + until there is a new ready-state transition to process. */ + (void) _tx_win32_wait_for_scheduler_event(); + } +} + + +static VOID _tx_win32_thread_cleanup(TX_THREAD *thread_ptr) +{ +DWORD exit_code; +ULONG wait_count; + + if (thread_ptr -> tx_thread_win32_thread_handle != NULL) + { + wait_count = 0U; + + do + { + if ((GetExitCodeThread(thread_ptr -> tx_thread_win32_thread_handle, &exit_code) != 0) && + (exit_code != STILL_ACTIVE)) + { + break; + } + + _tx_win32_thread_resume(thread_ptr -> tx_thread_win32_thread_handle); + + _tx_win32_thread_run_signal(thread_ptr); + + _tx_win32_thread_sleep(1U); + wait_count++; + } while (wait_count < 100U); + + if ((GetExitCodeThread(thread_ptr -> tx_thread_win32_thread_handle, &exit_code) != 0) && + (exit_code == STILL_ACTIVE)) + { + (void) TerminateThread(thread_ptr -> tx_thread_win32_thread_handle, 0U); + (void) WaitForSingleObject(thread_ptr -> tx_thread_win32_thread_handle, INFINITE); + } + + CloseHandle(thread_ptr -> tx_thread_win32_thread_handle); + thread_ptr -> tx_thread_win32_thread_handle = NULL; + } + + if (thread_ptr -> tx_thread_win32_thread_run_semaphore != NULL) + { + CloseHandle(thread_ptr -> tx_thread_win32_thread_run_semaphore); + thread_ptr -> tx_thread_win32_thread_run_semaphore = NULL; + } + + if (thread_ptr -> tx_thread_win32_thread_start_semaphore != NULL) + { + CloseHandle(thread_ptr -> tx_thread_win32_thread_start_semaphore); + thread_ptr -> tx_thread_win32_thread_start_semaphore = NULL; + } +} + + +void _tx_thread_delete_port_completion(TX_THREAD *thread_ptr, UINT tx_interrupt_save) +{ + _tx_thread_smp_unprotect(tx_interrupt_save); + + _tx_win32_thread_cleanup(thread_ptr); + + tx_interrupt_save = _tx_thread_smp_protect(); + TX_PARAMETER_NOT_USED(tx_interrupt_save); +} + + +void _tx_thread_reset_port_completion(TX_THREAD *thread_ptr, UINT tx_interrupt_save) +{ + _tx_thread_delete_port_completion(thread_ptr, tx_interrupt_save); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_core_get.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_core_get.c new file mode 100644 index 000000000..5a1698a46 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_core_get.c @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +UINT _tx_thread_smp_core_get(void) +{ + return(_tx_win32_smp_current_core_get()); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_core_preempt.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_core_preempt.c new file mode 100644 index 000000000..2da24fda3 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_core_preempt.c @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +void _tx_thread_smp_core_preempt(UINT core) +{ +TX_THREAD *preempt_thread; + + preempt_thread = _tx_thread_current_ptr[core]; + + if (preempt_thread != TX_NULL) + { + preempt_thread -> tx_thread_win32_deferred_preempt = TX_TRUE; + _tx_win32_debug_entry_insert("CORE_PREEMPT_deferred", __FILE__, __LINE__); + + MemoryBarrier(); + if (SetEvent(_tx_win32_scheduler_event) == 0) + { + _tx_win32_system_error++; + } + } + else if (_tx_thread_execute_ptr[core] != TX_NULL) + { + _tx_win32_debug_entry_insert("CORE_PREEMPT_wake_idle", __FILE__, __LINE__); + + MemoryBarrier(); + if (SetEvent(_tx_win32_scheduler_event) == 0) + { + _tx_win32_system_error++; + } + } +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_current_state_get.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_current_state_get.c new file mode 100644 index 000000000..bf1726dfb --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_current_state_get.c @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +ULONG _tx_thread_smp_current_state_get(void) +{ +UINT core; +DWORD thread_id; +UINT critical_section_owned; +ULONG current_state; + + thread_id = GetCurrentThreadId(); + critical_section_owned = (UINT) (_tx_win32_critical_section.tx_win32_critical_section_owner == thread_id); + + if (critical_section_owned == TX_FALSE) + { + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + } + + core = _tx_win32_smp_current_core_get(); + current_state = _tx_thread_system_state[core]; + + if (critical_section_owned == TX_FALSE) + { + _tx_win32_critical_section_release(&_tx_win32_critical_section); + } + + return(current_state); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_current_thread_get.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_current_thread_get.c new file mode 100644 index 000000000..ade6774f9 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_current_thread_get.c @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +TX_THREAD *_tx_thread_smp_current_thread_get(void) +{ +UINT core; +DWORD thread_id; +UINT critical_section_owned; +TX_THREAD *current_thread; + + thread_id = GetCurrentThreadId(); + critical_section_owned = (UINT) (_tx_win32_critical_section.tx_win32_critical_section_owner == thread_id); + + if (critical_section_owned == TX_FALSE) + { + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + } + + core = _tx_win32_smp_current_core_get(); + current_thread = _tx_thread_current_ptr[core]; + + if ((_tx_win32_threadx_thread != 0) && (current_thread == TX_NULL)) + { + _tx_win32_debug_entry_insert("CURRENT_THREAD_GET_NULL", __FILE__, __LINE__); + } + + if (critical_section_owned == TX_FALSE) + { + _tx_win32_critical_section_release(&_tx_win32_critical_section); + } + + return(current_thread); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_initialize_wait.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_initialize_wait.c new file mode 100644 index 000000000..06af9a932 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_initialize_wait.c @@ -0,0 +1,25 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +void _tx_thread_smp_initialize_wait(void) +{ +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_low_level_initialize.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_low_level_initialize.c new file mode 100644 index 000000000..a2c2e25d5 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_low_level_initialize.c @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +void _tx_thread_smp_low_level_initialize(UINT number_of_cores) +{ + TX_PARAMETER_NOT_USED(number_of_cores); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_protect.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_protect.c new file mode 100644 index 000000000..e59caffc5 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_protect.c @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.5). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +UINT _tx_thread_smp_protect(void) +{ +DWORD current_thread_id; +UINT core; +UINT interrupt_posture; +TX_THREAD *current_thread; +UINT current_state; + + do + { + do + { + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + current_thread_id = GetCurrentThreadId(); + core = _tx_thread_smp_core_get(); + current_thread = _tx_thread_current_ptr[core]; + + if ((_tx_win32_threadx_thread != 0) && + ((current_thread == TX_NULL) || (current_thread -> tx_thread_win32_thread_id != current_thread_id))) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + ExitThread(0); + } + + if (_tx_win32_threadx_thread == 0) + { + break; + } + + if ((current_thread != TX_NULL) && (_tx_thread_preempt_disable == 0U)) + { + current_state = current_thread -> tx_thread_state; + if ((current_state == TX_TERMINATED) || (current_state == TX_COMPLETED)) + { + current_thread -> tx_thread_win32_deferred_preempt = TX_FALSE; + current_thread -> tx_thread_win32_suspension_type = 1U; + + if (_tx_timer_time_slice[core] != 0U) + { + current_thread -> tx_thread_time_slice = _tx_timer_time_slice[core]; + _tx_timer_time_slice[core] = 0U; + } + + _tx_thread_current_ptr[core] = TX_NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread = TX_NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_handle = NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_id = 0U; + current_thread -> tx_thread_smp_core_control = 1U; + + _tx_win32_debug_entry_insert("SCHEDULE-thread_terminate_preempt_complete", __FILE__, __LINE__); + if (SetEvent(_tx_win32_scheduler_event) == 0) + { + _tx_win32_system_error++; + } + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + ExitThread(0); + } + } + + if ((current_thread != TX_NULL) && (current_thread -> tx_thread_win32_deferred_preempt != TX_FALSE)) + { + if (SetEvent(_tx_win32_scheduler_event) == 0) + { + _tx_win32_system_error++; + } + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + _tx_win32_thread_yield(); + } + else + { + break; + } + } while (1); + + interrupt_posture = (UINT) _tx_win32_global_int_disabled_flag; + + if (_tx_thread_smp_protection.tx_thread_smp_protect_core == core) + { + _tx_thread_smp_protection.tx_thread_smp_protect_count++; + _tx_win32_global_int_disabled_flag = TX_TRUE; + _tx_win32_debug_entry_insert("PROTECT-obtained-nested", __FILE__, __LINE__); + break; + } + else if (_tx_thread_smp_protection.tx_thread_smp_protect_core == 0xFFFFFFFFUL) + { + _tx_thread_smp_protection.tx_thread_smp_protect_in_force = TX_TRUE; + _tx_thread_smp_protection.tx_thread_smp_protect_thread = current_thread; + _tx_thread_smp_protection.tx_thread_smp_protect_core = core; + _tx_thread_smp_protection.tx_thread_smp_protect_count = 1U; + _tx_thread_smp_protection.tx_thread_smp_protect_thread_id = current_thread_id; + _tx_win32_global_int_disabled_flag = TX_TRUE; + _tx_win32_debug_entry_insert("PROTECT-obtained", __FILE__, __LINE__); + break; + } + else + { + _tx_win32_critical_section_release(&_tx_win32_critical_section); + } + } while (1); + + _tx_win32_global_int_disabled_flag = TX_TRUE; + return(interrupt_posture); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_time_get.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_time_get.c new file mode 100644 index 000000000..adf4a82e3 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_time_get.c @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +ULONG _tx_thread_smp_time_get(void) +{ + QueryPerformanceCounter((LARGE_INTEGER *) &_tx_win32_time_stamp); + return((ULONG) _tx_win32_time_stamp.LowPart); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_smp_unprotect.c b/ports_smp/win64/vs_2022/src/tx_thread_smp_unprotect.c new file mode 100644 index 000000000..b845767d9 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_smp_unprotect.c @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.4). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +void _tx_thread_smp_unprotect(UINT new_interrupt_posture) +{ +UINT core; +DWORD current_thread_id; +UINT critical_section_owned; + + current_thread_id = GetCurrentThreadId(); + critical_section_owned = (UINT) (_tx_win32_critical_section.tx_win32_critical_section_owner == current_thread_id); + + if (critical_section_owned == TX_FALSE) + { + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + } + + core = _tx_thread_smp_core_get(); + + if (_tx_thread_smp_protection.tx_thread_smp_protect_core == core) + { + _tx_thread_smp_protection.tx_thread_smp_protect_count--; + + if (_tx_thread_smp_protection.tx_thread_smp_protect_count == 0U) + { + _tx_win32_global_int_disabled_flag = new_interrupt_posture; + + if (_tx_thread_preempt_disable == 0U) + { + _tx_thread_smp_protection.tx_thread_smp_protect_in_force = TX_FALSE; + _tx_thread_smp_protection.tx_thread_smp_protect_thread = TX_NULL; + _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFFUL; + _tx_thread_smp_protection.tx_thread_smp_protect_thread_id = 0U; + _tx_win32_debug_entry_insert("UNPROTECT-keep", __FILE__, __LINE__); + } + else + { + _tx_win32_debug_entry_insert("UNPROTECT-released", __FILE__, __LINE__); + } + } + else + { + _tx_win32_debug_entry_insert("UNPROTECT-nested", __FILE__, __LINE__); + } + + _tx_win32_critical_section_release(&_tx_win32_critical_section); + } + else + { + _tx_win32_critical_section_release(&_tx_win32_critical_section); + } +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_stack_build.c b/ports_smp/win64/vs_2022/src/tx_thread_stack_build.c new file mode 100644 index 000000000..0531fb3b6 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_stack_build.c @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.5). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" + +#include + +static DWORD WINAPI _tx_win32_thread_entry(LPVOID ptr); + +VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +{ + TX_PARAMETER_NOT_USED(function_ptr); + +#if (TX_WIN32_USE_ADDRESS_WAIT != 0) + thread_ptr -> tx_thread_win32_thread_run_semaphore = NULL; + thread_ptr -> tx_thread_win32_thread_start_semaphore = NULL; +#else + thread_ptr -> tx_thread_win32_thread_run_semaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); + if (thread_ptr -> tx_thread_win32_thread_run_semaphore == NULL) + { + printf("ThreadX SMP Win64 error creating thread run semaphore!\n"); + while (1) + { + } + } + + thread_ptr -> tx_thread_win32_thread_start_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + if (thread_ptr -> tx_thread_win32_thread_start_semaphore == NULL) + { + printf("ThreadX SMP Win64 error creating thread start semaphore!\n"); + while (1) + { + } + } +#endif + + thread_ptr -> tx_thread_win32_thread_handle = CreateThread(NULL, + TX_WIN32_THREAD_STACK_SIZE, + _tx_win32_thread_entry, + (LPVOID) thread_ptr, + CREATE_SUSPENDED, + &thread_ptr -> tx_thread_win32_thread_id); + if (thread_ptr -> tx_thread_win32_thread_handle == NULL) + { + printf("ThreadX SMP Win64 error creating thread!\n"); + while (1) + { + } + } + + SetThreadPriority(thread_ptr -> tx_thread_win32_thread_handle, TX_WIN32_PRIORITY_USER_THREAD); + + thread_ptr -> tx_thread_win32_suspension_type = 2U; + thread_ptr -> tx_thread_win32_mutex_access = TX_FALSE; + thread_ptr -> tx_thread_win32_int_disabled_flag = TX_FALSE; + thread_ptr -> tx_thread_win32_deferred_preempt = TX_FALSE; + thread_ptr -> tx_thread_win32_virtual_core = 0U; + thread_ptr -> tx_thread_win32_run_sequence = 0L; + thread_ptr -> tx_thread_win32_run_sequence_seen = 0L; + thread_ptr -> tx_thread_win32_start_sequence = 0L; + + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((CHAR *) thread_ptr -> tx_thread_stack_end) - 8); + *(((ULONG *) thread_ptr -> tx_thread_stack_ptr) - 1) = 0UL; + thread_ptr -> tx_thread_smp_core_control = 1U; + + ResumeThread(thread_ptr -> tx_thread_win32_thread_handle); +} + + +static DWORD WINAPI _tx_win32_thread_entry(LPVOID ptr) +{ +TX_THREAD *thread_ptr; + + thread_ptr = (TX_THREAD *) ptr; + _tx_win32_threadx_thread = 1; + + _tx_win32_debug_entry_insert("THREAD_ENTRY_wait", __FILE__, __LINE__); + _tx_win32_wait_for_thread_run(thread_ptr); +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_mark_run_wake(thread_ptr); +#endif + _tx_win32_debug_entry_insert("THREAD_ENTRY_wake", __FILE__, __LINE__); + + /* A delete/reset cleanup may wake a host thread that was never scheduled. + Only treat the wakeup as terminal if the ThreadX thread is already in a + terminal state; otherwise allow the normal startup race to complete. */ + if ((thread_ptr -> tx_thread_smp_core_control != 0U) && + ((thread_ptr -> tx_thread_state == TX_TERMINATED) || + (thread_ptr -> tx_thread_state == TX_COMPLETED))) + { + ExitThread(0U); + } + + _tx_win32_current_virtual_core = thread_ptr -> tx_thread_win32_virtual_core; +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_mark_start_ack(thread_ptr); +#endif + _tx_win32_thread_start_ack_signal(thread_ptr); + _tx_win32_debug_entry_insert("THREAD_ENTRY_ack", __FILE__, __LINE__); + + _tx_thread_shell_entry(); + + return(0U); +} diff --git a/ports_smp/win64/vs_2022/src/tx_thread_system_return.c b/ports_smp/win64/vs_2022/src/tx_thread_system_return.c new file mode 100644 index 000000000..8207f3450 --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_thread_system_return.c @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +// Some portions generated by Codex (gpt 5.5). + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +VOID _tx_thread_system_return(VOID) +{ +TX_THREAD *temp_thread_ptr; +UINT temp_thread_state; +UINT core; +DWORD thread_id; + + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + core = _tx_thread_smp_core_get(); + thread_id = GetCurrentThreadId(); + + _tx_win32_debug_entry_insert("SYSTEM_RETURN", __FILE__, __LINE__); + + temp_thread_ptr = _tx_thread_current_ptr[core]; + + if ((_tx_win32_threadx_thread != 0) && + ((temp_thread_ptr == TX_NULL) || (temp_thread_ptr -> tx_thread_win32_thread_id != thread_id))) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + ExitThread(0); + } + + if (_tx_timer_time_slice[core] != 0U) + { + temp_thread_ptr -> tx_thread_time_slice = _tx_timer_time_slice[core]; + _tx_timer_time_slice[core] = 0U; + } + + temp_thread_state = temp_thread_ptr -> tx_thread_state; + temp_thread_ptr -> tx_thread_win32_suspension_type = 2U; + _tx_thread_current_ptr[core] = TX_NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_handle = NULL; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread_id = 0U; + _tx_win32_virtual_cores[core].tx_thread_smp_core_mapping_thread = TX_NULL; + + _tx_win32_debug_entry_insert("SYSTEM_RETURN-release_sem", __FILE__, __LINE__); + + if (temp_thread_ptr != _tx_thread_smp_protection.tx_thread_smp_protect_thread) + { + _tx_win32_system_error++; + } + + _tx_thread_smp_protection.tx_thread_smp_protect_count = 0U; + _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFFUL; + _tx_thread_smp_protection.tx_thread_smp_protect_thread = TX_NULL; + _tx_thread_smp_protection.tx_thread_smp_protect_in_force = TX_FALSE; + _tx_thread_smp_protection.tx_thread_smp_protect_thread_id = 0U; + + temp_thread_ptr -> tx_thread_smp_core_control = 1U; + _tx_win32_global_int_disabled_flag = TX_FALSE; + _tx_thread_preempt_disable = 0U; + + MemoryBarrier(); + if (SetEvent(_tx_win32_scheduler_event) == 0) + { + _tx_win32_system_error++; + } + + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + + if (temp_thread_state == TX_TERMINATED) + { + ExitThread(0); + } + + _tx_win32_wait_for_thread_run(temp_thread_ptr); +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_mark_run_wake(temp_thread_ptr); +#endif + _tx_win32_current_virtual_core = temp_thread_ptr -> tx_thread_win32_virtual_core; +#ifdef TX_WIN32_PROFILE_ENABLE + _tx_win32_profile_mark_start_ack(temp_thread_ptr); +#endif + _tx_win32_thread_start_ack_signal(temp_thread_ptr); + + _tx_win32_critical_section_obtain(&_tx_win32_critical_section); + + _tx_win32_debug_entry_insert("SYSTEM_RETURN-wake_up", __FILE__, __LINE__); + + core = _tx_thread_smp_core_get(); + temp_thread_ptr = _tx_thread_current_ptr[core]; + + if ((_tx_win32_threadx_thread != 0) && + ((temp_thread_ptr == TX_NULL) || (temp_thread_ptr -> tx_thread_win32_thread_id != thread_id))) + { + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); + ExitThread(0); + } + + _tx_win32_debug_entry_insert("SYSTEM_RETURN-finish", __FILE__, __LINE__); + _tx_win32_critical_section_release_all(&_tx_win32_critical_section); +} diff --git a/ports_smp/win64/vs_2022/src/tx_timer_interrupt.c b/ports_smp/win64/vs_2022/src/tx_timer_interrupt.c new file mode 100644 index 000000000..0a19d394e --- /dev/null +++ b/ports_smp/win64/vs_2022/src/tx_timer_interrupt.c @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +#include "tx_api.h" +#include "tx_timer.h" +#include "tx_thread.h" + +VOID _tx_timer_interrupt(VOID) +{ +UINT saved_posture; + + saved_posture = _tx_thread_smp_protect(); + + _tx_timer_interrupt_active++; + _tx_win32_debug_entry_insert("TIMER INTERRUPT", __FILE__, __LINE__); + + _tx_timer_system_clock++; + + if (*_tx_timer_current_ptr) + { + _tx_timer_expired = TX_TRUE; + } + else + { + _tx_timer_current_ptr++; + if (_tx_timer_current_ptr == _tx_timer_list_end) + { + _tx_timer_current_ptr = _tx_timer_list_start; + } + } + + if (_tx_timer_expired != TX_FALSE) + { + _tx_timer_expiration_process(); + } + + _tx_thread_time_slice(); + + _tx_timer_interrupt_active++; + _tx_thread_smp_unprotect(saved_posture); +} diff --git a/scripts/build_smp.ps1 b/scripts/build_smp.ps1 new file mode 100644 index 000000000..c66a2beb7 --- /dev/null +++ b/scripts/build_smp.ps1 @@ -0,0 +1,54 @@ +[CmdletBinding()] +param( + [AllowNull()] + [object]$Configuration = 'all', + + [int]$Parallel = [Math]::Max(1, [Environment]::ProcessorCount), + + [int]$BuildTimeoutSeconds = 180, + + [string]$BuildDir, + + [switch]$Clean +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'tx_windows_common.ps1') + +$repoRoot = Split-Path -Parent $PSScriptRoot + +if (-not $BuildDir) { + $BuildDir = Join-Path $repoRoot 'build\tests\win64_smp' +} + +$selectedConfigurations = Resolve-RegressionConfigurations -RequestedConfigurations $Configuration +Write-Host "Selected configurations: $($selectedConfigurations -join ', ')" + +Enter-VisualStudioDevShell -VsArch 'amd64' + +foreach ($currentConfiguration in $selectedConfigurations) { + $currentBuildDirName = Get-RegressionBuildDirectoryName -ConfigurationName $currentConfiguration + $currentBuildDir = Join-Path $BuildDir $currentBuildDirName + + if ($Clean) { + Remove-BuildDirectory -Path $currentBuildDir -RepoRoot $repoRoot + } + + Remove-NinjaLock -Path $currentBuildDir + + Write-Host "Configuring win64_smp / $currentConfiguration" + Invoke-NativeCommand -FilePath 'cmake' -Arguments @( + '-S', (Join-Path $repoRoot 'test\smp\cmake'), + '-B', $currentBuildDir, + '-G', 'Ninja', + '-DCMAKE_C_COMPILER_FORCED=TRUE', + '-DCMAKE_C_COMPILER_WORKS=TRUE', + '-DCMAKE_C_ABI_COMPILED=TRUE', + "-DCMAKE_BUILD_TYPE=$currentConfiguration", + '-DTHREADX_ARCH=win64', + '-DTHREADX_TOOLCHAIN=vs_2022' + ) + + Write-Host "Building win64_smp / $currentConfiguration" + Invoke-CMakeBuild -BuildDir $currentBuildDir -Parallel $Parallel -TimeoutSeconds $BuildTimeoutSeconds +} diff --git a/scripts/build_tx.ps1 b/scripts/build_tx.ps1 new file mode 100644 index 000000000..eeaa2222a --- /dev/null +++ b/scripts/build_tx.ps1 @@ -0,0 +1,58 @@ +[CmdletBinding()] +param( + [ValidateSet('win64', 'win32')] + [string]$Arch = 'win64', + + [AllowNull()] + [object]$Configuration = 'all', + + [int]$Parallel = [Math]::Max(1, [Environment]::ProcessorCount), + + [int]$BuildTimeoutSeconds = 60, + + [string]$BuildDir, + + [switch]$Clean +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'tx_windows_common.ps1') + +$repoRoot = Split-Path -Parent $PSScriptRoot +$settings = Get-PortSettings -SelectedArch $Arch + +if (-not $BuildDir) { + $BuildDir = Join-Path $repoRoot "build\tests\$Arch" +} + +$selectedConfigurations = Resolve-RegressionConfigurations -RequestedConfigurations $Configuration +Write-Host "Selected configurations: $($selectedConfigurations -join ', ')" + +Enter-VisualStudioDevShell -VsArch $settings.VsArch + +foreach ($currentConfiguration in $selectedConfigurations) { + $currentBuildDirName = Get-RegressionBuildDirectoryName -ConfigurationName $currentConfiguration + $currentBuildDir = Join-Path $BuildDir $currentBuildDirName + + if ($Clean) { + Remove-BuildDirectory -Path $currentBuildDir -RepoRoot $repoRoot + } + + Remove-NinjaLock -Path $currentBuildDir + + Write-Host "Configuring $Arch / $currentConfiguration" + Invoke-NativeCommand -FilePath 'cmake' -Arguments @( + '-S', (Join-Path $repoRoot 'test\tx\cmake'), + '-B', $currentBuildDir, + '-G', 'Ninja', + '-DCMAKE_C_COMPILER_FORCED=TRUE', + '-DCMAKE_C_COMPILER_WORKS=TRUE', + '-DCMAKE_C_ABI_COMPILED=TRUE', + "-DCMAKE_BUILD_TYPE=$currentConfiguration", + "-DTHREADX_ARCH=$($settings.ThreadXArch)", + "-DTHREADX_TOOLCHAIN=$($settings.ThreadXToolchain)" + ) + + Write-Host "Building $Arch / $currentConfiguration" + Invoke-CMakeBuild -BuildDir $currentBuildDir -Parallel $Parallel -TimeoutSeconds $BuildTimeoutSeconds +} diff --git a/scripts/test_smp.ps1 b/scripts/test_smp.ps1 new file mode 100644 index 000000000..e71b10815 --- /dev/null +++ b/scripts/test_smp.ps1 @@ -0,0 +1,120 @@ +[CmdletBinding()] +param( + [AllowNull()] + [object]$Configuration = 'all', + + [int]$Parallel = 1, + + [int]$RepeatFailCount = 2, + + [int]$TestTimeoutSeconds = 45, + + [switch]$CollectFailureDiagnostics = $true, + + [string]$TestRegex, + + [switch]$RerunFailedOnly, + + [string]$BuildDir, + + [switch]$Clean +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'tx_windows_common.ps1') + +$repoRoot = Split-Path -Parent $PSScriptRoot + +if (-not $BuildDir) { + $BuildDir = Join-Path $repoRoot 'build\tests\win64_smp' +} + +$selectedConfigurations = Resolve-RegressionConfigurations -RequestedConfigurations $Configuration +Write-Host "Selected configurations: $($selectedConfigurations -join ', ')" + +Enter-VisualStudioDevShell -VsArch 'amd64' + +if ($Parallel -ne 1) { + Write-Warning 'Windows SMP simulator regression tests are timing-sensitive. Forcing -Parallel 1.' + $Parallel = 1 +} + +if ($TestRegex -and -not $PSBoundParameters.ContainsKey('TestTimeoutSeconds')) { + $TestTimeoutSeconds = 60 + Write-Host "Targeted run detected; using per-test timeout of $TestTimeoutSeconds seconds." +} + +$failedConfigurations = @() + +foreach ($currentConfiguration in $selectedConfigurations) { + $currentBuildDirName = Get-RegressionBuildDirectoryName -ConfigurationName $currentConfiguration + $currentBuildDir = Join-Path $BuildDir $currentBuildDirName + $currentTestingTemporaryDir = Join-Path $currentBuildDir 'Testing\Temporary' + + try { + if ($Clean) { + $currentTestingDir = Join-Path $currentBuildDir 'Testing' + Remove-CtestTestingDirectory -Path $currentTestingDir + } + + if (-not (Test-Path -LiteralPath $currentBuildDir)) { + throw "Build directory does not exist for win64_smp / ${currentConfiguration}: $currentBuildDir" + } + + Remove-NinjaLock -Path $currentBuildDir + if (Test-Path -LiteralPath $currentTestingTemporaryDir) { + Remove-Item -LiteralPath (Join-Path $currentTestingTemporaryDir 'LastTest.log') -Force -ErrorAction SilentlyContinue + Remove-Item -LiteralPath (Join-Path $currentTestingTemporaryDir 'LastTestsFailed.log') -Force -ErrorAction SilentlyContinue + } + + Write-Host "Testing win64_smp / $currentConfiguration" + $ctestArguments = @( + '--test-dir', $currentBuildDir, + '--output-on-failure', + '--timeout', $TestTimeoutSeconds.ToString(), + '-j', $Parallel.ToString() + ) + + if ($RepeatFailCount -gt 1) { + $ctestArguments += @('--repeat', "until-pass:$RepeatFailCount") + } + + if ($TestRegex) { + $ctestArguments += @('-R', $TestRegex) + } + + if ($RerunFailedOnly) { + $ctestArguments += '--rerun-failed' + } + + Invoke-NativeCommand -FilePath 'ctest' -Arguments $ctestArguments + } + catch { + if ($CollectFailureDiagnostics -and (Test-Path -LiteralPath $currentBuildDir)) { + try { + Invoke-CtestFailureDiagnostics -BuildDir $currentBuildDir -TestingTemporaryDir $currentTestingTemporaryDir ` + -TimeoutSeconds $TestTimeoutSeconds + } + catch { + Write-Warning "Failure diagnostics collection failed for ${currentConfiguration}: $($_.Exception.Message)" + } + } + + $failedConfigurations += @{ + Configuration = $currentConfiguration + Message = $_.Exception.Message + } + + Write-Warning "Configuration failed: $currentConfiguration" + } +} + +if ($failedConfigurations.Count -gt 0) { + Write-Host '' + Write-Host 'Configuration failure summary:' + foreach ($failedConfiguration in $failedConfigurations) { + Write-Host "- $($failedConfiguration.Configuration): $($failedConfiguration.Message)" + } + + throw "One or more configurations failed: $($failedConfigurations.Configuration -join ', ')" +} diff --git a/scripts/test_tx.ps1 b/scripts/test_tx.ps1 new file mode 100644 index 000000000..a5607ed7f --- /dev/null +++ b/scripts/test_tx.ps1 @@ -0,0 +1,121 @@ +[CmdletBinding()] +param( + [ValidateSet('win64', 'win32')] + [string]$Arch = 'win64', + + [AllowNull()] + [object]$Configuration = 'all', + + [int]$Parallel = [Math]::Max(1, [Environment]::ProcessorCount), + + [int]$RepeatFailCount = 1, + + [int]$TestTimeoutSeconds = 20, + + [switch]$CollectFailureDiagnostics = $true, + + [string]$TestRegex, + + [switch]$RerunFailedOnly, + + [string]$BuildDir, + + [switch]$Clean +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'tx_windows_common.ps1') + +$repoRoot = Split-Path -Parent $PSScriptRoot +$settings = Get-PortSettings -SelectedArch $Arch + +if (-not $BuildDir) { + $BuildDir = Join-Path $repoRoot "build\tests\$Arch" +} + +$selectedConfigurations = Resolve-RegressionConfigurations -RequestedConfigurations $Configuration +Write-Host "Selected configurations: $($selectedConfigurations -join ', ')" + +Enter-VisualStudioDevShell -VsArch $settings.VsArch + +if (($settings.ThreadXArch -eq 'win32') -or ($settings.ThreadXArch -eq 'win64')) { + if ($Parallel -ne 1) { + Write-Warning "Windows simulator regression tests are timing-sensitive under concurrent ctest execution. Forcing -Parallel 1." + $Parallel = 1 + } +} + +$failedConfigurations = @() + +foreach ($currentConfiguration in $selectedConfigurations) { + $currentBuildDirName = Get-RegressionBuildDirectoryName -ConfigurationName $currentConfiguration + $currentBuildDir = Join-Path $BuildDir $currentBuildDirName + $currentTestingTemporaryDir = Join-Path $currentBuildDir 'Testing\Temporary' + + try { + if ($Clean) { + $currentTestingDir = Join-Path $currentBuildDir 'Testing' + Remove-CtestTestingDirectory -Path $currentTestingDir + } + + if (-not (Test-Path -LiteralPath $currentBuildDir)) { + throw "Build directory does not exist for $Arch / ${currentConfiguration}: $currentBuildDir" + } + + Remove-NinjaLock -Path $currentBuildDir + if (Test-Path -LiteralPath $currentTestingTemporaryDir) { + Remove-Item -LiteralPath (Join-Path $currentTestingTemporaryDir 'LastTest.log') -Force -ErrorAction SilentlyContinue + Remove-Item -LiteralPath (Join-Path $currentTestingTemporaryDir 'LastTestsFailed.log') -Force -ErrorAction SilentlyContinue + } + + Write-Host "Testing $Arch / $currentConfiguration" + $ctestArguments = @( + '--test-dir', $currentBuildDir, + '--output-on-failure', + '--timeout', $TestTimeoutSeconds.ToString(), + '-j', $Parallel.ToString() + ) + + if ($RepeatFailCount -gt 1) { + $ctestArguments += @('--repeat', "until-pass:$RepeatFailCount") + } + + if ($TestRegex) { + $ctestArguments += @('-R', $TestRegex) + } + + if ($RerunFailedOnly) { + $ctestArguments += '--rerun-failed' + } + + Invoke-NativeCommand -FilePath 'ctest' -Arguments $ctestArguments + } + catch { + if ($CollectFailureDiagnostics -and (Test-Path -LiteralPath $currentBuildDir)) { + try { + Invoke-CtestFailureDiagnostics -BuildDir $currentBuildDir -TestingTemporaryDir $currentTestingTemporaryDir ` + -TimeoutSeconds $TestTimeoutSeconds + } + catch { + Write-Warning "Failure diagnostics collection failed for ${currentConfiguration}: $($_.Exception.Message)" + } + } + + $failedConfigurations += @{ + Configuration = $currentConfiguration + Message = $_.Exception.Message + } + + Write-Warning "Configuration failed: $currentConfiguration" + } +} + +if ($failedConfigurations.Count -gt 0) { + Write-Host '' + Write-Host 'Configuration failure summary:' + foreach ($failedConfiguration in $failedConfigurations) { + Write-Host "- $($failedConfiguration.Configuration): $($failedConfiguration.Message)" + } + + throw "One or more configurations failed: $($failedConfigurations.Configuration -join ', ')" +} diff --git a/scripts/tx_windows_common.ps1 b/scripts/tx_windows_common.ps1 new file mode 100644 index 000000000..7b712d4ad --- /dev/null +++ b/scripts/tx_windows_common.ps1 @@ -0,0 +1,941 @@ +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' + +function Invoke-NativeCommand { + param( + [Parameter(Mandatory = $true)] + [string]$FilePath, + + [Parameter()] + [string[]]$Arguments = @() + ) + + & $FilePath @Arguments + if ($LASTEXITCODE -ne 0) { + throw "Command failed with exit code ${LASTEXITCODE}: $FilePath $($Arguments -join ' ')" + } +} + +function Get-CommandPathIfAvailable { + param( + [Parameter(Mandatory = $true)] + [string]$CommandName + ) + + $command = Get-Command $CommandName -ErrorAction SilentlyContinue + if ($null -eq $command) { + return $null + } + + return $command.Source +} + +function Get-PortSettings { + param( + [Parameter(Mandatory = $true)] + [string]$SelectedArch + ) + + switch ($SelectedArch) { + 'win32' { + return @{ + ThreadXArch = 'win32' + ThreadXToolchain = 'vs_2019' + VsArch = 'x86' + } + } + 'win64' { + return @{ + ThreadXArch = 'win64' + ThreadXToolchain = 'vs_2022' + VsArch = 'amd64' + } + } + default { + throw "Unsupported architecture: $SelectedArch" + } + } +} + +function Get-RegressionConfigurations { + return @( + 'default_build_coverage', + 'disable_notify_callbacks_build', + 'stack_checking_build', + 'stack_checking_rand_fill_build', + 'trace_build' + ) +} + +function Resolve-RegressionConfigurations { + param( + [Parameter(Mandatory = $false)] + [AllowNull()] + [AllowEmptyCollection()] + [object]$RequestedConfigurations = 'all' + ) + + $allConfigurations = Get-RegressionConfigurations + $resolvedConfigurations = @() + + if ($null -eq $RequestedConfigurations) { + $resolvedConfigurations = @('all') + } + elseif ($RequestedConfigurations -is [System.Array]) { + foreach ($requestedConfiguration in $RequestedConfigurations) { + if ($null -ne $requestedConfiguration) { + $resolvedConfigurations += [string]$requestedConfiguration + } + } + } + else { + $resolvedConfigurations = @([string]$RequestedConfigurations) + } + + $normalizedConfigurations = @() + foreach ($requestedConfiguration in $resolvedConfigurations) { + foreach ($configurationPart in ($requestedConfiguration -split ',')) { + $trimmedConfiguration = $configurationPart.Trim() + if ($trimmedConfiguration.Length -gt 0) { + $normalizedConfigurations += $trimmedConfiguration + } + } + } + + if (($normalizedConfigurations.Count -eq 0) -or ($normalizedConfigurations -contains 'all')) { + return $allConfigurations + } + + foreach ($normalizedConfiguration in $normalizedConfigurations) { + if ($allConfigurations -notcontains $normalizedConfiguration) { + throw "Unsupported configuration: $normalizedConfiguration" + } + } + + return $normalizedConfigurations +} + +function Get-RegressionBuildDirectoryName { + param( + [Parameter(Mandatory = $true)] + [string]$ConfigurationName + ) + + switch ($ConfigurationName) { + 'default_build_coverage' { + return 'dbc' + } + 'disable_notify_callbacks_build' { + return 'dnc' + } + 'stack_checking_build' { + return 'sc' + } + 'stack_checking_rand_fill_build' { + return 'scrf' + } + 'trace_build' { + return 'tr' + } + default { + throw "Unsupported configuration: $ConfigurationName" + } + } +} + +function Enter-VisualStudioDevShell { + param( + [Parameter(Mandatory = $true)] + [string]$VsArch + ) + + $targetArch = switch ($VsArch) { + 'amd64' { 'x64' } + 'x86' { 'x86' } + default { $VsArch } + } + + if ((Get-Command cl -ErrorAction SilentlyContinue) -and ($env:VSCMD_ARG_TGT_ARCH -eq $targetArch)) { + return + } + + $vsWherePath = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe' + if (-not (Test-Path -LiteralPath $vsWherePath)) { + throw "Unable to locate vswhere.exe at $vsWherePath" + } + + $installationPath = & $vsWherePath -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + if (-not $installationPath) { + throw 'Unable to locate a Visual Studio 2022 installation with MSVC build tools.' + } + + $launchScript = Join-Path $installationPath 'Common7\Tools\Launch-VsDevShell.ps1' + if (-not (Test-Path -LiteralPath $launchScript)) { + throw "Unable to locate Launch-VsDevShell.ps1 at $launchScript" + } + + $env:VSCMD_SKIP_SENDTELEMETRY = '1' + & $launchScript -VsInstallationPath $installationPath -Arch $VsArch -HostArch amd64 -SkipAutomaticLocation | Out-Null + + if (-not (Get-Command cl -ErrorAction SilentlyContinue)) { + throw 'MSVC compiler environment was not activated successfully.' + } +} + +function Remove-BuildDirectory { + param( + [Parameter(Mandatory = $true)] + [string]$Path, + + [Parameter(Mandatory = $true)] + [string]$RepoRoot + ) + + $fullRepoRoot = [System.IO.Path]::GetFullPath($RepoRoot) + $fullPath = [System.IO.Path]::GetFullPath($Path) + + if (-not $fullPath.StartsWith($fullRepoRoot, [System.StringComparison]::OrdinalIgnoreCase)) { + throw "Refusing to remove a directory outside the repository: $fullPath" + } + + if (Test-Path -LiteralPath $fullPath) { + try { + Remove-Item -LiteralPath $fullPath -Recurse -Force -ErrorAction Stop + return + } catch { + Write-Warning "Failed to remove build directory '$fullPath': $($_.Exception.Message)" + } + + Get-ChildItem -LiteralPath $fullPath -Force -Recurse -ErrorAction SilentlyContinue | ForEach-Object { + try { + if (($_.Attributes -band [System.IO.FileAttributes]::ReadOnly) -ne 0) { + $_.Attributes = ($_.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)) + } + } catch { + } + } + + try { + Remove-Item -LiteralPath $fullPath -Recurse -Force -ErrorAction Stop + } catch { + Write-Warning "Proceeding with partially cleaned build directory '$fullPath': $($_.Exception.Message)" + } + } +} + +function Remove-CtestTestingDirectory { + param( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + if (-not (Test-Path -LiteralPath $Path)) { + return + } + + try { + Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop + return + } catch { + Write-Warning "Failed to remove CTest directory '$Path': $($_.Exception.Message)" + } + + Get-ChildItem -LiteralPath $Path -Force -Recurse -ErrorAction SilentlyContinue | ForEach-Object { + try { + if (($_.Attributes -band [System.IO.FileAttributes]::ReadOnly) -ne 0) { + $_.Attributes = ($_.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)) + } + } catch { + } + } + + try { + Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop + } catch { + Write-Warning "Proceeding with partially cleaned CTest directory '$Path': $($_.Exception.Message)" + } +} + +function Remove-NinjaLock { + param( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + $ninjaLockPath = Join-Path $Path '.ninja_lock' + if (Test-Path -LiteralPath $ninjaLockPath) { + Remove-Item -LiteralPath $ninjaLockPath -Force + } +} + +function Get-WindowsDebuggerPath { + $debuggerPath = Get-CommandPathIfAvailable -CommandName 'cdb.exe' + if ($debuggerPath) { + return $debuggerPath + } + + $candidatePaths = @( + (Join-Path ${env:ProgramFiles(x86)} 'Windows Kits\10\Debuggers\x64\cdb.exe'), + (Join-Path ${env:ProgramFiles(x86)} 'Windows Kits\10\Debuggers\x86\cdb.exe') + ) + + foreach ($candidatePath in $candidatePaths) { + if (Test-Path -LiteralPath $candidatePath) { + return $candidatePath + } + } + + return $null +} + +function Get-SanitizedFileName { + param( + [Parameter(Mandatory = $true)] + [string]$Name + ) + + $safeName = [regex]::Replace($Name, '[<>:"/\\|?*]', '_') + $safeName = $safeName -replace '\s+', '_' + return $safeName +} + +function Initialize-MinidumpSupport { + if ($null -ne ('ThreadX.WindowsMiniDump' -as [type])) { + return + } + + Add-Type -TypeDefinition @' +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace ThreadX +{ + public static class WindowsMiniDump + { + [DllImport("Dbghelp.dll", SetLastError = true)] + private static extern bool MiniDumpWriteDump( + IntPtr hProcess, + uint processId, + IntPtr hFile, + uint dumpType, + IntPtr exceptionParam, + IntPtr userStreamParam, + IntPtr callbackParam); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr OpenProcess(uint desiredAccess, bool inheritHandle, int processId); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool CloseHandle(IntPtr handle); + + private const uint ProcessQueryInformation = 0x0400U; + private const uint ProcessVmRead = 0x0010U; + private const uint ProcessDupHandle = 0x0040U; + + public static bool WriteDump(int processId, string dumpPath, uint dumpType, out int errorCode) + { + IntPtr processHandle = OpenProcess(ProcessQueryInformation | ProcessVmRead | ProcessDupHandle, false, processId); + if (processHandle == IntPtr.Zero) + { + errorCode = Marshal.GetLastWin32Error(); + return false; + } + + try + { + using (FileStream dumpStream = new FileStream(dumpPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) + { + bool success = MiniDumpWriteDump( + processHandle, + unchecked((uint)processId), + dumpStream.SafeFileHandle.DangerousGetHandle(), + dumpType, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero); + + errorCode = success ? 0 : Marshal.GetLastWin32Error(); + return success; + } + } + finally + { + CloseHandle(processHandle); + } + } + } +} +'@ +} + +function Wait-FileReadable { + param( + [Parameter(Mandatory = $true)] + [string]$Path, + + [Parameter()] + [int]$TimeoutSeconds = 10 + ) + + $deadline = (Get-Date).AddSeconds($TimeoutSeconds) + while ((Get-Date) -lt $deadline) { + if (-not (Test-Path -LiteralPath $Path)) { + Start-Sleep -Milliseconds 200 + continue + } + + try { + $fileStream = [System.IO.File]::Open($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) + $fileStream.Dispose() + return $true + } + catch { + Start-Sleep -Milliseconds 200 + } + } + + return $false +} + +function Get-CtestTestMetadata { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir + ) + + $ctestOutput = & ctest --test-dir $BuildDir --show-only=json-v1 + if ($LASTEXITCODE -ne 0) { + throw "Unable to enumerate ctest metadata in $BuildDir" + } + + return (($ctestOutput -join [Environment]::NewLine) | ConvertFrom-Json).tests +} + +function Get-CtestFailedTestNames { + param( + [Parameter(Mandatory = $true)] + [string]$TestingTemporaryDir + ) + + $lastFailedPath = Join-Path $TestingTemporaryDir 'LastTestsFailed.log' + if (-not (Test-Path -LiteralPath $lastFailedPath)) { + return @() + } + + $failedTestNames = @() + foreach ($logLine in (Get-Content -LiteralPath $lastFailedPath)) { + if ([string]::IsNullOrWhiteSpace($logLine)) { + continue + } + + if ($logLine -match '^\s*\d+:(?.+)\s*$') { + $failedTestNames += $Matches['name'].Trim() + } + else { + $failedTestNames += $logLine.Trim() + } + } + + return $failedTestNames +} + +function Invoke-ProcessDumpCapture { + param( + [Parameter(Mandatory = $true)] + [int]$ProcessId, + + [Parameter(Mandatory = $true)] + [string]$DumpPath, + + [Parameter()] + [int]$TimeoutSeconds = 15 + ) + + $outputDirectory = Split-Path -Parent $DumpPath + if (-not (Test-Path -LiteralPath $outputDirectory)) { + New-Item -ItemType Directory -Path $outputDirectory | Out-Null + } + + Remove-Item -LiteralPath $DumpPath -Force -ErrorAction SilentlyContinue + + Initialize-MinidumpSupport + $dumpType = [uint32]0x00001006 + $errorCode = 0 + $dumpCaptured = [ThreadX.WindowsMiniDump]::WriteDump($ProcessId, $DumpPath, $dumpType, [ref]$errorCode) + + if (-not $dumpCaptured) { + Write-Warning "MiniDumpWriteDump failed for PID ${ProcessId} with Win32 error $errorCode" + return $false + } + + return (Test-Path -LiteralPath $DumpPath) +} + +function Invoke-DumpStackAnalysis { + param( + [Parameter(Mandatory = $true)] + [string]$DumpPath, + + [Parameter(Mandatory = $true)] + [string]$OutputBasePath, + + [Parameter()] + [string]$SymbolPath, + + [Parameter()] + [int]$TimeoutSeconds = 15 + ) + + if (-not (Test-Path -LiteralPath $DumpPath)) { + Write-Warning "Skipping dump analysis because the dump file was not created: $DumpPath" + return $false + } + + if (-not (Wait-FileReadable -Path $DumpPath)) { + Write-Warning "Skipping dump analysis because the dump file is not readable yet: $DumpPath" + return $false + } + + $debuggerPath = Get-WindowsDebuggerPath + if (-not $debuggerPath) { + Write-Warning 'Skipping dump analysis because cdb.exe is not available.' + return $false + } + + $outputDirectory = Split-Path -Parent $OutputBasePath + if (-not (Test-Path -LiteralPath $outputDirectory)) { + New-Item -ItemType Directory -Path $outputDirectory | Out-Null + } + + $stdoutPath = "${OutputBasePath}.stdout.txt" + $stderrPath = "${OutputBasePath}.stderr.txt" + $commandFilePath = "${OutputBasePath}.commands.txt" + Set-Content -LiteralPath $commandFilePath -Value @( + '!runaway 7' + '~* kb 200' + 'q' + ) -Encoding Ascii + $cdbArguments = @( + '-lines', + '-z', $DumpPath + ) + + if ($SymbolPath) { + $cdbArguments += @('-y', $SymbolPath) + } + + $cdbArguments += @('-cf', $commandFilePath) + $cdbProcess = Start-Process -FilePath $debuggerPath -ArgumentList $cdbArguments -PassThru -NoNewWindow ` + -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath + + try { + $cdbProcess | Wait-Process -Timeout $TimeoutSeconds -ErrorAction Stop + } + catch { + if (-not $cdbProcess.HasExited) { + $null = Start-Process -FilePath 'taskkill.exe' -ArgumentList @('/PID', $cdbProcess.Id.ToString(), '/T', '/F') ` + -WindowStyle Hidden -Wait -PassThru + } + } + + return $true +} + +function Invoke-ProcessWithTimeout { + param( + [Parameter(Mandatory = $true)] + [string]$FilePath, + + [Parameter()] + [string[]]$Arguments = @(), + + [Parameter()] + [int]$TimeoutSeconds = 0, + + [Parameter()] + [string]$WorkingDirectory, + + [Parameter()] + [string]$RedirectStandardOutputPath, + + [Parameter()] + [string]$RedirectStandardErrorPath, + + [Parameter()] + [scriptblock]$OnTimeout, + + [Parameter()] + [scriptblock]$PostTimeout + ) + + $argumentList = @() + foreach ($argument in $Arguments) { + if ($argument -match '\s|"') { + $argumentList += '"' + ($argument -replace '"', '\"') + '"' + } + else { + $argumentList += $argument + } + } + + $startProcessParameters = @{ + FilePath = $FilePath + NoNewWindow = $true + PassThru = $true + } + + if ($argumentList.Count -gt 0) { + $startProcessParameters['ArgumentList'] = $argumentList + } + + if ($WorkingDirectory) { + $startProcessParameters['WorkingDirectory'] = $WorkingDirectory + } + + if ($RedirectStandardOutputPath) { + $startProcessParameters['RedirectStandardOutput'] = $RedirectStandardOutputPath + } + + if ($RedirectStandardErrorPath) { + $startProcessParameters['RedirectStandardError'] = $RedirectStandardErrorPath + } + + $process = Start-Process @startProcessParameters + if ($TimeoutSeconds -le 0) { + $process | Wait-Process + $completed = $true + } + else { + try { + $process | Wait-Process -Timeout $TimeoutSeconds -ErrorAction Stop + $completed = $true + } + catch { + $completed = $false + } + } + + if (-not $completed) { + if ($null -ne $OnTimeout) { + & $OnTimeout $process + } + + $null = Start-Process -FilePath 'taskkill.exe' -ArgumentList @('/PID', $process.Id.ToString(), '/T', '/F') -WindowStyle Hidden -Wait -PassThru + + if ($null -ne $PostTimeout) { + & $PostTimeout $process + } + + return @{ + Completed = $false + ExitCode = $null + ProcessId = $process.Id + } + } + + $process.Refresh() + return @{ + Completed = $true + ExitCode = $process.ExitCode + ProcessId = $process.Id + } +} + +function Invoke-CtestFailureDiagnostics { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir, + + [Parameter(Mandatory = $true)] + [string]$TestingTemporaryDir, + + [Parameter(Mandatory = $true)] + [int]$TimeoutSeconds + ) + + $failedTestNames = Get-CtestFailedTestNames -TestingTemporaryDir $TestingTemporaryDir + if ($failedTestNames.Count -eq 0) { + Write-Warning "No failed tests were recorded in $TestingTemporaryDir" + return + } + + $testMetadataList = Get-CtestTestMetadata -BuildDir $BuildDir + $testMetadataMap = @{} + foreach ($testMetadata in $testMetadataList) { + $testMetadataMap[$testMetadata.name] = $testMetadata + } + + $diagnosticsRoot = Join-Path $TestingTemporaryDir 'FailureDiagnostics' + if (-not (Test-Path -LiteralPath $diagnosticsRoot)) { + New-Item -ItemType Directory -Path $diagnosticsRoot | Out-Null + } + + foreach ($failedTestName in $failedTestNames) { + if (-not $testMetadataMap.ContainsKey($failedTestName)) { + Write-Warning "Unable to locate ctest metadata for failed test: $failedTestName" + continue + } + + $testMetadata = $testMetadataMap[$failedTestName] + if (($null -eq $testMetadata.command) -or ($testMetadata.command.Count -eq 0)) { + Write-Warning "No executable command was recorded for failed test: $failedTestName" + continue + } + + $testArguments = @() + if ($testMetadata.command.Count -gt 1) { + $testArguments = @($testMetadata.command[1..($testMetadata.command.Count - 1)]) + } + + $safeTestName = Get-SanitizedFileName -Name $failedTestName + $stdoutPath = Join-Path $diagnosticsRoot "${safeTestName}.stdout.txt" + $stderrPath = Join-Path $diagnosticsRoot "${safeTestName}.stderr.txt" + $debugOutputBasePath = Join-Path $diagnosticsRoot "${safeTestName}.cdb" + $workingDirectory = $null + $symbolDirectory = Split-Path -Parent $testMetadata.command[0] + + if ($null -ne $testMetadata.properties) { + foreach ($testProperty in $testMetadata.properties) { + if ($testProperty.name -eq 'WORKING_DIRECTORY') { + $workingDirectory = $testProperty.value + break + } + } + } + + Write-Warning "Collecting failure diagnostics for $failedTestName" + $dumpPath = '{0}.{1}.dmp' -f $debugOutputBasePath, ([DateTime]::UtcNow.ToString('yyyyMMddHHmmssfff')) + $testResult = Invoke-ProcessWithTimeout -FilePath $testMetadata.command[0] -Arguments $testArguments ` + -TimeoutSeconds $TimeoutSeconds -WorkingDirectory $workingDirectory -RedirectStandardOutputPath $stdoutPath ` + -RedirectStandardErrorPath $stderrPath -OnTimeout { + param($timedOutProcess) + Invoke-ProcessDumpCapture -ProcessId $timedOutProcess.Id -DumpPath $dumpPath | Out-Null + } -PostTimeout { + param($timedOutProcess) + if (Test-Path -LiteralPath $dumpPath) { + Invoke-DumpStackAnalysis -DumpPath $dumpPath -OutputBasePath $debugOutputBasePath -SymbolPath $symbolDirectory | Out-Null + } + } + + if (-not $testResult.Completed) { + Write-Warning "Timeout diagnostics were captured for $failedTestName under $diagnosticsRoot" + continue + } + + Write-Warning "Replay finished for $failedTestName with exit code $($testResult.ExitCode). Output was saved under $diagnosticsRoot" + } +} + +function Test-IsNinjaBuildDirectory { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir + ) + + return (Test-Path -LiteralPath (Join-Path $BuildDir 'build.ninja')) +} + +function Get-NinjaBuildStatements { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir + ) + + $buildNinjaPath = Join-Path $BuildDir 'build.ninja' + if (-not (Test-Path -LiteralPath $buildNinjaPath)) { + throw "Unable to locate build.ninja in $BuildDir" + } + + return Get-Content -LiteralPath $buildNinjaPath +} + +function New-NinjaRspFile { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir, + + [Parameter(Mandatory = $true)] + [string]$RspRelativePath + ) + + $buildStatements = Get-NinjaBuildStatements -BuildDir $BuildDir + $rspLine = ' RSP_FILE = ' + $RspRelativePath + $rspIndex = -1 + + for ($index = 0; $index -lt $buildStatements.Count; $index++) { + if ($buildStatements[$index] -eq $rspLine) { + $rspIndex = $index + break + } + } + + if ($rspIndex -lt 0) { + throw "Unable to locate RSP_FILE entry for $RspRelativePath in build.ninja." + } + + $buildIndex = -1 + for ($index = $rspIndex; $index -ge 0; $index--) { + if ($buildStatements[$index].StartsWith('build ')) { + $buildIndex = $index + break + } + } + + if ($buildIndex -lt 0) { + throw "Unable to locate the build statement that owns $RspRelativePath." + } + + $buildLine = $buildStatements[$buildIndex] + if ($buildLine -notmatch '^build\s+\S+:\s+\S+\s+(.+)$') { + throw "Unable to parse build statement for $RspRelativePath." + } + + $rspContents = ($Matches[1] -split '\s+') -join [Environment]::NewLine + $rspPath = Join-Path $BuildDir $RspRelativePath + $rspParent = Split-Path -Parent $rspPath + + if (-not (Test-Path -LiteralPath $rspParent)) { + New-Item -ItemType Directory -Path $rspParent | Out-Null + } + + Set-Content -LiteralPath $rspPath -Value $rspContents +} + +function Ensure-NinjaRspFiles { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir, + + [Parameter(Mandatory = $true)] + [string]$CommandLine + ) + + $rspMatches = [regex]::Matches($CommandLine, '@(?[^\s"]+\.rsp)') + foreach ($rspMatch in $rspMatches) { + $rspRelativePath = $rspMatch.Groups['path'].Value + $rspPath = Join-Path $BuildDir $rspRelativePath + if (-not (Test-Path -LiteralPath $rspPath)) { + New-NinjaRspFile -BuildDir $BuildDir -RspRelativePath $rspRelativePath + } + } +} + +function Get-PendingNinjaCommands { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir + ) + + $commandLines = & ninja -C $BuildDir -t commands + if ($LASTEXITCODE -ne 0) { + throw "Unable to enumerate pending Ninja commands in $BuildDir" + } + + return $commandLines | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } +} + +function Invoke-NinjaFallbackBuild { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir + ) + + $pendingCommands = Get-PendingNinjaCommands -BuildDir $BuildDir + if ($pendingCommands.Count -eq 0) { + return + } + + Push-Location $BuildDir + try { + foreach ($pendingCommand in $pendingCommands) { + Ensure-NinjaRspFiles -BuildDir $BuildDir -CommandLine $pendingCommand + + $commandToRun = $pendingCommand -replace '\s/showIncludes(?=\s|$)', '' + + if ($commandToRun -match '^[^ ]*cmd(?:\.exe)?\s+/C\s+"(?.*)"\s*$') { + & cmd.exe /C $Matches['inner'] + } + else { + & cmd.exe /C $commandToRun + } + + if ($LASTEXITCODE -ne 0) { + throw "Fallback Ninja command failed with exit code ${LASTEXITCODE}: $pendingCommand" + } + } + } + finally { + Pop-Location + } +} + +function Invoke-CMakeBuild { + param( + [Parameter(Mandatory = $true)] + [string]$BuildDir, + + [Parameter(Mandatory = $true)] + [int]$Parallel, + + [Parameter()] + [int]$TimeoutSeconds = 0 + ) + + Remove-NinjaLock -Path $BuildDir + $isNinjaBuild = Test-IsNinjaBuildDirectory -BuildDir $BuildDir + + if ($TimeoutSeconds -le 0) { + if ($isNinjaBuild) { + Invoke-NativeCommand -FilePath 'ninja' -Arguments @( + '-C', $BuildDir, + '-j', $Parallel.ToString() + ) + } + else { + Invoke-NativeCommand -FilePath 'cmake' -Arguments @( + '--build', $BuildDir, + '--parallel', $Parallel.ToString() + ) + } + return + } + + if ($isNinjaBuild) { + $buildToolName = 'Ninja' + $buildResult = Invoke-ProcessWithTimeout -FilePath 'ninja' -Arguments @( + '-C', $BuildDir, + '-j', $Parallel.ToString() + ) -TimeoutSeconds $TimeoutSeconds + } + else { + $buildToolName = 'CMake' + $buildResult = Invoke-ProcessWithTimeout -FilePath 'cmake' -Arguments @( + '--build', $BuildDir, + '--parallel', $Parallel.ToString() + ) -TimeoutSeconds $TimeoutSeconds + } + + if ($buildResult.Completed -and ($buildResult.ExitCode -eq 0)) { + return + } + + if (-not $isNinjaBuild) { + if (-not $buildResult.Completed) { + throw "$buildToolName build timed out after $TimeoutSeconds seconds in $BuildDir" + } + + throw "$buildToolName build failed with exit code $($buildResult.ExitCode) in $BuildDir" + } + + if ($buildResult.Completed) { + throw "$buildToolName build failed with exit code $($buildResult.ExitCode) in $BuildDir" + } + + Write-Warning "$buildToolName build timed out after $TimeoutSeconds seconds in $BuildDir. Replaying pending Ninja commands from PowerShell." + + Remove-NinjaLock -Path $BuildDir + Invoke-NinjaFallbackBuild -BuildDir $BuildDir +} diff --git a/test/shared/regression/testcontrol_weak_defaults.c b/test/shared/regression/testcontrol_weak_defaults.c new file mode 100644 index 000000000..b93a28e5f --- /dev/null +++ b/test/shared/regression/testcontrol_weak_defaults.c @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifdef _MSC_VER +#if defined(_M_IX86) +#define TX_TEST_LINKER_ALIAS(symbol, default_symbol) __pragma(comment(linker, "/alternatename:_" #symbol "=_" #default_symbol)) +#else +#define TX_TEST_LINKER_ALIAS(symbol, default_symbol) __pragma(comment(linker, "/alternatename:" #symbol "=" #default_symbol)) +#endif + +void tx_test_default_abort_all_threads_suspended_on_mutex(void) +{ +} +TX_TEST_LINKER_ALIAS(abort_all_threads_suspended_on_mutex, tx_test_default_abort_all_threads_suspended_on_mutex) + +void tx_test_default_suspend_lowest_priority(void) +{ +} +TX_TEST_LINKER_ALIAS(suspend_lowest_priority, tx_test_default_suspend_lowest_priority) + +void tx_test_default_abort_and_resume_byte_allocating_thread(void) +{ +} +TX_TEST_LINKER_ALIAS(abort_and_resume_byte_allocating_thread, tx_test_default_abort_and_resume_byte_allocating_thread) +#else +__attribute__((weak)) void abort_all_threads_suspended_on_mutex(void) +{ +} + +__attribute__((weak)) void suspend_lowest_priority(void) +{ +} + +__attribute__((weak)) void abort_and_resume_byte_allocating_thread(void) +{ +} +#endif diff --git a/test/smp/cmake/CMakeLists.txt b/test/smp/cmake/CMakeLists.txt index f57d253ce..e2ca8627c 100644 --- a/test/smp/cmake/CMakeLists.txt +++ b/test/smp/cmake/CMakeLists.txt @@ -2,11 +2,19 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) cmake_policy(SET CMP0054 NEW) cmake_policy(SET CMP0057 NEW) +if((DEFINED THREADX_ARCH) AND (THREADX_ARCH STREQUAL "win64")) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() + project(threadx_smp_test LANGUAGES C) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + # Set build configurations -set(BUILD_CONFIGURATIONS default_build_coverage - disable_notify_callbacks_build stack_checking_build stack_checking_rand_fill_build +set(BUILD_CONFIGURATIONS default_build_coverage disable_notify_callbacks_build + stack_checking_build stack_checking_rand_fill_build trace_build) set(CMAKE_CONFIGURATION_TYPES ${BUILD_CONFIGURATIONS} @@ -23,26 +31,31 @@ endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.") -set(default_build_coverage -DTX_QUEUE_MESSAGE_MAX_SIZE=32) -set(disable_notify_callbacks_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_DISABLE_NOTIFY_CALLBACKS) -set(stack_checking_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_STACK_CHECKING) -set(stack_checking_rand_fill_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_STACK_CHECKING -DTX_ENABLE_RANDOM_NUMBER_STACK_FILLING) -set(trace_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_EVENT_TRACE) +set(default_build_coverage TX_QUEUE_MESSAGE_MAX_SIZE=32) +set(disable_notify_callbacks_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_DISABLE_NOTIFY_CALLBACKS) +set(stack_checking_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_STACK_CHECKING) +set(stack_checking_rand_fill_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_STACK_CHECKING TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) +set(trace_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_EVENT_TRACE) -add_compile_options( - -m32 - -std=c99 - -ggdb - -g3 - -gdwarf-2 - -fdiagnostics-color - # -Werror - -DTX_THREAD_SMP_ONLY_CORE_0_DEFAULT - -DTX_SMP_NOT_POSSIBLE - -DTX_REGRESSION_TEST - -DTEST_STACK_SIZE_PRINTF=4096 +add_compile_definitions( + TX_THREAD_SMP_ONLY_CORE_0_DEFAULT + TX_SMP_NOT_POSSIBLE + TX_REGRESSION_TEST + TEST_STACK_SIZE_PRINTF=4096 ${${CMAKE_BUILD_TYPE}}) -add_link_options(-m32) + +if(MSVC) + add_compile_options(/W3 /Zi) + add_link_options(/DEBUG /INCREMENTAL:NO) +else() + add_compile_options( + -m32 + -ggdb + -g3 + -gdwarf-2 + -fdiagnostics-color) + add_link_options(-m32) +endif() enable_testing() @@ -52,26 +65,31 @@ add_subdirectory(samples) # Coverage if(CMAKE_BUILD_TYPE MATCHES ".*_coverage") - target_compile_options(threadx_smp PRIVATE -fprofile-arcs -ftest-coverage) - target_link_options(threadx_smp PRIVATE -fprofile-arcs -ftest-coverage) + if(NOT MSVC) + target_compile_options(threadx_smp PRIVATE -fprofile-arcs -ftest-coverage) + target_link_options(threadx_smp PRIVATE -fprofile-arcs -ftest-coverage) + endif() endif() -target_compile_options( - threadx_smp - PRIVATE # -Werror - -Wall - -Wextra - -pedantic - -fmessage-length=0 - -fsigned-char - -ffunction-sections - -fdata-sections - -Wunused - -Wuninitialized - -Wmissing-declarations - -Wconversion - -Wpointer-arith - # -Wshadow - -Wlogical-op - -Waggregate-return - -Wfloat-equal) +if(MSVC) + target_compile_options(threadx_smp PRIVATE /W3) +else() + target_compile_options( + threadx_smp + PRIVATE + -Wall + -Wextra + -pedantic + -fmessage-length=0 + -fsigned-char + -ffunction-sections + -fdata-sections + -Wunused + -Wuninitialized + -Wmissing-declarations + -Wconversion + -Wpointer-arith + -Wlogical-op + -Waggregate-return + -Wfloat-equal) +endif() diff --git a/test/smp/cmake/regression/CMakeLists.txt b/test/smp/cmake/regression/CMakeLists.txt index f7226c216..7d258e50e 100644 --- a/test/smp/cmake/regression/CMakeLists.txt +++ b/test/smp/cmake/regression/CMakeLists.txt @@ -4,6 +4,16 @@ cmake_policy(SET CMP0057 NEW) project(regression_test LANGUAGES C) set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../regression) +set(REPO_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../..) +set(PORT_LOW_LEVEL_SOURCE ${REPO_ROOT}/ports_smp/${THREADX_ARCH}/${THREADX_TOOLCHAIN}/src/tx_initialize_low_level.c) +set(GENERATED_LOW_LEVEL_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/tx_initialize_low_level.c) +set(TESTCONTROL_WEAK_DEFAULTS_SOURCE ${REPO_ROOT}/test/shared/regression/testcontrol_weak_defaults.c) + +include_directories(${REPO_ROOT}/test/tx/regression) + +if(NOT EXISTS ${PORT_LOW_LEVEL_SOURCE}) + message(FATAL_ERROR "Unable to locate tx_initialize_low_level.c for ${THREADX_ARCH}/${THREADX_TOOLCHAIN}") +endif() set(regression_test_cases ${SOURCE_DIR}/threadx_block_memory_basic_test.c @@ -117,18 +127,32 @@ set(regression_test_cases ${SOURCE_DIR}/threadx_initialize_kernel_setup_test.c) add_custom_command( - OUTPUT ${SOURCE_DIR}/tx_initialize_low_level.c - COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.sh + OUTPUT ${GENERATED_LOW_LEVEL_SOURCE} + COMMAND ${CMAKE_COMMAND} + -DSOURCE_FILE=${PORT_LOW_LEVEL_SOURCE} + -DOUTPUT_FILE=${GENERATED_LOW_LEVEL_SOURCE} + -P ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.cmake + DEPENDS ${PORT_LOW_LEVEL_SOURCE} ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.cmake COMMENT "Generating tx_initialize_low_level.c for test") -add_library(test_utility ${SOURCE_DIR}/tx_initialize_low_level.c - ${SOURCE_DIR}/testcontrol.c) -target_link_libraries(test_utility PUBLIC azrtos::threadx_smp) -target_compile_definitions(test_utility PUBLIC CTEST BATCH_TEST) +add_library(test_utility OBJECT ${GENERATED_LOW_LEVEL_SOURCE} + ${SOURCE_DIR}/testcontrol.c + ${TESTCONTROL_WEAK_DEFAULTS_SOURCE}) +target_link_libraries(test_utility PRIVATE azrtos::threadx_smp) +target_compile_definitions(test_utility PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) foreach(test_case ${regression_test_cases}) get_filename_component(test_name ${test_case} NAME_WE) - add_executable(${test_name} ${test_case}) - target_link_libraries(${test_name} PRIVATE test_utility) + + if(test_name STREQUAL "threadx_initialize_kernel_setup_test") + add_executable(${test_name} ${test_case}) + else() + add_executable(${test_name} ${test_case} $) + target_compile_definitions(${test_name} PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) + endif() + + target_link_libraries(${test_name} PRIVATE azrtos::threadx_smp) add_test(${CMAKE_BUILD_TYPE}::${test_name} ${test_name}) endforeach() diff --git a/test/smp/cmake/regression/generate_test_file.cmake b/test/smp/cmake/regression/generate_test_file.cmake new file mode 100644 index 000000000..77cccaa59 --- /dev/null +++ b/test/smp/cmake/regression/generate_test_file.cmake @@ -0,0 +1,44 @@ +if(NOT DEFINED SOURCE_FILE) + message(FATAL_ERROR "SOURCE_FILE is required") +endif() + +if(NOT DEFINED OUTPUT_FILE) + message(FATAL_ERROR "OUTPUT_FILE is required") +endif() + +file(STRINGS "${SOURCE_FILE}" FILE_LINES) + +set(UPDATED_FILE_CONTENTS "") +set(DISPATCH_DECLARATION "VOID test_interrupt_dispatch(VOID);") +set(DISPATCH_CALL "test_interrupt_dispatch();") +set(DECLARATION_INSERTED FALSE) +set(CALL_INSERTED FALSE) + +foreach(FILE_LINE IN LISTS FILE_LINES) + if((NOT DECLARATION_INSERTED) AND + ((FILE_LINE MATCHES "^void[ \t]+\\*_tx_linux_timer_interrupt\\(void \\*p\\);[ \t]*$") + OR + (FILE_LINE MATCHES "^VOID[ \t]+_tx_win32_timer_interrupt\\(VOID\\);[ \t]*$") + OR + (FILE_LINE MATCHES "^VOID CALLBACK[ \t]+_tx_win32_timer_interrupt\\(UINT wTimerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2\\);[ \t]*$"))) + string(APPEND UPDATED_FILE_CONTENTS "${FILE_LINE}\n${DISPATCH_DECLARATION}\n") + set(DECLARATION_INSERTED TRUE) + elseif((NOT CALL_INSERTED) AND (FILE_LINE MATCHES "^([ \t]*)_tx_timer_interrupt\\(\\);[ \t]*$")) + string(APPEND UPDATED_FILE_CONTENTS "${CMAKE_MATCH_1}${DISPATCH_CALL}\n${FILE_LINE}\n") + set(CALL_INSERTED TRUE) + else() + string(APPEND UPDATED_FILE_CONTENTS "${FILE_LINE}\n") + endif() +endforeach() + +if(NOT CALL_INSERTED) + message(FATAL_ERROR "Unable to insert test interrupt dispatcher call into ${SOURCE_FILE}") +endif() + +if(NOT DECLARATION_INSERTED) + message(FATAL_ERROR "Unable to insert test interrupt dispatcher declaration into ${SOURCE_FILE}") +endif() + +get_filename_component(OUTPUT_DIRECTORY "${OUTPUT_FILE}" DIRECTORY) +file(MAKE_DIRECTORY "${OUTPUT_DIRECTORY}") +file(WRITE "${OUTPUT_FILE}" "${UPDATED_FILE_CONTENTS}") diff --git a/test/smp/cmake/threadx_smp/CMakeLists.txt b/test/smp/cmake/threadx_smp/CMakeLists.txt index f65962537..568fece41 100644 --- a/test/smp/cmake/threadx_smp/CMakeLists.txt +++ b/test/smp/cmake/threadx_smp/CMakeLists.txt @@ -1,11 +1,5 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) -# Set up the project -project(threadx_smp - VERSION 6.0.0 - LANGUAGES C ASM -) - if(NOT DEFINED THREADX_ARCH) message(FATAL_ERROR "Error: THREADX_ARCH not defined") endif() @@ -13,6 +7,17 @@ if(NOT DEFINED THREADX_TOOLCHAIN) message(FATAL_ERROR "Error: THREADX_TOOLCHAIN not defined") endif() +set(THREADX_SMP_PROJECT_LANGUAGES C) +if(NOT ((THREADX_ARCH STREQUAL "win64") AND (THREADX_TOOLCHAIN STREQUAL "vs_2022"))) + list(APPEND THREADX_SMP_PROJECT_LANGUAGES ASM) +endif() + +# Set up the project +project(threadx_smp + VERSION 6.0.0 + LANGUAGES ${THREADX_SMP_PROJECT_LANGUAGES} +) + set(PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../..) # Define our target library and an alias for consumers @@ -57,4 +62,4 @@ set(CPACK_SOURCE_IGNORE_FILES ".*~$" ) set(CPACK_VERBATIM_VARIABLES YES) -include(CPack) \ No newline at end of file +include(CPack) diff --git a/test/smp/cmake/threadx_smp/ports_smp/win64/vs_2022/CMakeLists.txt b/test/smp/cmake/threadx_smp/ports_smp/win64/vs_2022/CMakeLists.txt new file mode 100644 index 000000000..6b219bb21 --- /dev/null +++ b/test/smp/cmake/threadx_smp/ports_smp/win64/vs_2022/CMakeLists.txt @@ -0,0 +1,29 @@ +set(CURRENT_DIR ${PROJECT_DIR}/ports_smp/win64/vs_2022) +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CURRENT_DIR}/src/tx_initialize_low_level.c + ${CURRENT_DIR}/src/tx_thread_context_restore.c + ${CURRENT_DIR}/src/tx_thread_context_save.c + ${CURRENT_DIR}/src/tx_thread_interrupt_control.c + ${CURRENT_DIR}/src/tx_thread_schedule.c + ${CURRENT_DIR}/src/tx_thread_smp_core_get.c + ${CURRENT_DIR}/src/tx_thread_smp_core_preempt.c + ${CURRENT_DIR}/src/tx_thread_smp_current_state_get.c + ${CURRENT_DIR}/src/tx_thread_smp_current_thread_get.c + ${CURRENT_DIR}/src/tx_thread_smp_initialize_wait.c + ${CURRENT_DIR}/src/tx_thread_smp_low_level_initialize.c + ${CURRENT_DIR}/src/tx_thread_smp_protect.c + ${CURRENT_DIR}/src/tx_thread_smp_time_get.c + ${CURRENT_DIR}/src/tx_thread_smp_unprotect.c + ${CURRENT_DIR}/src/tx_thread_stack_build.c + ${CURRENT_DIR}/src/tx_thread_system_return.c + ${CURRENT_DIR}/src/tx_timer_interrupt.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CURRENT_DIR}/inc +) diff --git a/test/smp/regression/testcontrol.c b/test/smp/regression/testcontrol.c index dacaf70e8..07aa9a3f0 100644 --- a/test/smp/regression/testcontrol.c +++ b/test/smp/regression/testcontrol.c @@ -1,4 +1,5 @@ /* 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). #define TX_THREAD_SMP_SOURCE_CODE @@ -1355,6 +1356,10 @@ UINT i; /* 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); @@ -1435,6 +1440,10 @@ UINT old_posture = TX_INT_ENABLE; 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. */ tx_thread_resume(&test_control_thread); } @@ -1447,6 +1456,51 @@ TX_MUTEX *mutex_ptr; TX_THREAD *thread_ptr; + /* 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. */ + while ((thread_ptr == &_tx_timer_thread) || (thread_ptr == &test_control_thread)) + { + + /* 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); + } + /* Delete all queues. */ while(_tx_queue_created_ptr) { @@ -1524,49 +1578,6 @@ TX_THREAD *thread_ptr; tx_mutex_delete(mutex_ptr); } - /* Delete all threads, except for timer thread, and test control thread. */ - 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. */ - while ((thread_ptr == &_tx_timer_thread) || (thread_ptr == &test_control_thread)) - { - - /* 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); - } - /* At this point, only the test control thread and the system timer thread and/or mutex should still be in the system. */ } @@ -1609,15 +1620,3 @@ 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; } - -__attribute__((weak)) void abort_all_threads_suspended_on_mutex(void) -{ -} - -__attribute__((weak)) void suspend_lowest_priority(void) -{ -} - -__attribute__((weak)) void abort_and_resume_byte_allocating_thread(void) -{ -} diff --git a/test/smp/regression/threadx_block_memory_basic_test.c b/test/smp/regression/threadx_block_memory_basic_test.c index 12e301d1a..117872c3f 100644 --- a/test/smp/regression/threadx_block_memory_basic_test.c +++ b/test/smp/regression/threadx_block_memory_basic_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" typedef struct BLOCK_MEMORY_TEST_STRUCT { @@ -86,7 +87,7 @@ CHAR *pointer; /* Attempt to create a block pool from a timer. */ pointer = (CHAR *) 0x30000; - status = tx_block_pool_create(&pool_3, "pool 3", 100, pointer, 320); + status = tx_block_pool_create(&pool_3, "pool 3", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -146,7 +147,7 @@ UINT status; } /* Attempt to create a block pool from an ISR. */ - status = tx_block_pool_create(&pool_3, "pool 3", 100, (void *) 0x100000, 320); + status = tx_block_pool_create(&pool_3, "pool 3", 100, (void *) 0x100000, TX_TEST_BLOCK_POOL_BYTES(100, 3)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -217,8 +218,8 @@ CHAR *pointer; } /* Create block pools 0 and 1. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -228,8 +229,8 @@ CHAR *pointer; test_control_return(1); } - status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -241,7 +242,7 @@ CHAR *pointer; /* Check the no-blocks path. */ status = _tx_block_pool_create(&pool_2, "pool 2", 100, pointer, 50); - pointer = pointer + 320; + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SIZE_ERROR) @@ -265,7 +266,7 @@ CHAR *pointer_2; CHAR *pointer_3; CHAR *pointer_4; INT i; -unsigned long fake_block[20]; +TX_TEST_POINTER_WORD fake_block[20]; /* Inform user. */ @@ -316,7 +317,7 @@ unsigned long fake_block[20]; /* Try to release a block that points to a non-pool. */ fake_block[0] = 0; - fake_block[1] = (unsigned long) &fake_block[0]; + TX_TEST_STORE_POINTER(fake_block[1], &fake_block[0]); status = tx_block_release(&fake_block[2]); /* Check status. */ @@ -519,7 +520,7 @@ unsigned long fake_block[20]; { /* Block memory error. */ - printf("ERROR #20\n"); + printf("ERROR #20 (%lu %lu %lu)\n", error, timer_executed, isr_executed); test_control_return(1); } diff --git a/test/smp/regression/threadx_block_memory_error_detection_test.c b/test/smp/regression/threadx_block_memory_error_detection_test.c index 7b7f2f610..e9869b1eb 100644 --- a/test/smp/regression/threadx_block_memory_error_detection_test.c +++ b/test/smp/regression/threadx_block_memory_error_detection_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -49,18 +50,18 @@ INT status; pointer = pointer + TEST_STACK_SIZE_PRINTF; /* Create block pool 0. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); #ifndef TX_DISABLE_ERROR_CHECKING /* skip this test and pretend it passed */ /* Create block pool again to get pool_ptr error. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); if (status != TX_POOL_ERROR) return; /* Create block pool with NULL pointer. */ - status = tx_block_pool_create(TX_NULL, "pool 0", 100, pointer, 320); + status = tx_block_pool_create(TX_NULL, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); if (status != TX_POOL_ERROR) { @@ -69,7 +70,7 @@ INT status; } /* Create block pool pointer if NULL start. */ - status = tx_block_pool_create(&pool_1, "pool 0", 100, NULL, 320); + status = tx_block_pool_create(&pool_1, "pool 0", 100, NULL, TX_TEST_BLOCK_POOL_BYTES(100, 3)); if (status != TX_PTR_ERROR) { @@ -128,11 +129,11 @@ INT i; #ifndef TX_DISABLE_ERROR_CHECKING /* skip this test and pretend it passed */ - status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Attempt to create a pool with an invalid size. */ - status = _txe_block_pool_create(&pool_2, "pool 2", 100, pointer, 320, 777777); + status = _txe_block_pool_create(&pool_2, "pool 2", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3), 777777); if (status != TX_POOL_ERROR) { @@ -384,4 +385,3 @@ INT i; printf("SUCCESS!\n"); test_control_return(0); } - diff --git a/test/smp/regression/threadx_block_memory_suspension_test.c b/test/smp/regression/threadx_block_memory_suspension_test.c index da14f454e..9ce8939c5 100644 --- a/test/smp/regression/threadx_block_memory_suspension_test.c +++ b/test/smp/regression/threadx_block_memory_suspension_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -83,8 +84,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -308,4 +309,3 @@ CHAR *pointer_1; } } - diff --git a/test/smp/regression/threadx_block_memory_suspension_timeout_test.c b/test/smp/regression/threadx_block_memory_suspension_timeout_test.c index dc460662d..57be8aaa3 100644 --- a/test/smp/regression/threadx_block_memory_suspension_timeout_test.c +++ b/test/smp/regression/threadx_block_memory_suspension_timeout_test.c @@ -1,7 +1,10 @@ /* This test is designed to test timeouts on suspension on memory block pools. */ +// Some portions generated by Codex (gpt 5.4). + #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -84,8 +87,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -135,15 +138,15 @@ CHAR *pointer_3; /* Set all the memory of the blocks. */ TX_MEMSET(pointer_3, (CHAR) 0xEF, 100); - /* Sleep for 64 ticks to allow the other thread 6 timeouts on the block - pool. */ + /* Sleep long enough to allow the other threads to complete their expected + timeout cycles on the block pool. */ tx_thread_sleep(64); /* Incrment the run counter. */ thread_0_counter++; /* Check the counter of the other thread. */ - if ((thread_1_counter != 6) || (thread_2_counter != 3)) + if ((thread_1_counter != 6UL) || (thread_2_counter != 3UL)) { /* Block memory error. */ @@ -209,4 +212,3 @@ CHAR *pointer_1; thread_2_counter++; } } - diff --git a/test/smp/regression/threadx_block_memory_thread_terminate_test.c b/test/smp/regression/threadx_block_memory_thread_terminate_test.c index 6064891df..291ab0187 100644 --- a/test/smp/regression/threadx_block_memory_thread_terminate_test.c +++ b/test/smp/regression/threadx_block_memory_thread_terminate_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -69,8 +70,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -182,4 +183,3 @@ CHAR *pointer_1; thread_1_counter++; } } - diff --git a/test/smp/regression/threadx_byte_memory_basic_test.c b/test/smp/regression/threadx_byte_memory_basic_test.c index 36f413317..d609712c1 100644 --- a/test/smp/regression/threadx_byte_memory_basic_test.c +++ b/test/smp/regression/threadx_byte_memory_basic_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" typedef struct BYTE_MEMORY_TEST_STRUCT { @@ -91,7 +92,7 @@ CHAR *pointer; /* Attempt to create a byte pool from a timer. */ pointer = (CHAR *) 0x30000; - status = tx_byte_pool_create(&pool_2, "pool 2", pointer, 108); + status = tx_byte_pool_create(&pool_2, "pool 2", pointer, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -103,7 +104,7 @@ CHAR *pointer; /* Attempt to create a byte pool with an invalid size. */ status = _txe_byte_pool_create(&pool_3, "pool 3", pointer, - 108, (sizeof(TX_BYTE_POOL)+1)); + TX_TEST_BYTE_POOL_BYTES(108), (sizeof(TX_BYTE_POOL)+1)); /* Check status. */ if (status != TX_POOL_ERROR) @@ -186,7 +187,7 @@ UINT status; /* Attempt to create a byte pool from an ISR. */ - status = tx_byte_pool_create(&pool_2, "pool 0", (void *) 0x100000, 108); + status = tx_byte_pool_create(&pool_2, "pool 0", (void *) 0x100000, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -256,8 +257,8 @@ CHAR *pointer; } /* Create byte pools 0 and 1. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_CAPACITY_BYTES(24, 3)); + pointer = pointer + TX_TEST_BYTE_POOL_CAPACITY_BYTES(24, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -267,8 +268,8 @@ CHAR *pointer; test_control_return(1); } - status = tx_byte_pool_create(&pool_1, "pool 1", pointer, 200); - pointer = pointer + 200; + status = tx_byte_pool_create(&pool_1, "pool 1", pointer, TX_TEST_BYTE_POOL_BYTES(200)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(200); /* Check status. */ if (status != TX_SUCCESS) @@ -279,9 +280,9 @@ CHAR *pointer; } /* Test for search pointer issue on wrapped seach with prior block to search pointer merged. */ - status = tx_byte_pool_create(&pool_4, "pool 4", pointer, 300); + status = tx_byte_pool_create(&pool_4, "pool 4", pointer, TX_TEST_BYTE_POOL_CAPACITY_BYTES(84, 3)); pool_4_memory = pointer; - pointer = pointer + 300; + pointer = pointer + TX_TEST_BYTE_POOL_CAPACITY_BYTES(84, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -353,7 +354,7 @@ CHAR *pointer_2; CHAR *pointer_3; CHAR *pointer_4; INT i; -ULONG array[20]; +TX_TEST_POINTER_WORD array[20]; UCHAR *save_search; @@ -394,7 +395,7 @@ UCHAR *save_search; /* Try to create a NULL pool. */ pointer_1 = (CHAR *) 0x30000; - status = tx_byte_pool_create(TX_NULL, "pool 0", pointer_1, 108); + status = tx_byte_pool_create(TX_NULL, "pool 0", pointer_1, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_POOL_ERROR) @@ -406,7 +407,7 @@ UCHAR *save_search; } /* Try to create the same pool. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer_1, 108); + status = tx_byte_pool_create(&pool_0, "pool 0", pointer_1, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_POOL_ERROR) @@ -418,7 +419,7 @@ UCHAR *save_search; } /* Try to create a pool with a NULL start address. */ - status = tx_byte_pool_create(&pool_2, "pool 2", TX_NULL, 108); + status = tx_byte_pool_create(&pool_2, "pool 2", TX_NULL, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_PTR_ERROR) @@ -626,7 +627,7 @@ UCHAR *save_search; /* Test another bad block release.... pool pointer is not a valid pool! */ array[0] = 0; - array[1] = (ULONG) &array[3]; + TX_TEST_STORE_POINTER(array[1], &array[3]); array[2] = 0; array[3] = 0; status = _tx_byte_release(&array[2]); @@ -865,7 +866,7 @@ UCHAR *save_search; } /* Move the search pointer to the third block to exercise that code in the byte search algorithm. */ - pool_0.tx_byte_pool_search = (UCHAR *) pointer_3-8; + pool_0.tx_byte_pool_search = (UCHAR *) pointer_3 - TX_TEST_BYTE_POOL_SEARCH_OFFSET; /* Now allocate the block again. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer_2, 24, TX_NO_WAIT); @@ -885,7 +886,7 @@ UCHAR *save_search; status += tx_byte_release(pointer_1); /* Move the search pointer to the third block to exercise that code in the byte search algorithm. */ - pool_0.tx_byte_pool_search = (UCHAR *) pointer_3-8; + pool_0.tx_byte_pool_search = (UCHAR *) pointer_3 - TX_TEST_BYTE_POOL_SEARCH_OFFSET; /* Allocate a large block to force the search pointer update. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer_3, 88, TX_NO_WAIT); @@ -961,7 +962,7 @@ UCHAR *save_search; } /* Create pool 4. */ - status = tx_byte_pool_create(&pool_4, "pool 4", pool_4_memory, 300); + status = tx_byte_pool_create(&pool_4, "pool 4", pool_4_memory, TX_TEST_BYTE_POOL_CAPACITY_BYTES(84, 3)); /* Check status. */ if (status != TX_SUCCESS) @@ -1061,4 +1062,3 @@ UCHAR *save_search; printf("SUCCESS!\n"); test_control_return(0); } - diff --git a/test/smp/regression/threadx_byte_memory_information_test.c b/test/smp/regression/threadx_byte_memory_information_test.c index 8a6192694..df607acb4 100644 --- a/test/smp/regression/threadx_byte_memory_information_test.c +++ b/test/smp/regression/threadx_byte_memory_information_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" #include "tx_byte_pool.h" @@ -193,7 +194,7 @@ CHAR *pointer; } /* Create the byte_pool with one byte. */ - status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, 100); + status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(100)); pointer = pointer + 100; /* Check for status. */ diff --git a/test/smp/regression/threadx_byte_memory_prioritize_test.c b/test/smp/regression/threadx_byte_memory_prioritize_test.c index 294071350..9160db91a 100644 --- a/test/smp/regression/threadx_byte_memory_prioritize_test.c +++ b/test/smp/regression/threadx_byte_memory_prioritize_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" /* Define the ISR dispatch. */ @@ -197,7 +198,7 @@ CHAR *pointer; } /* Create the byte_pool with one byte. */ - status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, 100); + status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(100)); pointer = pointer + 100; /* Check for status. */ @@ -495,4 +496,3 @@ VOID *pointer; thread_6_counter++; } } - diff --git a/test/smp/regression/threadx_byte_memory_suspension_test.c b/test/smp/regression/threadx_byte_memory_suspension_test.c index ba159894b..2736195dc 100644 --- a/test/smp/regression/threadx_byte_memory_suspension_test.c +++ b/test/smp/regression/threadx_byte_memory_suspension_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -121,8 +122,8 @@ CHAR *pointer; } /* Create byte pool 0. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/smp/regression/threadx_byte_memory_suspension_timeout_test.c b/test/smp/regression/threadx_byte_memory_suspension_timeout_test.c index 5e94df97a..68f18c74d 100644 --- a/test/smp/regression/threadx_byte_memory_suspension_timeout_test.c +++ b/test/smp/regression/threadx_byte_memory_suspension_timeout_test.c @@ -1,7 +1,10 @@ /* This test is designed to test suspension timeout on a memory byte pool. */ +// Some portions generated by Codex (gpt 5.4). + #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -85,8 +88,8 @@ CHAR *pointer; } /* Create byte pool 0. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Check status. */ if (status != TX_SUCCESS) @@ -126,12 +129,12 @@ CHAR *pointer; test_control_return(1); } - /* Sleep to allow the other thread to suspend and timeout on the memory - pool once. */ + /* Sleep long enough to allow the other threads to complete their expected + timeout cycles on the byte pool. */ tx_thread_sleep(64); /* Check the counter of the other thread. */ - if ((thread_1_counter != 6) || (thread_2_counter != 3)) + if ((thread_1_counter != 6UL) || (thread_2_counter != 3UL)) { /* Block memory error. */ diff --git a/test/smp/regression/threadx_byte_memory_thread_contention_test.c b/test/smp/regression/threadx_byte_memory_thread_contention_test.c index 899ed2691..cd379e54c 100644 --- a/test/smp/regression/threadx_byte_memory_thread_contention_test.c +++ b/test/smp/regression/threadx_byte_memory_thread_contention_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -89,8 +90,8 @@ CHAR *pointer; } /* Create byte pool 0. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Save off the intial pool size. */ initial_pool_size = pool_0.tx_byte_pool_available; @@ -126,7 +127,6 @@ CHAR *pointer; while(1) { - /* Allocate memory from the pool. This size will cause merge activity because the search pointer will sit in this large block about half the time. */ @@ -195,10 +195,8 @@ static void thread_1_entry(ULONG thread_input) UINT status; CHAR *pointer; - while(test_done == TX_FALSE) { - /* Allocate memory from the pool. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer, 30, TX_WAIT_FOREVER); @@ -228,10 +226,8 @@ static void thread_2_entry(ULONG thread_input) UINT status; CHAR *pointer; - while(test_done == TX_FALSE) { - /* Allocate memory from the pool. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer, 12, TX_WAIT_FOREVER); diff --git a/test/smp/regression/threadx_byte_memory_thread_terminate_test.c b/test/smp/regression/threadx_byte_memory_thread_terminate_test.c index 4805ba571..35a4615aa 100644 --- a/test/smp/regression/threadx_byte_memory_thread_terminate_test.c +++ b/test/smp/regression/threadx_byte_memory_thread_terminate_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -68,8 +69,8 @@ CHAR *pointer; } /* Create byte pools 0 and 1. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Check status. */ if (status != TX_SUCCESS) @@ -174,4 +175,3 @@ CHAR *pointer; thread_1_counter++; } } - diff --git a/test/smp/regression/threadx_event_flag_isr_set_clear_test.c b/test/smp/regression/threadx_event_flag_isr_set_clear_test.c index b1420d767..54b18a2e6 100644 --- a/test/smp/regression/threadx_event_flag_isr_set_clear_test.c +++ b/test/smp/regression/threadx_event_flag_isr_set_clear_test.c @@ -240,14 +240,12 @@ ULONG actual; /* Inform user. */ printf("Running Event Flag Set/Clear from ISR Test.......................... "); - /* Setup the test ISR. */ test_isr_dispatch = test_isr; /* Loop to exploit the probability window inside tx_event_flags_set call. */ while (condition_count < 40) { - /* Suspend on the event_flags that is going to be set via the ISR. */ status = tx_event_flags_get(&event_flags_0, 2, TX_OR_CLEAR, &actual, 4); @@ -354,5 +352,3 @@ static void timer_0_entry(ULONG input) { timer_0_counter++; } - - diff --git a/test/smp/regression/threadx_event_flag_isr_wait_abort_test.c b/test/smp/regression/threadx_event_flag_isr_wait_abort_test.c index 95d3b5ab9..8706b8bc4 100644 --- a/test/smp/regression/threadx_event_flag_isr_wait_abort_test.c +++ b/test/smp/regression/threadx_event_flag_isr_wait_abort_test.c @@ -32,7 +32,6 @@ static unsigned long condition_count = 0; static TX_EVENT_FLAGS_GROUP event_flags_0; - /* Define thread prototypes. */ static void thread_0_entry(ULONG thread_input); diff --git a/test/smp/regression/threadx_initialize_kernel_setup_test.c b/test/smp/regression/threadx_initialize_kernel_setup_test.c index a1172ded9..8c2b11596 100644 --- a/test/smp/regression/threadx_initialize_kernel_setup_test.c +++ b/test/smp/regression/threadx_initialize_kernel_setup_test.c @@ -33,18 +33,6 @@ UINT mutex_priority_change_extension_selection; UINT priority_change_extension_selection; -__attribute__((weak)) void abort_all_threads_suspended_on_mutex(void) -{ -} - -__attribute__((weak)) void suspend_lowest_priority(void) -{ -} - -__attribute__((weak)) void abort_and_resume_byte_allocating_thread(void) -{ -} - void main() { @@ -80,4 +68,4 @@ void delete_timer_thread(void) _tx_thread_delete(&_tx_timer_thread); } -#endif \ No newline at end of file +#endif diff --git a/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_pt_test.c b/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_pt_test.c index db174ef2c..ce2315f8a 100644 --- a/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_pt_test.c +++ b/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_pt_test.c @@ -1,7 +1,9 @@ /* Define the ThreadX SMP random resume/suspend/exclude/pt test. */ +// Some portions generated by Codex (gpt 5.4). #include #include "tx_api.h" +#include "tx_thread.h" //#define MAX_PASSES 50000000 //#define MAX_PASSES 50000 diff --git a/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_test.c b/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_test.c index 1a02cf190..379e1fdfe 100644 --- a/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_test.c +++ b/test/smp/regression/threadx_smp_random_resume_suspend_exclusion_test.c @@ -2370,4 +2370,3 @@ TX_THREAD *thread_ptr; tx_thread_suspend(thread_ptr); } } - diff --git a/test/smp/regression/threadx_smp_random_resume_suspend_test.c b/test/smp/regression/threadx_smp_random_resume_suspend_test.c index 8098dfe77..8550bdf24 100644 --- a/test/smp/regression/threadx_smp_random_resume_suspend_test.c +++ b/test/smp/regression/threadx_smp_random_resume_suspend_test.c @@ -2325,5 +2325,3 @@ static void thread_entry(ULONG id) tx_thread_suspend(_smp_randomized_source_array[id]); } } - - diff --git a/test/smp/regression/threadx_smp_relinquish_test.c b/test/smp/regression/threadx_smp_relinquish_test.c index dddb13c27..96b2864d6 100644 --- a/test/smp/regression/threadx_smp_relinquish_test.c +++ b/test/smp/regression/threadx_smp_relinquish_test.c @@ -362,4 +362,3 @@ static void thread_31h_entry(ULONG thread_input) thread_31h_counter++; } } - diff --git a/test/smp/regression/threadx_smp_time_slice_test.c b/test/smp/regression/threadx_smp_time_slice_test.c index fe97590ac..b6cd67eae 100644 --- a/test/smp/regression/threadx_smp_time_slice_test.c +++ b/test/smp/regression/threadx_smp_time_slice_test.c @@ -1,5 +1,7 @@ /* Define the ThreadX SMP time-slice test. */ +// Some portions generated by Codex (gpt 5.4). + #include #include "tx_api.h" @@ -243,7 +245,7 @@ UINT status; status += tx_thread_resume(&thread_31g); status += tx_thread_resume(&thread_31h); - /* Now sleep for 20 ticks to let see if all the threads execute. */ + /* Sleep long enough to see if all the threads execute. */ tx_thread_sleep(20); /* Now check and make sure all the threads ran. */ @@ -358,4 +360,3 @@ static void thread_31h_entry(ULONG thread_input) thread_31h_counter++; } } - diff --git a/test/smp/regression/threadx_thread_basic_execution_test.c b/test/smp/regression/threadx_thread_basic_execution_test.c index f10ce2e9f..42ded1842 100644 --- a/test/smp/regression/threadx_thread_basic_execution_test.c +++ b/test/smp/regression/threadx_thread_basic_execution_test.c @@ -11,6 +11,7 @@ #include "tx_queue.h" #include "tx_semaphore.h" #include "tx_thread.h" +#include "tx_timer.h" typedef struct THREAD_MEMORY_TEST_STRUCT @@ -33,6 +34,7 @@ static THREAD_MEMORY_TEST thread_memory; /* Define the ISR dispatch. */ extern VOID (*test_isr_dispatch)(void); +extern TX_TIMER_INTERNAL *_tx_timer_expired_timer_ptr; static unsigned long thread_0_counter = 0; @@ -314,7 +316,22 @@ VOID (*temp_mutex_release)(TX_THREAD *thread_ptr); test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL; test_thread.tx_thread_suspending = TX_TRUE; test_thread.tx_thread_delayed_suspend = TX_TRUE; +#if defined(_WIN64) + { + TX_TIMER_INTERNAL timeout_timer; + TX_TIMER_INTERNAL *saved_expired_timer_ptr; + + + TX_MEMSET(&timeout_timer, 0, sizeof(TX_TIMER_INTERNAL)); + saved_expired_timer_ptr = _tx_timer_expired_timer_ptr; + _tx_timer_expired_timer_ptr = &timeout_timer; + timeout_timer.tx_timer_internal_extension_ptr = (VOID *) &test_thread; + _tx_thread_timeout(0); + _tx_timer_expired_timer_ptr = saved_expired_timer_ptr; + } +#else _tx_thread_timeout((ULONG) &test_thread); +#endif /* Setup test thread to make sure _tx_thread_terminate can handle a NULL mutex release function pointer. */ temp_mutex_release = _tx_thread_mutex_release; diff --git a/test/smp/regression/threadx_thread_delayed_suspension_test.c b/test/smp/regression/threadx_thread_delayed_suspension_test.c index e5d1e0912..72c872ddd 100644 --- a/test/smp/regression/threadx_thread_delayed_suspension_test.c +++ b/test/smp/regression/threadx_thread_delayed_suspension_test.c @@ -227,7 +227,6 @@ UINT status; /* Inform user. */ printf("Running Thread Delayed Suspension Clearing Test..................... "); - /* Relinquish to the other thread. */ tx_thread_relinquish(); @@ -270,7 +269,6 @@ UINT status; /* Wait until we see the delayed suspension set flag. */ while(delayed_suspend_set == 0) { - /* Abort the suspension for thread 2. */ tx_thread_wait_abort(&thread_2); diff --git a/test/smp/regression/threadx_thread_multiple_sleep_test.c b/test/smp/regression/threadx_thread_multiple_sleep_test.c index f2ae3b998..76443afa3 100644 --- a/test/smp/regression/threadx_thread_multiple_sleep_test.c +++ b/test/smp/regression/threadx_thread_multiple_sleep_test.c @@ -1,5 +1,7 @@ /* This test is designed to test multiple threads sleeping for 33 ticks. */ +// Some portions generated by Codex (gpt 5.4). + #include #include "tx_api.h" @@ -154,14 +156,14 @@ static void thread_2_entry(ULONG thread_input) static void thread_3_entry(ULONG thread_input) { - /* Inform user. */ printf("Running Thread Multiple Thread Sleep for 33 Test.................... "); /* Clear the tick count. */ tx_time_set(0); - /* Sleep for 100 ticks (+1 in case tick before threads 0,1,2 have run). */ + /* Sleep for the target interval, with one extra tick in the default case + in case the first tick arrives before threads 0, 1, and 2 have run. */ tx_thread_sleep(101); /* Determine if the sleep was accurate. */ @@ -181,4 +183,3 @@ static void thread_3_entry(ULONG thread_input) test_control_return(1); } } - diff --git a/test/smp/regression/threadx_thread_relinquish_test.c b/test/smp/regression/threadx_thread_relinquish_test.c index 7b46c8204..7c7efbd18 100644 --- a/test/smp/regression/threadx_thread_relinquish_test.c +++ b/test/smp/regression/threadx_thread_relinquish_test.c @@ -218,7 +218,6 @@ CHAR *pointer; static void thread_0_entry(ULONG thread_input) { - /* Check for correct input value and execution of other threads. */ if ((thread_input != 0) || (thread_1_counter) || (thread_2_counter) || (thread_3_counter)) @@ -234,7 +233,6 @@ static void thread_0_entry(ULONG thread_input) static void thread_1_entry(ULONG thread_input) { - /* Check for correct input value and execution of other threads. */ if ((thread_input != 1) || (thread_0_counter != 1) || (thread_2_counter) || (thread_3_counter)) @@ -250,7 +248,6 @@ static void thread_1_entry(ULONG thread_input) static void thread_2_entry(ULONG thread_input) { - /* Check for correct input value and execution of other threads. */ if ((thread_input != 2) || (thread_0_counter != 1) || (thread_1_counter != 1) || (thread_3_counter)) @@ -455,5 +452,3 @@ static void thread_9_entry(ULONG thread_input) tx_thread_relinquish(); } } - - diff --git a/test/smp/regression/threadx_thread_sleep_for_100ticks_test.c b/test/smp/regression/threadx_thread_sleep_for_100ticks_test.c index 344672461..addb28757 100644 --- a/test/smp/regression/threadx_thread_sleep_for_100ticks_test.c +++ b/test/smp/regression/threadx_thread_sleep_for_100ticks_test.c @@ -311,7 +311,6 @@ volatile ULONG value = 0; #endif while (isr_test_suspend_interrupted_condition != TX_TRUE) { - /* Sleep to get a frest timer slot. */ tx_thread_sleep(1); @@ -432,4 +431,3 @@ volatile ULONG value = 0; test_control_return(0); } } - diff --git a/test/smp/regression/threadx_thread_wait_abort_and_isr_test.c b/test/smp/regression/threadx_thread_wait_abort_and_isr_test.c index 5a65fced9..d5241cc3b 100644 --- a/test/smp/regression/threadx_thread_wait_abort_and_isr_test.c +++ b/test/smp/regression/threadx_thread_wait_abort_and_isr_test.c @@ -269,4 +269,3 @@ static void timer_0_entry(ULONG input) { timer_0_counter++; } - diff --git a/test/smp/regression/threadx_timer_multiple_accuracy_test.c b/test/smp/regression/threadx_timer_multiple_accuracy_test.c index af9957d8b..206f28025 100644 --- a/test/smp/regression/threadx_timer_multiple_accuracy_test.c +++ b/test/smp/regression/threadx_timer_multiple_accuracy_test.c @@ -183,4 +183,3 @@ static void timer_2_expiration(ULONG timer_input) /* Process timer expiration. */ timer_2_counter++; } - diff --git a/test/smp/regression/threadx_timer_simple_test.c b/test/smp/regression/threadx_timer_simple_test.c index b763ad553..b7c3f7c75 100644 --- a/test/smp/regression/threadx_timer_simple_test.c +++ b/test/smp/regression/threadx_timer_simple_test.c @@ -596,7 +596,6 @@ ULONG exclusion_map; /* Create a timer for the test. */ tx_timer_create(&timer_0, "timer 0", timer_entry, 0, 1, 1, TX_AUTO_ACTIVATE); - /* Setup the ISR. */ test_isr_dispatch = test_isr; diff --git a/test/tx/cmake/CMakeLists.txt b/test/tx/cmake/CMakeLists.txt index 2dd16ade5..97b3615bd 100644 --- a/test/tx/cmake/CMakeLists.txt +++ b/test/tx/cmake/CMakeLists.txt @@ -2,8 +2,16 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) cmake_policy(SET CMP0054 NEW) cmake_policy(SET CMP0057 NEW) +if((DEFINED THREADX_ARCH) AND ((THREADX_ARCH STREQUAL "win32") OR (THREADX_ARCH STREQUAL "win64"))) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() + project(threadx_test LANGUAGES C) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + # Set build configurations set(BUILD_CONFIGURATIONS default_build_coverage disable_notify_callbacks_build stack_checking_build stack_checking_rand_fill_build trace_build) @@ -22,24 +30,30 @@ endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.") -set(default_build_coverage -DTX_QUEUE_MESSAGE_MAX_SIZE=32) -set(disable_notify_callbacks_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_DISABLE_NOTIFY_CALLBACKS) -set(stack_checking_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_STACK_CHECKING) -set(stack_checking_rand_fill_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_STACK_CHECKING -DTX_ENABLE_RANDOM_NUMBER_STACK_FILLING) -set(trace_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_EVENT_TRACE) +set(default_build_coverage TX_QUEUE_MESSAGE_MAX_SIZE=32) +set(disable_notify_callbacks_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_DISABLE_NOTIFY_CALLBACKS) +set(stack_checking_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_STACK_CHECKING) +set(stack_checking_rand_fill_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_STACK_CHECKING TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) +set(trace_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_EVENT_TRACE) -add_compile_options( - -m32 - -std=c99 - -ggdb - -g3 - -gdwarf-2 - -fdiagnostics-color - -Werror - -DTX_REGRESSION_TEST - -DTEST_STACK_SIZE_PRINTF=4096 +add_compile_definitions( + TX_REGRESSION_TEST + TEST_STACK_SIZE_PRINTF=4096 ${${CMAKE_BUILD_TYPE}}) -add_link_options(-m32) + +if(MSVC) + add_compile_options(/W3 /Zi) + add_link_options(/DEBUG /INCREMENTAL:NO) +else() + add_compile_options( + -m32 + -ggdb + -g3 + -gdwarf-2 + -fdiagnostics-color + -Werror) + add_link_options(-m32) +endif() enable_testing() @@ -49,26 +63,32 @@ add_subdirectory(samples) # Coverage if(CMAKE_BUILD_TYPE MATCHES ".*_coverage") - target_compile_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) - target_link_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) + if(NOT MSVC) + target_compile_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) + target_link_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) + endif() endif() -target_compile_options( - threadx - PRIVATE -Werror - -Wall - -Wextra - -pedantic - -fmessage-length=0 - -fsigned-char - -ffunction-sections - -fdata-sections - -Wunused - -Wuninitialized - -Wmissing-declarations - -Wconversion - -Wpointer-arith - # -Wshadow - -Wlogical-op - -Waggregate-return - -Wfloat-equal) +if(MSVC) + target_compile_options(threadx PRIVATE /W3) +else() + target_compile_options( + threadx + PRIVATE -Werror + -Wall + -Wextra + -pedantic + -fmessage-length=0 + -fsigned-char + -ffunction-sections + -fdata-sections + -Wunused + -Wuninitialized + -Wmissing-declarations + -Wconversion + -Wpointer-arith + # -Wshadow + -Wlogical-op + -Waggregate-return + -Wfloat-equal) +endif() diff --git a/test/tx/cmake/regression/CMakeLists.txt b/test/tx/cmake/regression/CMakeLists.txt index d60743fac..25029ab91 100644 --- a/test/tx/cmake/regression/CMakeLists.txt +++ b/test/tx/cmake/regression/CMakeLists.txt @@ -4,6 +4,14 @@ cmake_policy(SET CMP0057 NEW) project(regression_test LANGUAGES C) set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../regression) +set(REPO_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../..) +set(PORT_LOW_LEVEL_SOURCE ${REPO_ROOT}/ports/${THREADX_ARCH}/${THREADX_TOOLCHAIN}/src/tx_initialize_low_level.c) +set(GENERATED_LOW_LEVEL_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/tx_initialize_low_level.c) +set(TESTCONTROL_WEAK_DEFAULTS_SOURCE ${REPO_ROOT}/test/shared/regression/testcontrol_weak_defaults.c) + +if(NOT EXISTS ${PORT_LOW_LEVEL_SOURCE}) + message(FATAL_ERROR "Unable to locate tx_initialize_low_level.c for ${THREADX_ARCH}/${THREADX_TOOLCHAIN}") +endif() set(regression_test_cases ${SOURCE_DIR}/threadx_block_memory_basic_test.c @@ -104,19 +112,32 @@ set(regression_test_cases ${SOURCE_DIR}/threadx_initialize_kernel_setup_test.c) add_custom_command( - OUTPUT ${SOURCE_DIR}/tx_initialize_low_level.c - COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.sh + OUTPUT ${GENERATED_LOW_LEVEL_SOURCE} + COMMAND ${CMAKE_COMMAND} + -DSOURCE_FILE=${PORT_LOW_LEVEL_SOURCE} + -DOUTPUT_FILE=${GENERATED_LOW_LEVEL_SOURCE} + -P ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.cmake + DEPENDS ${PORT_LOW_LEVEL_SOURCE} ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.cmake COMMENT "Generating tx_initialize_low_level.c for test") -add_library(test_utility ${SOURCE_DIR}/tx_initialize_low_level.c - ${SOURCE_DIR}/testcontrol.c) -target_link_libraries(test_utility PUBLIC azrtos::threadx) -target_compile_definitions(test_utility PUBLIC CTEST BATCH_TEST - TEST_STACK_SIZE_PRINTF=4096) +add_library(test_utility OBJECT ${GENERATED_LOW_LEVEL_SOURCE} + ${SOURCE_DIR}/testcontrol.c + ${TESTCONTROL_WEAK_DEFAULTS_SOURCE}) +target_compile_definitions(test_utility PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) +target_link_libraries(test_utility PRIVATE azrtos::threadx) foreach(test_case ${regression_test_cases}) get_filename_component(test_name ${test_case} NAME_WE) - add_executable(${test_name} ${test_case}) - target_link_libraries(${test_name} PRIVATE test_utility) + + if(test_name STREQUAL "threadx_initialize_kernel_setup_test") + add_executable(${test_name} ${test_case}) + else() + add_executable(${test_name} ${test_case} $) + target_compile_definitions(${test_name} PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) + endif() + + target_link_libraries(${test_name} PRIVATE azrtos::threadx) add_test(${CMAKE_BUILD_TYPE}::${test_name} ${test_name}) endforeach() diff --git a/test/tx/cmake/regression/generate_test_file.cmake b/test/tx/cmake/regression/generate_test_file.cmake new file mode 100644 index 000000000..d37e744b3 --- /dev/null +++ b/test/tx/cmake/regression/generate_test_file.cmake @@ -0,0 +1,42 @@ +if(NOT DEFINED SOURCE_FILE) + message(FATAL_ERROR "SOURCE_FILE is required") +endif() + +if(NOT DEFINED OUTPUT_FILE) + message(FATAL_ERROR "OUTPUT_FILE is required") +endif() + +file(STRINGS "${SOURCE_FILE}" FILE_LINES) + +set(UPDATED_FILE_CONTENTS "") +set(DISPATCH_DECLARATION "VOID test_interrupt_dispatch(VOID);") +set(DISPATCH_CALL "test_interrupt_dispatch();") +set(DECLARATION_INSERTED FALSE) +set(CALL_INSERTED FALSE) + +foreach(FILE_LINE IN LISTS FILE_LINES) + if((NOT DECLARATION_INSERTED) AND + ((FILE_LINE MATCHES "^void[ \t]+\\*_tx_linux_timer_interrupt\\(void \\*p\\);[ \t]*$") + OR + (FILE_LINE MATCHES "^VOID CALLBACK[ \t]+_tx_win32_timer_interrupt\\(UINT wTimerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2\\);[ \t]*$"))) + string(APPEND UPDATED_FILE_CONTENTS "${FILE_LINE}\n${DISPATCH_DECLARATION}\n") + set(DECLARATION_INSERTED TRUE) + elseif((NOT CALL_INSERTED) AND (FILE_LINE MATCHES "^([ \t]*)_tx_timer_interrupt\\(\\);[ \t]*$")) + string(APPEND UPDATED_FILE_CONTENTS "${CMAKE_MATCH_1}${DISPATCH_CALL}\n${FILE_LINE}\n") + set(CALL_INSERTED TRUE) + else() + string(APPEND UPDATED_FILE_CONTENTS "${FILE_LINE}\n") + endif() +endforeach() + +if(NOT CALL_INSERTED) + message(FATAL_ERROR "Unable to insert test interrupt dispatcher call into ${SOURCE_FILE}") +endif() + +if(NOT DECLARATION_INSERTED) + message(FATAL_ERROR "Unable to insert test interrupt dispatcher declaration into ${SOURCE_FILE}") +endif() + +get_filename_component(OUTPUT_DIRECTORY "${OUTPUT_FILE}" DIRECTORY) +file(MAKE_DIRECTORY "${OUTPUT_DIRECTORY}") +file(WRITE "${OUTPUT_FILE}" "${UPDATED_FILE_CONTENTS}") diff --git a/test/tx/regression/testcontrol.c b/test/tx/regression/testcontrol.c index 4944784a6..68f1158ee 100644 --- a/test/tx/regression/testcontrol.c +++ b/test/tx/regression/testcontrol.c @@ -1,4 +1,5 @@ /* This is the test control routine of the ThreadX kernel. All tests are dispatched from this routine. */ +// Some portions generated by Codex (gpt 5.4). #include "tx_api.h" #include @@ -122,7 +123,6 @@ 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 *); @@ -483,7 +483,6 @@ UINT i, j; TX_THREAD *thread_ptr; #endif - /* Initialize the test error/success counters. */ test_control_successful_tests = 0; test_control_failed_tests = 0; @@ -565,6 +564,7 @@ TX_THREAD *thread_ptr; /* 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(); @@ -586,7 +586,7 @@ TX_THREAD *thread_ptr; init_test_thread.tx_thread_ready_previous = &init_test_thread; _tx_thread_time_slice(); _tx_thread_current_ptr = TX_NULL; - +#ifndef TX_DISABLE_NOTIFY_CALLBACKS /* 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; @@ -605,6 +605,7 @@ TX_THREAD *thread_ptr; _tx_thread_current_ptr = TX_NULL; _tx_thread_execute_ptr = temp_thread; _tx_thread_mutex_release = temp_mutex_release; /* Recover Mutex release pointer. */ +#endif /* Test _tx_thread_system_suspend when not current, preemption is needed but disabled. */ temp_thread = _tx_thread_execute_ptr; @@ -1413,14 +1414,3 @@ void test_exit_notify(TX_THREAD *thread_ptr, UINT type) } -__attribute__((weak)) void abort_all_threads_suspended_on_mutex(void) -{ -} - -__attribute__((weak)) void suspend_lowest_priority(void) -{ -} - -__attribute__((weak)) void abort_and_resume_byte_allocating_thread(void) -{ -} diff --git a/test/tx/regression/threadx_block_memory_basic_test.c b/test/tx/regression/threadx_block_memory_basic_test.c index 12e301d1a..c49effe7a 100644 --- a/test/tx/regression/threadx_block_memory_basic_test.c +++ b/test/tx/regression/threadx_block_memory_basic_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" typedef struct BLOCK_MEMORY_TEST_STRUCT { @@ -86,7 +87,7 @@ CHAR *pointer; /* Attempt to create a block pool from a timer. */ pointer = (CHAR *) 0x30000; - status = tx_block_pool_create(&pool_3, "pool 3", 100, pointer, 320); + status = tx_block_pool_create(&pool_3, "pool 3", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -146,7 +147,7 @@ UINT status; } /* Attempt to create a block pool from an ISR. */ - status = tx_block_pool_create(&pool_3, "pool 3", 100, (void *) 0x100000, 320); + status = tx_block_pool_create(&pool_3, "pool 3", 100, (void *) 0x100000, TX_TEST_BLOCK_POOL_BYTES(100, 3)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -217,8 +218,8 @@ CHAR *pointer; } /* Create block pools 0 and 1. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -228,8 +229,8 @@ CHAR *pointer; test_control_return(1); } - status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -265,7 +266,7 @@ CHAR *pointer_2; CHAR *pointer_3; CHAR *pointer_4; INT i; -unsigned long fake_block[20]; +TX_TEST_POINTER_WORD fake_block[20]; /* Inform user. */ @@ -316,7 +317,7 @@ unsigned long fake_block[20]; /* Try to release a block that points to a non-pool. */ fake_block[0] = 0; - fake_block[1] = (unsigned long) &fake_block[0]; + TX_TEST_STORE_POINTER(fake_block[1], &fake_block[0]); status = tx_block_release(&fake_block[2]); /* Check status. */ diff --git a/test/tx/regression/threadx_block_memory_error_detection_test.c b/test/tx/regression/threadx_block_memory_error_detection_test.c index 7b7f2f610..e9869b1eb 100644 --- a/test/tx/regression/threadx_block_memory_error_detection_test.c +++ b/test/tx/regression/threadx_block_memory_error_detection_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -49,18 +50,18 @@ INT status; pointer = pointer + TEST_STACK_SIZE_PRINTF; /* Create block pool 0. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); #ifndef TX_DISABLE_ERROR_CHECKING /* skip this test and pretend it passed */ /* Create block pool again to get pool_ptr error. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); if (status != TX_POOL_ERROR) return; /* Create block pool with NULL pointer. */ - status = tx_block_pool_create(TX_NULL, "pool 0", 100, pointer, 320); + status = tx_block_pool_create(TX_NULL, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); if (status != TX_POOL_ERROR) { @@ -69,7 +70,7 @@ INT status; } /* Create block pool pointer if NULL start. */ - status = tx_block_pool_create(&pool_1, "pool 0", 100, NULL, 320); + status = tx_block_pool_create(&pool_1, "pool 0", 100, NULL, TX_TEST_BLOCK_POOL_BYTES(100, 3)); if (status != TX_PTR_ERROR) { @@ -128,11 +129,11 @@ INT i; #ifndef TX_DISABLE_ERROR_CHECKING /* skip this test and pretend it passed */ - status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Attempt to create a pool with an invalid size. */ - status = _txe_block_pool_create(&pool_2, "pool 2", 100, pointer, 320, 777777); + status = _txe_block_pool_create(&pool_2, "pool 2", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3), 777777); if (status != TX_POOL_ERROR) { @@ -384,4 +385,3 @@ INT i; printf("SUCCESS!\n"); test_control_return(0); } - diff --git a/test/tx/regression/threadx_block_memory_suspension_test.c b/test/tx/regression/threadx_block_memory_suspension_test.c index da14f454e..9ce8939c5 100644 --- a/test/tx/regression/threadx_block_memory_suspension_test.c +++ b/test/tx/regression/threadx_block_memory_suspension_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -83,8 +84,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -308,4 +309,3 @@ CHAR *pointer_1; } } - diff --git a/test/tx/regression/threadx_block_memory_suspension_timeout_test.c b/test/tx/regression/threadx_block_memory_suspension_timeout_test.c index dc460662d..d3f8d4935 100644 --- a/test/tx/regression/threadx_block_memory_suspension_timeout_test.c +++ b/test/tx/regression/threadx_block_memory_suspension_timeout_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -84,8 +85,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -209,4 +210,3 @@ CHAR *pointer_1; thread_2_counter++; } } - diff --git a/test/tx/regression/threadx_block_memory_thread_terminate_test.c b/test/tx/regression/threadx_block_memory_thread_terminate_test.c index 6064891df..291ab0187 100644 --- a/test/tx/regression/threadx_block_memory_thread_terminate_test.c +++ b/test/tx/regression/threadx_block_memory_thread_terminate_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -69,8 +70,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, TX_TEST_BLOCK_POOL_BYTES(100, 3)); + pointer = pointer + TX_TEST_BLOCK_POOL_BYTES(100, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -182,4 +183,3 @@ CHAR *pointer_1; thread_1_counter++; } } - diff --git a/test/tx/regression/threadx_byte_memory_basic_test.c b/test/tx/regression/threadx_byte_memory_basic_test.c index 36f413317..d609712c1 100644 --- a/test/tx/regression/threadx_byte_memory_basic_test.c +++ b/test/tx/regression/threadx_byte_memory_basic_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" typedef struct BYTE_MEMORY_TEST_STRUCT { @@ -91,7 +92,7 @@ CHAR *pointer; /* Attempt to create a byte pool from a timer. */ pointer = (CHAR *) 0x30000; - status = tx_byte_pool_create(&pool_2, "pool 2", pointer, 108); + status = tx_byte_pool_create(&pool_2, "pool 2", pointer, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -103,7 +104,7 @@ CHAR *pointer; /* Attempt to create a byte pool with an invalid size. */ status = _txe_byte_pool_create(&pool_3, "pool 3", pointer, - 108, (sizeof(TX_BYTE_POOL)+1)); + TX_TEST_BYTE_POOL_BYTES(108), (sizeof(TX_BYTE_POOL)+1)); /* Check status. */ if (status != TX_POOL_ERROR) @@ -186,7 +187,7 @@ UINT status; /* Attempt to create a byte pool from an ISR. */ - status = tx_byte_pool_create(&pool_2, "pool 0", (void *) 0x100000, 108); + status = tx_byte_pool_create(&pool_2, "pool 0", (void *) 0x100000, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_CALLER_ERROR) @@ -256,8 +257,8 @@ CHAR *pointer; } /* Create byte pools 0 and 1. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_CAPACITY_BYTES(24, 3)); + pointer = pointer + TX_TEST_BYTE_POOL_CAPACITY_BYTES(24, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -267,8 +268,8 @@ CHAR *pointer; test_control_return(1); } - status = tx_byte_pool_create(&pool_1, "pool 1", pointer, 200); - pointer = pointer + 200; + status = tx_byte_pool_create(&pool_1, "pool 1", pointer, TX_TEST_BYTE_POOL_BYTES(200)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(200); /* Check status. */ if (status != TX_SUCCESS) @@ -279,9 +280,9 @@ CHAR *pointer; } /* Test for search pointer issue on wrapped seach with prior block to search pointer merged. */ - status = tx_byte_pool_create(&pool_4, "pool 4", pointer, 300); + status = tx_byte_pool_create(&pool_4, "pool 4", pointer, TX_TEST_BYTE_POOL_CAPACITY_BYTES(84, 3)); pool_4_memory = pointer; - pointer = pointer + 300; + pointer = pointer + TX_TEST_BYTE_POOL_CAPACITY_BYTES(84, 3); /* Check status. */ if (status != TX_SUCCESS) @@ -353,7 +354,7 @@ CHAR *pointer_2; CHAR *pointer_3; CHAR *pointer_4; INT i; -ULONG array[20]; +TX_TEST_POINTER_WORD array[20]; UCHAR *save_search; @@ -394,7 +395,7 @@ UCHAR *save_search; /* Try to create a NULL pool. */ pointer_1 = (CHAR *) 0x30000; - status = tx_byte_pool_create(TX_NULL, "pool 0", pointer_1, 108); + status = tx_byte_pool_create(TX_NULL, "pool 0", pointer_1, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_POOL_ERROR) @@ -406,7 +407,7 @@ UCHAR *save_search; } /* Try to create the same pool. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer_1, 108); + status = tx_byte_pool_create(&pool_0, "pool 0", pointer_1, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_POOL_ERROR) @@ -418,7 +419,7 @@ UCHAR *save_search; } /* Try to create a pool with a NULL start address. */ - status = tx_byte_pool_create(&pool_2, "pool 2", TX_NULL, 108); + status = tx_byte_pool_create(&pool_2, "pool 2", TX_NULL, TX_TEST_BYTE_POOL_BYTES(108)); /* Check status. */ if (status != TX_PTR_ERROR) @@ -626,7 +627,7 @@ UCHAR *save_search; /* Test another bad block release.... pool pointer is not a valid pool! */ array[0] = 0; - array[1] = (ULONG) &array[3]; + TX_TEST_STORE_POINTER(array[1], &array[3]); array[2] = 0; array[3] = 0; status = _tx_byte_release(&array[2]); @@ -865,7 +866,7 @@ UCHAR *save_search; } /* Move the search pointer to the third block to exercise that code in the byte search algorithm. */ - pool_0.tx_byte_pool_search = (UCHAR *) pointer_3-8; + pool_0.tx_byte_pool_search = (UCHAR *) pointer_3 - TX_TEST_BYTE_POOL_SEARCH_OFFSET; /* Now allocate the block again. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer_2, 24, TX_NO_WAIT); @@ -885,7 +886,7 @@ UCHAR *save_search; status += tx_byte_release(pointer_1); /* Move the search pointer to the third block to exercise that code in the byte search algorithm. */ - pool_0.tx_byte_pool_search = (UCHAR *) pointer_3-8; + pool_0.tx_byte_pool_search = (UCHAR *) pointer_3 - TX_TEST_BYTE_POOL_SEARCH_OFFSET; /* Allocate a large block to force the search pointer update. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer_3, 88, TX_NO_WAIT); @@ -961,7 +962,7 @@ UCHAR *save_search; } /* Create pool 4. */ - status = tx_byte_pool_create(&pool_4, "pool 4", pool_4_memory, 300); + status = tx_byte_pool_create(&pool_4, "pool 4", pool_4_memory, TX_TEST_BYTE_POOL_CAPACITY_BYTES(84, 3)); /* Check status. */ if (status != TX_SUCCESS) @@ -1061,4 +1062,3 @@ UCHAR *save_search; printf("SUCCESS!\n"); test_control_return(0); } - diff --git a/test/tx/regression/threadx_byte_memory_information_test.c b/test/tx/regression/threadx_byte_memory_information_test.c index 8a6192694..df607acb4 100644 --- a/test/tx/regression/threadx_byte_memory_information_test.c +++ b/test/tx/regression/threadx_byte_memory_information_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" #include "tx_byte_pool.h" @@ -193,7 +194,7 @@ CHAR *pointer; } /* Create the byte_pool with one byte. */ - status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, 100); + status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(100)); pointer = pointer + 100; /* Check for status. */ diff --git a/test/tx/regression/threadx_byte_memory_prioritize_test.c b/test/tx/regression/threadx_byte_memory_prioritize_test.c index 294071350..9160db91a 100644 --- a/test/tx/regression/threadx_byte_memory_prioritize_test.c +++ b/test/tx/regression/threadx_byte_memory_prioritize_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" /* Define the ISR dispatch. */ @@ -197,7 +198,7 @@ CHAR *pointer; } /* Create the byte_pool with one byte. */ - status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, 100); + status = tx_byte_pool_create(&byte_pool_0, "byte_pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(100)); pointer = pointer + 100; /* Check for status. */ @@ -495,4 +496,3 @@ VOID *pointer; thread_6_counter++; } } - diff --git a/test/tx/regression/threadx_byte_memory_suspension_test.c b/test/tx/regression/threadx_byte_memory_suspension_test.c index ba159894b..2736195dc 100644 --- a/test/tx/regression/threadx_byte_memory_suspension_test.c +++ b/test/tx/regression/threadx_byte_memory_suspension_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -121,8 +122,8 @@ CHAR *pointer; } /* Create byte pool 0. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/tx/regression/threadx_byte_memory_suspension_timeout_test.c b/test/tx/regression/threadx_byte_memory_suspension_timeout_test.c index 5e94df97a..c5f8adac1 100644 --- a/test/tx/regression/threadx_byte_memory_suspension_timeout_test.c +++ b/test/tx/regression/threadx_byte_memory_suspension_timeout_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -85,8 +86,8 @@ CHAR *pointer; } /* Create byte pool 0. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/tx/regression/threadx_byte_memory_thread_contention_test.c b/test/tx/regression/threadx_byte_memory_thread_contention_test.c index da2634e31..1c4deedaa 100644 --- a/test/tx/regression/threadx_byte_memory_thread_contention_test.c +++ b/test/tx/regression/threadx_byte_memory_thread_contention_test.c @@ -3,6 +3,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -89,8 +90,8 @@ CHAR *pointer; } /* Create byte pool 0. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Save off the intial pool size. */ initial_pool_size = pool_0.tx_byte_pool_available; diff --git a/test/tx/regression/threadx_byte_memory_thread_terminate_test.c b/test/tx/regression/threadx_byte_memory_thread_terminate_test.c index 4805ba571..35a4615aa 100644 --- a/test/tx/regression/threadx_byte_memory_thread_terminate_test.c +++ b/test/tx/regression/threadx_byte_memory_thread_terminate_test.c @@ -2,6 +2,7 @@ #include #include "tx_api.h" +#include "threadx_test_port.h" static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; @@ -68,8 +69,8 @@ CHAR *pointer; } /* Create byte pools 0 and 1. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, TX_TEST_BYTE_POOL_BYTES(108)); + pointer = pointer + TX_TEST_BYTE_POOL_BYTES(108); /* Check status. */ if (status != TX_SUCCESS) @@ -174,4 +175,3 @@ CHAR *pointer; thread_1_counter++; } } - diff --git a/test/tx/regression/threadx_event_flag_isr_set_clear_test.c b/test/tx/regression/threadx_event_flag_isr_set_clear_test.c index 7f89a06fb..4cb004d51 100644 --- a/test/tx/regression/threadx_event_flag_isr_set_clear_test.c +++ b/test/tx/regression/threadx_event_flag_isr_set_clear_test.c @@ -355,5 +355,3 @@ static void timer_0_entry(ULONG input) { timer_0_counter++; } - - diff --git a/test/tx/regression/threadx_event_flag_suspension_timeout_test.c b/test/tx/regression/threadx_event_flag_suspension_timeout_test.c index 149cb58fa..ebb39dd8a 100644 --- a/test/tx/regression/threadx_event_flag_suspension_timeout_test.c +++ b/test/tx/regression/threadx_event_flag_suspension_timeout_test.c @@ -180,13 +180,13 @@ UINT status; /* Check the run counters. */ if (((thread_1_counter != 32) -#ifdef __linux__ +#if defined(__linux__) || defined(_WIN32) && (thread_1_counter != 33) /* Depending on the starting time, thread 1 can run either 32 or 33 rounds. */ #endif ) || ((thread_2_counter != 13) -#ifdef __linux__ - && (thread_2_counter != 14) /* When CPU starves, the thread 2 can run 14 ronuds. */ +#if defined(__linux__) || defined(_WIN32) + && (thread_2_counter != 14) /* When CPU starves, the thread 2 can run 14 rounds. */ #endif )) { diff --git a/test/tx/regression/threadx_initialize_kernel_setup_test.c b/test/tx/regression/threadx_initialize_kernel_setup_test.c index 2ef8e1580..7f12b872f 100644 --- a/test/tx/regression/threadx_initialize_kernel_setup_test.c +++ b/test/tx/regression/threadx_initialize_kernel_setup_test.c @@ -1,6 +1,8 @@ /* This test is designed to test kernel setup functionality in ThreadX. */ +// Some portions generated by Codex (gpt 5.4). #include +#include #include "tx_api.h" #include "tx_initialize.h" #include "tx_thread.h" @@ -30,20 +32,7 @@ UINT test_byte_pool_create_init; UINT test_block_pool_create_init; UINT test_timer_create_init; - -__attribute__((weak)) void abort_all_threads_suspended_on_mutex(void) -{ -} - -__attribute__((weak)) void suspend_lowest_priority(void) -{ -} - -__attribute__((weak)) void abort_and_resume_byte_allocating_thread(void) -{ -} - -void main() +int main(void) { /* Setup the ThreadX kernel. */ @@ -59,6 +48,8 @@ void main() printf("Running Initialize Kernel Setup Test................................ ERROR!\n"); exit(1); } + + return(0); } void test_application_define(void *first_unused_memory){} @@ -78,4 +69,4 @@ void delete_timer_thread(void) _tx_thread_delete(&_tx_timer_thread); } -#endif \ No newline at end of file +#endif diff --git a/test/tx/regression/threadx_semaphore_timeout_test.c b/test/tx/regression/threadx_semaphore_timeout_test.c index 96585337a..c2fa83ae5 100644 --- a/test/tx/regression/threadx_semaphore_timeout_test.c +++ b/test/tx/regression/threadx_semaphore_timeout_test.c @@ -163,5 +163,3 @@ ULONG now; test_control_return(0); } } - - diff --git a/test/tx/regression/threadx_test_port.h b/test/tx/regression/threadx_test_port.h new file mode 100644 index 000000000..b8502b4de --- /dev/null +++ b/test/tx/regression/threadx_test_port.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef THREADX_TEST_PORT_H +#define THREADX_TEST_PORT_H + +#include "tx_api.h" + +/* Preserve the original 32-bit test expectations for pool capacity. */ + +#define TX_TEST_BYTE_POOL_BASELINE_OVERHEAD ((ULONG) (2U * sizeof(ULONG))) +#define TX_TEST_BYTE_POOL_OVERHEAD ((ULONG) (sizeof(VOID *) + sizeof(ALIGN_TYPE))) +#define TX_TEST_BYTE_POOL_ALIGN(a) ((ULONG) (((((ULONG) (a)) + ((ULONG) sizeof(ALIGN_TYPE)) - ((ULONG) 1)) / ((ULONG) sizeof(ALIGN_TYPE))) * ((ULONG) sizeof(ALIGN_TYPE)))) +#define TX_TEST_BYTE_POOL_BYTES(a) ((ULONG) ((a) + (2U * (TX_TEST_BYTE_POOL_OVERHEAD - TX_TEST_BYTE_POOL_BASELINE_OVERHEAD)))) +#define TX_TEST_BYTE_POOL_CAPACITY_BYTES(a, b) ((ULONG) (((b) * TX_TEST_BYTE_POOL_ALIGN(a)) + (((b) + 1U) * TX_TEST_BYTE_POOL_OVERHEAD))) +#define TX_TEST_BYTE_POOL_SEARCH_OFFSET TX_TEST_BYTE_POOL_OVERHEAD + +#define TX_TEST_BLOCK_POOL_ALIGN(a) ((ULONG) (((((ULONG) (a)) + ((ULONG) sizeof(ALIGN_TYPE)) - ((ULONG) 1)) / ((ULONG) sizeof(ALIGN_TYPE))) * ((ULONG) sizeof(ALIGN_TYPE)))) +#define TX_TEST_BLOCK_POOL_BYTES(a, b) ((ULONG) ((b) * (TX_TEST_BLOCK_POOL_ALIGN(a) + ((ULONG) sizeof(UCHAR *))))) + +typedef ALIGN_TYPE TX_TEST_POINTER_WORD; + +#define TX_TEST_STORE_POINTER(a, b) (a) = ((TX_TEST_POINTER_WORD) TX_POINTER_TO_ALIGN_TYPE_CONVERT(b)) + +#endif diff --git a/test/tx/regression/threadx_thread_basic_execution_test.c b/test/tx/regression/threadx_thread_basic_execution_test.c index af2c8fa1c..a0f65a8cc 100644 --- a/test/tx/regression/threadx_thread_basic_execution_test.c +++ b/test/tx/regression/threadx_thread_basic_execution_test.c @@ -11,6 +11,7 @@ #include "tx_queue.h" #include "tx_semaphore.h" #include "tx_thread.h" +#include "tx_timer.h" typedef struct THREAD_MEMORY_TEST_STRUCT @@ -52,6 +53,9 @@ static unsigned long timer_executed = 0; static unsigned long isr_executed = 0; +extern TX_TIMER_INTERNAL *_tx_timer_expired_timer_ptr; + + /* Define task prototypes. */ static void thread_0_entry(ULONG task_input); @@ -386,7 +390,22 @@ VOID (*temp_mutex_release)(TX_THREAD *thread_ptr); test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL; test_thread.tx_thread_suspending = TX_TRUE; test_thread.tx_thread_delayed_suspend = TX_TRUE; +#if defined(_WIN64) + { + TX_TIMER_INTERNAL timeout_timer; + TX_TIMER_INTERNAL *saved_expired_timer_ptr; + + + TX_MEMSET(&timeout_timer, 0, sizeof(TX_TIMER_INTERNAL)); + saved_expired_timer_ptr = _tx_timer_expired_timer_ptr; + _tx_timer_expired_timer_ptr = &timeout_timer; + timeout_timer.tx_timer_internal_extension_ptr = (VOID *) &test_thread; + _tx_thread_timeout(0); + _tx_timer_expired_timer_ptr = saved_expired_timer_ptr; + } +#else _tx_thread_timeout((ULONG) &test_thread); +#endif /* Setup test thread to make sure _tx_thread_terminate can handle a NULL mutex release function pointer. */ temp_mutex_release = _tx_thread_mutex_release; @@ -975,5 +994,3 @@ TX_INTERRUPT_SAVE_AREA #endif #endif - - diff --git a/test/tx/regression/threadx_thread_simple_sleep_non_clear_test.c b/test/tx/regression/threadx_thread_simple_sleep_non_clear_test.c index df8bb62b6..99c5b848a 100644 --- a/test/tx/regression/threadx_thread_simple_sleep_non_clear_test.c +++ b/test/tx/regression/threadx_thread_simple_sleep_non_clear_test.c @@ -89,4 +89,3 @@ ULONG now; test_control_return(1); } } - diff --git a/test/tx/regression/threadx_thread_simple_sleep_test.c b/test/tx/regression/threadx_thread_simple_sleep_test.c index f038fafd5..45d42cb00 100644 --- a/test/tx/regression/threadx_thread_simple_sleep_test.c +++ b/test/tx/regression/threadx_thread_simple_sleep_test.c @@ -84,4 +84,3 @@ ULONG now; test_control_return(1); } } - diff --git a/test/tx/regression/threadx_thread_sleep_for_100ticks_test.c b/test/tx/regression/threadx_thread_sleep_for_100ticks_test.c index 344672461..4b257e8cb 100644 --- a/test/tx/regression/threadx_thread_sleep_for_100ticks_test.c +++ b/test/tx/regression/threadx_thread_sleep_for_100ticks_test.c @@ -432,4 +432,3 @@ volatile ULONG value = 0; test_control_return(0); } } - diff --git a/test/tx/regression/threadx_time_get_set_test.c b/test/tx/regression/threadx_time_get_set_test.c index 06b8e6f53..84b099472 100644 --- a/test/tx/regression/threadx_time_get_set_test.c +++ b/test/tx/regression/threadx_time_get_set_test.c @@ -104,4 +104,3 @@ ULONG current_time; test_control_return(0); } } - diff --git a/test/tx/regression/threadx_timer_multiple_accuracy_test.c b/test/tx/regression/threadx_timer_multiple_accuracy_test.c index af9957d8b..206f28025 100644 --- a/test/tx/regression/threadx_timer_multiple_accuracy_test.c +++ b/test/tx/regression/threadx_timer_multiple_accuracy_test.c @@ -183,4 +183,3 @@ static void timer_2_expiration(ULONG timer_input) /* Process timer expiration. */ timer_2_counter++; } - diff --git a/test/tx/regression/threadx_timer_multiple_test.c b/test/tx/regression/threadx_timer_multiple_test.c index 688a7d5ba..46742a385 100644 --- a/test/tx/regression/threadx_timer_multiple_test.c +++ b/test/tx/regression/threadx_timer_multiple_test.c @@ -346,4 +346,3 @@ static void timer_2_expiration(ULONG timer_input) /* Process timer expiration. */ timer_2_counter++; } -