Added win64 ports of ThreadX and ThreadX SMP (#529)

Windows x64 port and regression suite

 This PR adds the Windows x64 (Win64) simulation port for both the standalone
 and SMP variants of ThreadX, along with the full CMake build and test
 infrastructure needed to run the regression suite on Windows.


 New ports

 Win64 standalone (ports/win64/vs_2022): self-contained Windows simulation
 port using Win32 threading primitives as virtual cores. Includes CMake
 integration, build/test scripts, and MSVC project files.

 Win64 SMP (ports/win64_smp/vs_2022): multi-core Windows simulation port.
 Supports up to 4 virtual cores backed by Windows host threads.


 Scheduler and timer improvements

 The initial port used coarse polling and synchronous SuspendThread/ResumeThread
 pairs throughout the scheduler hot path. Several rounds of optimization reduced
 the SMP regression suite runtime from ~150 s to ~78 s (-48%), with no
 regressions:

 - Replaced scheduler polling with an event-driven wake path; switched the
   simulated timer to one-shot rearming to eliminate catch-up ticks.
 - Skip SuspendThread when _tx_thread_preempt_disable != 0 (new suspension
   type 3) -- the primary optimization, yielding up to 7.9x speedup on
   preemption-heavy tests.
 - Skip SuspendThread when a thread is spinning on the Win32 critical section
   (suspension type 4), and fix a stale-TLS bug in
   _tx_win32_critical_section_obtain that could stamp mutex_access on the
   wrong virtual core.
 - Added a 2 ms scheduler event timeout (matching the Linux SMP port) to
   prevent stalls on any missed SetEvent.
 - Enabled high-resolution waitable timers (SetWaitableTimerEx) for accurate
   100 Hz tick cadence.
 - Increased TX_WIN32_CONTENTION_PAUSE_COUNT from 64 to 256 to reduce
   SwitchToThread overhead under heavy CS contention.


 Build and test infrastructure

 - Hardened the Windows build wrapper (scripts/build_tx.ps1): invoke Ninja
   directly for Ninja build trees, fix timeout detection, add a default build
   timeout, and limit fallback replay to real timeout cases.
 - Added -Clean support to Windows test scripts to remove stale CTest state
   before each run.
 - Skip Visual Studio DevShell re-entry when the active MSVC environment
   already matches the requested architecture.
 - Fixed scripts/build_tx.sh (Linux) regression source generation: replaced
   brittle exact-string insertion with line-based matching so the interrupt
   dispatcher hook is inserted reliably for both simulator ports.


 Test suite updates

 - Introduced test/tx/regression/threadx_test_port.h with portable macros
   (TX_TEST_POINTER_WORD, TX_TEST_STORE_POINTER) for storing pointers in test
   arrays on 64-bit targets where ULONG remains 32-bit.
 - Adjusted pool-capacity and pointer-storage patterns in regression tests to
   use ALIGN_TYPE-sized slots, making the suite correct on 64-bit hosts.
 - Restored stricter event flag, sleep, and timer expectations now that
   port-level fixes make prior Windows accommodations unnecessary.
 - Tightened SMP watchdog and clean-build timeout defaults.


 Version metadata

 Updated Win32, Win64, and Win64 SMP port version strings to 6.5.1.202602.

 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
 Co-authored-by: Codex (gpt 5.5) <codex@openai.com>
This commit is contained in:
Frédéric Desbiens
2026-05-26 17:17:40 -04:00
committed by GitHub
parent e031a3d506
commit 2c16114a45
108 changed files with 7614 additions and 395 deletions
+19 -6
View File
@@ -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,24 @@ 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
)
endif()
option(THREADX_SMP "Build ThreadX SMP version" OFF)
if(THREADX_SMP)
@@ -95,4 +108,4 @@ set(CPACK_SOURCE_IGNORE_FILES
".*~$"
)
set(CPACK_VERBATIM_VARIABLES YES)
include(CPack)
include(CPack)
+3 -9
View File
@@ -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)
+9
View File
@@ -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)
+6 -1
View File
@@ -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
}
+3 -1
View File
@@ -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
}
+2 -1
View File
@@ -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
}
-1
View File
@@ -960,4 +960,3 @@ VOID _tx_thread_system_ni_resume(TX_THREAD *thread_ptr)
_tx_thread_system_resume(thread_ptr);
}
#endif
@@ -984,4 +984,3 @@ VOID _tx_thread_system_ni_suspend(TX_THREAD *thread_ptr, ULONG timeout)
_tx_thread_system_suspend(thread_ptr);
}
#endif
+96 -9
View File
@@ -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 <stdlib.h>
#include <string.h>
/* 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
@@ -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();
}
}
+19
View File
@@ -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
)
File diff suppressed because it is too large Load Diff
@@ -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 <stdio.h>
#include <stdlib.h>
#include <windows.h>
#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)
{
}
}
}
@@ -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);
}
@@ -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);
}
@@ -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 <winbase.h>
/* 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);
}
@@ -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++;
}
}
@@ -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 <stdio.h>
/* 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;
}
@@ -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 <stdio.h>
/**************************************************************************/
/* */
/* 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);
}
}
@@ -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);
}
+28
View File
@@ -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
)
File diff suppressed because it is too large Load Diff
@@ -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.
File diff suppressed because it is too large Load Diff
@@ -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);
}
@@ -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]++;
}
@@ -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);
}
@@ -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);
}
@@ -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());
}
@@ -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++;
}
}
}
@@ -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);
}
@@ -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);
}
@@ -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)
{
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
}
@@ -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 <stdio.h>
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);
}
@@ -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);
}
@@ -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);
}
+54
View File
@@ -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
}
+58
View File
@@ -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
}
+120
View File
@@ -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 ', ')"
}
+121
View File
@@ -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 ', ')"
}
File diff suppressed because it is too large Load Diff
@@ -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
+59 -41
View File
@@ -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()
+32 -8
View File
@@ -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_OBJECTS:test_utility>)
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()
@@ -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}")
+12 -7
View File
@@ -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)
include(CPack)

Some files were not shown because too many files have changed in this diff Show More