/*************************************************************************** * Copyright (C) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * AI Disclosure: This file was largely AI-generated by Codex (gpt 5.4). * The AI-generated portions may be considered public domain (CC0-1.0) * and not subject to the project's licence. The human contributor has * reviewed and verified that the code is correct. * * SPDX-License-Identifier: MIT and CC0-1.0 **************************************************************************/ // Some portions generated by Codex (gpt 5.5). // Some portions generated by GitHub Copilot (claude-sonnet-4.6). #define TX_SOURCE_CODE #define TX_THREAD_SMP_SOURCE_CODE #include "tx_api.h" #include "tx_thread.h" #include "tx_timer.h" #include #include #pragma comment (lib, "Winmm.lib") #if (TX_WIN32_USE_ADDRESS_WAIT != 0) #pragma comment (lib, "Synchronization.lib") #endif TX_WIN32_CRITICAL_SECTION _tx_win32_critical_section; HANDLE _tx_win32_scheduler_event; DWORD _tx_win32_scheduler_id; ULONG _tx_win32_global_int_disabled_flag; LARGE_INTEGER _tx_win32_time_stamp; ULONG _tx_win32_system_error; HANDLE _tx_win32_timer_handle; HANDLE _tx_win32_timer_thread_handle; DWORD _tx_win32_timer_thread_id; HANDLE _tx_win32_isr_semaphore; UINT _tx_win32_timer_waiting; TX_THREAD_SMP_CORE_MAPPING _tx_win32_virtual_cores[TX_THREAD_SMP_MAX_CORES]; __declspec(thread) int _tx_win32_threadx_thread = 0; __declspec(thread) UINT _tx_win32_current_virtual_core = 0U; static VOID _tx_win32_timer_start(VOID); static DWORD WINAPI _tx_win32_timer_thread_entry(LPVOID thread_input); static UINT _tx_win32_smp_current_core_get_internal(DWORD thread_id); static UINT _tx_win32_smp_thread_core_get(DWORD thread_id); #ifndef TX_WIN32_CONTENTION_PAUSE_COUNT #define TX_WIN32_CONTENTION_PAUSE_COUNT 256U #endif #ifdef TX_WIN32_PROFILE_ENABLE typedef struct TX_WIN32_PROFILE_STRUCT { ULONG64 tx_win32_profile_start_ticks; ULONG64 tx_win32_profile_scheduler_wait_ticks; ULONG64 tx_win32_profile_thread_run_wait_ticks; ULONG64 tx_win32_profile_thread_start_wait_ticks; ULONG64 tx_win32_profile_thread_start_ack_wait_ticks; ULONG64 tx_win32_profile_run_signal_to_wake_ticks; ULONG64 tx_win32_profile_run_wake_to_ack_ticks; ULONG64 tx_win32_profile_isr_wait_ticks; ULONG64 tx_win32_profile_isr_rendezvous_wait_ticks; ULONG64 tx_win32_profile_timer_wait_ticks; ULONG64 tx_win32_profile_suspend_ticks; ULONG64 tx_win32_profile_resume_ticks; ULONG64 tx_win32_profile_semaphore_reset_ticks; ULONG64 tx_win32_profile_critical_section_spin_ticks; ULONG64 tx_win32_profile_yield_ticks; ULONG tx_win32_profile_scheduler_wait_count; ULONG tx_win32_profile_thread_run_wait_count; ULONG tx_win32_profile_thread_start_wait_count; ULONG tx_win32_profile_thread_start_ack_wait_count; ULONG tx_win32_profile_run_signal_to_wake_count; ULONG tx_win32_profile_run_wake_to_ack_count; ULONG tx_win32_profile_isr_wait_count; ULONG tx_win32_profile_isr_rendezvous_wait_count; ULONG tx_win32_profile_timer_wait_count; ULONG tx_win32_profile_suspend_count; ULONG tx_win32_profile_resume_count; ULONG tx_win32_profile_semaphore_reset_count; ULONG tx_win32_profile_semaphore_reset_loops; ULONG tx_win32_profile_critical_section_spin_count; ULONG tx_win32_profile_yield_count; } TX_WIN32_PROFILE; static TX_WIN32_PROFILE _tx_win32_profile; static LARGE_INTEGER _tx_win32_profile_frequency; static ULONG64 _tx_win32_profile_time_get(VOID); static VOID _tx_win32_profile_accumulate(ULONG64 *ticks_total, ULONG *count_total, ULONG64 start_ticks); static double _tx_win32_profile_ticks_to_ms(ULONG64 ticks); #endif #ifdef TX_WIN32_DEBUG_ENABLE #ifndef TX_WIN32_DEBUG_EVENT_SIZE #define TX_WIN32_DEBUG_EVENT_SIZE 400 #endif typedef struct TX_WIN32_DEBUG_ENTRY_STRUCT { char *tx_win32_debug_entry_action; DWORD tx_win32_debug_entry_running_id; UINT tx_win32_debug_entry_core; LARGE_INTEGER tx_win32_debug_entry_timestamp; char *tx_win32_debug_entry_file; unsigned long tx_win32_debug_entry_line; TX_WIN32_CRITICAL_SECTION tx_win32_debug_entry_critical_section; TX_THREAD_SMP_PROTECT tx_win32_debug_protection; unsigned long tx_win32_debug_entry_int_disabled_flag; UINT tx_win32_debug_entry_preempt_disable; ULONG tx_win32_debug_entry_system_state[TX_THREAD_SMP_MAX_CORES]; TX_THREAD *tx_win32_debug_entry_current_thread[TX_THREAD_SMP_MAX_CORES]; DWORD tx_win32_debug_entry_current_thread_id[TX_THREAD_SMP_MAX_CORES]; TX_THREAD *tx_win32_debug_entry_execute_thread[TX_THREAD_SMP_MAX_CORES]; DWORD tx_win32_debug_entry_execute_thread_id[TX_THREAD_SMP_MAX_CORES]; } TX_WIN32_DEBUG_ENTRY; TX_WIN32_DEBUG_ENTRY _tx_win32_debug_entry_array[TX_WIN32_DEBUG_EVENT_SIZE]; unsigned long _tx_win32_debug_entry_index = 0; void _tx_win32_debug_entry_insert(char *action, char *file, unsigned long line) { UINT i; DWORD thread_id; QueryPerformanceCounter((LARGE_INTEGER *) &_tx_win32_time_stamp); thread_id = GetCurrentThreadId(); _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_action = action; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_core = _tx_win32_smp_current_core_get_internal(thread_id); if (_tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_core == TX_THREAD_SMP_MAX_CORES) { _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_core = 0U; } _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_timestamp = _tx_win32_time_stamp; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_file = file; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_line = line; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_protection = _tx_thread_smp_protection; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_preempt_disable = _tx_thread_preempt_disable; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_critical_section = _tx_win32_critical_section; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_int_disabled_flag = _tx_win32_global_int_disabled_flag; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_running_id = thread_id; for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) { _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_system_state[i] = _tx_thread_system_state[i]; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread[i] = _tx_thread_current_ptr[i]; if (_tx_thread_current_ptr[i] != TX_NULL) { _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id[i] = _tx_thread_current_ptr[i] -> tx_thread_win32_thread_id; } else { _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id[i] = 0U; } _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread[i] = _tx_thread_execute_ptr[i]; if (_tx_thread_execute_ptr[i] != TX_NULL) { _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id[i] = _tx_thread_execute_ptr[i] -> tx_thread_win32_thread_id; } else { _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id[i] = 0U; } } _tx_win32_debug_entry_index++; if (_tx_win32_debug_entry_index >= TX_WIN32_DEBUG_EVENT_SIZE) { _tx_win32_debug_entry_index = 0U; } } #endif void _tx_timer_interrupt(void); VOID _tx_thread_context_save(VOID); VOID _tx_thread_context_restore(VOID); VOID _tx_win32_timer_interrupt(VOID); #if defined(CTEST) || defined(BATCH_TEST) VOID test_interrupt_dispatch(VOID); extern VOID (*test_isr_dispatch)(void); #if (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) static VOID _tx_win32_timer_tick_process(VOID); static UINT _tx_win32_timer_fast_active; static UINT _tx_win32_timer_fast_count; #endif #endif VOID _tx_initialize_low_level(VOID) { UINT i; TIMECAPS tc; UINT timer_resolution; _tx_initialize_unused_memory = malloc(TX_WIN32_MEMORY_SIZE); _tx_win32_scheduler_id = GetCurrentThreadId(); _tx_win32_critical_section.tx_win32_critical_section_mutex_handle = CreateMutex(NULL, FALSE, NULL); _tx_win32_critical_section.tx_win32_critical_section_nested_count = 0U; _tx_win32_critical_section.tx_win32_critical_section_owner = 0U; if (_tx_win32_critical_section.tx_win32_critical_section_mutex_handle == NULL) { printf("ThreadX SMP Win64 error creating critical section mutex!\n"); while (1) { } } _tx_win32_scheduler_event = CreateEvent(NULL, FALSE, FALSE, NULL); _tx_win32_isr_semaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); if ((_tx_win32_scheduler_event == NULL) || (_tx_win32_isr_semaphore == NULL)) { printf("ThreadX SMP Win64 error creating semaphores!\n"); while (1) { } } for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) { _tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread_handle = NULL; _tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread_id = 0U; _tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread = TX_NULL; } _tx_win32_global_int_disabled_flag = TX_FALSE; _tx_win32_timer_waiting = 0U; #if (defined(CTEST) || defined(BATCH_TEST)) && (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) _tx_win32_timer_fast_active = TX_FALSE; _tx_win32_timer_fast_count = 0U; #endif #ifdef TX_WIN32_PROFILE_ENABLE if (_tx_win32_profile_frequency.QuadPart == 0) { QueryPerformanceFrequency(&_tx_win32_profile_frequency); } _tx_win32_profile_reset(); #endif if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { printf("ThreadX SMP Win64 error querying timer resolution!\n"); while (1) { } } timer_resolution = (UINT) min(max(tc.wPeriodMin, TX_WIN32_ISR_PERIODIC), tc.wPeriodMax); if (timeBeginPeriod(timer_resolution) != TIMERR_NOERROR) { printf("ThreadX SMP Win64 error configuring timer resolution!\n"); while (1) { } } #if (TX_WIN32_USE_HIGH_RESOLUTION_TIMER != 0) _tx_win32_timer_handle = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); if (_tx_win32_timer_handle == NULL) #endif { _tx_win32_timer_handle = CreateWaitableTimer(NULL, FALSE, NULL); } if (_tx_win32_timer_handle == NULL) { printf("ThreadX SMP Win64 error creating timer handle!\n"); while (1) { } } _tx_win32_timer_thread_handle = CreateThread(NULL, 0, _tx_win32_timer_thread_entry, NULL, 0, &_tx_win32_timer_thread_id); if (_tx_win32_timer_thread_handle == NULL) { printf("ThreadX SMP Win64 error creating timer thread!\n"); while (1) { } } SetThreadPriority(GetCurrentThread(), TX_WIN32_PRIORITY_SCHEDULE); SetThreadPriority(_tx_win32_timer_thread_handle, TX_WIN32_PRIORITY_ISR); } #ifdef TX_WIN32_PROFILE_ENABLE static ULONG64 _tx_win32_profile_time_get(VOID) { LARGE_INTEGER current_time; QueryPerformanceCounter(¤t_time); return((ULONG64) current_time.QuadPart); } static VOID _tx_win32_profile_accumulate(ULONG64 *ticks_total, ULONG *count_total, ULONG64 start_ticks) { ULONG64 end_ticks; end_ticks = _tx_win32_profile_time_get(); *ticks_total = *ticks_total + (end_ticks - start_ticks); *count_total = *count_total + 1UL; } static double _tx_win32_profile_ticks_to_ms(ULONG64 ticks) { double frequency; frequency = (double) _tx_win32_profile_frequency.QuadPart; if (frequency == 0.0) { return(0.0); } return((((double) ticks) * 1000.0) / frequency); } void _tx_win32_profile_reset(VOID) { TX_MEMSET(&_tx_win32_profile, 0, sizeof(TX_WIN32_PROFILE)); _tx_win32_profile.tx_win32_profile_start_ticks = _tx_win32_profile_time_get(); } void _tx_win32_profile_report(CHAR *label) { ULONG64 elapsed_ticks; elapsed_ticks = _tx_win32_profile_time_get() - _tx_win32_profile.tx_win32_profile_start_ticks; printf("**** Win64 SMP Profile (%s) elapsed_ms=%.3f\n", label, _tx_win32_profile_ticks_to_ms(elapsed_ticks)); printf("**** waits: scheduler=%lu/%.3fms run=%lu/%.3fms start=%lu/%.3fms start_ack=%lu/%.3fms sig_wake=%lu/%.3fms wake_ack=%lu/%.3fms isr=%lu/%.3fms isr_rdv=%lu/%.3fms timer=%lu/%.3fms\n", _tx_win32_profile.tx_win32_profile_scheduler_wait_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_scheduler_wait_ticks), _tx_win32_profile.tx_win32_profile_thread_run_wait_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_thread_run_wait_ticks), _tx_win32_profile.tx_win32_profile_thread_start_wait_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_thread_start_wait_ticks), _tx_win32_profile.tx_win32_profile_thread_start_ack_wait_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_thread_start_ack_wait_ticks), _tx_win32_profile.tx_win32_profile_run_signal_to_wake_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_run_signal_to_wake_ticks), _tx_win32_profile.tx_win32_profile_run_wake_to_ack_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_run_wake_to_ack_ticks), _tx_win32_profile.tx_win32_profile_isr_wait_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_isr_wait_ticks), _tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_ticks), _tx_win32_profile.tx_win32_profile_timer_wait_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_timer_wait_ticks)); printf("**** host ops: suspend=%lu/%.3fms resume=%lu/%.3fms reset=%lu/%.3fms reset_loops=%lu cs_spin=%lu/%.3fms yield=%lu/%.3fms\n", _tx_win32_profile.tx_win32_profile_suspend_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_suspend_ticks), _tx_win32_profile.tx_win32_profile_resume_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_resume_ticks), _tx_win32_profile.tx_win32_profile_semaphore_reset_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_semaphore_reset_ticks), _tx_win32_profile.tx_win32_profile_semaphore_reset_loops, _tx_win32_profile.tx_win32_profile_critical_section_spin_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_critical_section_spin_ticks), _tx_win32_profile.tx_win32_profile_yield_count, _tx_win32_profile_ticks_to_ms(_tx_win32_profile.tx_win32_profile_yield_ticks)); } void _tx_win32_profile_mark_run_signal(TX_THREAD *thread_ptr) { thread_ptr -> tx_thread_win32_profile_run_signal_ticks = _tx_win32_profile_time_get(); thread_ptr -> tx_thread_win32_profile_run_wake_ticks = 0ULL; } void _tx_win32_profile_mark_run_wake(TX_THREAD *thread_ptr) { ULONG64 current_ticks; ULONG64 signal_ticks; current_ticks = _tx_win32_profile_time_get(); signal_ticks = thread_ptr -> tx_thread_win32_profile_run_signal_ticks; if (signal_ticks != 0ULL) { _tx_win32_profile.tx_win32_profile_run_signal_to_wake_ticks = _tx_win32_profile.tx_win32_profile_run_signal_to_wake_ticks + (current_ticks - signal_ticks); _tx_win32_profile.tx_win32_profile_run_signal_to_wake_count++; } thread_ptr -> tx_thread_win32_profile_run_signal_ticks = 0ULL; thread_ptr -> tx_thread_win32_profile_run_wake_ticks = current_ticks; } void _tx_win32_profile_mark_start_ack(TX_THREAD *thread_ptr) { ULONG64 current_ticks; ULONG64 wake_ticks; current_ticks = _tx_win32_profile_time_get(); wake_ticks = thread_ptr -> tx_thread_win32_profile_run_wake_ticks; if (wake_ticks != 0ULL) { _tx_win32_profile.tx_win32_profile_run_wake_to_ack_ticks = _tx_win32_profile.tx_win32_profile_run_wake_to_ack_ticks + (current_ticks - wake_ticks); _tx_win32_profile.tx_win32_profile_run_wake_to_ack_count++; } thread_ptr -> tx_thread_win32_profile_run_wake_ticks = 0ULL; } #endif void _tx_initialize_start_interrupts(void) { _tx_win32_timer_start(); } void _tx_win32_critical_section_obtain(TX_WIN32_CRITICAL_SECTION *critical_section) { DWORD thread_id; LONG previous_owner; UINT contention_count; TX_THREAD *current_thread; UINT core_scan; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; #endif thread_id = GetCurrentThreadId(); if (critical_section -> tx_win32_critical_section_owner == thread_id) { critical_section -> tx_win32_critical_section_nested_count++; } else { /* If this is a ThreadX application thread, signal that it is about to * spin waiting for the critical section. context_save checks this flag * before calling SuspendThread(): a thread that is merely blocked on the * spinlock does not need to be OS-suspended — it cannot execute protected * code anyway, and will proceed automatically once the CS is released. * The flag is cleared below once the CS has been acquired. * * Identify the calling thread by scanning _tx_win32_virtual_cores for a * matching OS thread ID. As a fast path, check the TLS-cached virtual core * index first; fall back to a full scan only when the TLS value is stale * (e.g., after a type-1 scheduler hand-off moves the thread to a new core). * Worst case: the entry is momentarily absent and current_thread stays * TX_NULL, which simply skips the mutex_access optimisation this cycle. */ current_thread = TX_NULL; if (_tx_win32_threadx_thread != 0) { core_scan = _tx_win32_current_virtual_core; if ((core_scan < TX_THREAD_SMP_MAX_CORES) && (_tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread_id == thread_id)) { current_thread = _tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread; } else { for (core_scan = 0U; core_scan < TX_THREAD_SMP_MAX_CORES; core_scan++) { if (_tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread_id == thread_id) { current_thread = _tx_win32_virtual_cores[core_scan].tx_thread_smp_core_mapping_thread; break; } } } if (current_thread != TX_NULL) { current_thread -> tx_thread_win32_mutex_access = TX_TRUE; } } contention_count = 0U; #ifdef TX_WIN32_PROFILE_ENABLE start_ticks = _tx_win32_profile_time_get(); #endif do { previous_owner = InterlockedCompareExchange((LONG *) &(critical_section -> tx_win32_critical_section_owner), (LONG) thread_id, 0L); if (previous_owner != 0L) { #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile.tx_win32_profile_critical_section_spin_count++; #endif contention_count++; if (contention_count < TX_WIN32_CONTENTION_PAUSE_COUNT) { YieldProcessor(); } else { contention_count = 0U; _tx_win32_thread_yield(); } } } while (previous_owner != 0L); #ifdef TX_WIN32_PROFILE_ENABLE if (critical_section -> tx_win32_critical_section_owner == thread_id) { _tx_win32_profile.tx_win32_profile_critical_section_spin_ticks = _tx_win32_profile.tx_win32_profile_critical_section_spin_ticks + (_tx_win32_profile_time_get() - start_ticks); } #endif /* CS acquired; clear the spinning flag so the next timer ISR will see * the thread as normally running. The thread is now inside the CS body * and the ISR cannot reach context_save until we release the CS. */ if (current_thread != TX_NULL) { current_thread -> tx_thread_win32_mutex_access = TX_FALSE; } critical_section -> tx_win32_critical_section_nested_count = 1U; } } void _tx_win32_critical_section_release(TX_WIN32_CRITICAL_SECTION *critical_section) { DWORD thread_id; thread_id = GetCurrentThreadId(); if (critical_section -> tx_win32_critical_section_owner == thread_id) { if (critical_section -> tx_win32_critical_section_nested_count != 0U) { critical_section -> tx_win32_critical_section_nested_count--; if (critical_section -> tx_win32_critical_section_nested_count == 0U) { InterlockedExchange((LONG *) &(critical_section -> tx_win32_critical_section_owner), 0L); } } } else { _tx_win32_system_error++; } } void _tx_win32_critical_section_release_all(TX_WIN32_CRITICAL_SECTION *critical_section) { DWORD thread_id; thread_id = GetCurrentThreadId(); if (critical_section -> tx_win32_critical_section_owner == thread_id) { if (critical_section -> tx_win32_critical_section_nested_count != 0U) { critical_section -> tx_win32_critical_section_nested_count = 0U; InterlockedExchange((LONG *) &(critical_section -> tx_win32_critical_section_owner), 0L); } } else { _tx_win32_system_error++; } } UINT _tx_win32_smp_current_core_get(void) { UINT core; DWORD thread_id; UINT critical_section_owned; thread_id = GetCurrentThreadId(); critical_section_owned = (UINT) (_tx_win32_critical_section.tx_win32_critical_section_owner == thread_id); if (critical_section_owned == TX_FALSE) { _tx_win32_critical_section_obtain(&_tx_win32_critical_section); } if (_tx_win32_threadx_thread != 0) { core = _tx_win32_smp_thread_core_get(thread_id); if (core == TX_THREAD_SMP_MAX_CORES) { core = _tx_win32_current_virtual_core; } else { _tx_win32_current_virtual_core = core; } } else { core = _tx_win32_smp_current_core_get_internal(thread_id); if (core == TX_THREAD_SMP_MAX_CORES) { core = 0U; } } if (critical_section_owned == TX_FALSE) { _tx_win32_critical_section_release(&_tx_win32_critical_section); } return(core); } static UINT _tx_win32_smp_thread_core_get(DWORD thread_id) { UINT core; UINT i; TX_THREAD *thread_ptr; core = TX_THREAD_SMP_MAX_CORES; for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) { if (_tx_win32_virtual_cores[i].tx_thread_smp_core_mapping_thread_id == thread_id) { core = i; break; } } if (core == TX_THREAD_SMP_MAX_CORES) { for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) { thread_ptr = _tx_thread_current_ptr[i]; if ((thread_ptr != TX_NULL) && (thread_ptr -> tx_thread_win32_thread_id == thread_id)) { core = i; break; } } } return(core); } static UINT _tx_win32_smp_current_core_get_internal(DWORD thread_id) { UINT core; UINT i; TX_THREAD *thread_ptr; core = _tx_win32_smp_thread_core_get(thread_id); if (core == TX_THREAD_SMP_MAX_CORES) { for (i = 0U; i < TX_THREAD_SMP_MAX_CORES; i++) { thread_ptr = _tx_thread_execute_ptr[i]; if ((thread_ptr != TX_NULL) && (thread_ptr -> tx_thread_win32_thread_id == thread_id)) { core = i; break; } } } return(core); } void _tx_win32_thread_suspend(HANDLE thread_handle) { #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif if (SuspendThread(thread_handle) == (DWORD) -1) { _tx_win32_system_error++; while (1) { } } #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_suspend_ticks, &_tx_win32_profile.tx_win32_profile_suspend_count, start_ticks); #endif } void _tx_win32_thread_resume(HANDLE thread_handle) { DWORD suspend_count; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif do { suspend_count = ResumeThread(thread_handle); if (suspend_count == (DWORD) -1) { _tx_win32_system_error++; while (1) { } } } while (suspend_count > 1U); #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_resume_ticks, &_tx_win32_profile.tx_win32_profile_resume_count, start_ticks); #endif } void _tx_win32_thread_sleep(ULONG milliseconds) { if (milliseconds == 0U) { milliseconds = 1U; } Sleep((DWORD) milliseconds); } void _tx_win32_thread_yield(VOID) { #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif if (SwitchToThread() == 0) { Sleep(0); } #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_yield_ticks, &_tx_win32_profile.tx_win32_profile_yield_count, start_ticks); #endif } void _tx_win32_semaphore_reset(HANDLE semaphore_handle) { #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif while (WaitForSingleObject(semaphore_handle, 0) == WAIT_OBJECT_0) { #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile.tx_win32_profile_semaphore_reset_loops++; #endif } #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_semaphore_reset_ticks, &_tx_win32_profile.tx_win32_profile_semaphore_reset_count, start_ticks); #endif } LONG _tx_win32_thread_start_sequence_get(TX_THREAD *thread_ptr) { #if (TX_WIN32_USE_ADDRESS_WAIT != 0) return(thread_ptr -> tx_thread_win32_start_sequence); #else TX_PARAMETER_NOT_USED(thread_ptr); return(0L); #endif } void _tx_win32_thread_run_signal(TX_THREAD *thread_ptr) { #if (TX_WIN32_USE_ADDRESS_WAIT != 0) (void) InterlockedIncrement(&(thread_ptr -> tx_thread_win32_run_sequence)); WakeByAddressSingle(&(thread_ptr -> tx_thread_win32_run_sequence)); #else ReleaseSemaphore(thread_ptr -> tx_thread_win32_thread_run_semaphore, 1, NULL); #endif } DWORD _tx_win32_wait_for_thread_run(TX_THREAD *thread_ptr) { DWORD wait_status; #if (TX_WIN32_USE_ADDRESS_WAIT != 0) LONG observed_sequence; LONG current_sequence; #endif #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif #if (TX_WIN32_USE_ADDRESS_WAIT != 0) wait_status = WAIT_OBJECT_0; observed_sequence = thread_ptr -> tx_thread_win32_run_sequence_seen; current_sequence = thread_ptr -> tx_thread_win32_run_sequence; while (current_sequence == observed_sequence) { if (WaitOnAddress(&(thread_ptr -> tx_thread_win32_run_sequence), &observed_sequence, sizeof(thread_ptr -> tx_thread_win32_run_sequence), INFINITE) == 0) { _tx_win32_system_error++; wait_status = WAIT_FAILED; break; } current_sequence = thread_ptr -> tx_thread_win32_run_sequence; } if (wait_status == WAIT_OBJECT_0) { thread_ptr -> tx_thread_win32_run_sequence_seen = current_sequence; } #else wait_status = WaitForSingleObject(thread_ptr -> tx_thread_win32_thread_run_semaphore, INFINITE); #endif #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_run_wait_ticks, &_tx_win32_profile.tx_win32_profile_thread_run_wait_count, start_ticks); #endif return(wait_status); } void _tx_win32_thread_start_ack_signal(TX_THREAD *thread_ptr) { #if (TX_WIN32_USE_ADDRESS_WAIT != 0) (void) InterlockedIncrement(&(thread_ptr -> tx_thread_win32_start_sequence)); WakeByAddressSingle(&(thread_ptr -> tx_thread_win32_start_sequence)); #else ReleaseSemaphore(thread_ptr -> tx_thread_win32_thread_start_semaphore, 1, NULL); #endif } DWORD _tx_win32_wait_for_scheduler_event(VOID) { DWORD wait_status; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif /* Use a 2 ms timeout (matching the Linux SMP port's sem_timedwait interval) so that * a missed SetEvent() does not stall the scheduler indefinitely and so that the * timer-tick phase varies slightly between iterations, preventing a systematic * resonance where the ISR always fires outside the _tx_thread_preempt_disable * window (which would make tests like threadx_thread_wait_abort_and_isr_test * take an unreasonably long time to complete). */ wait_status = WaitForSingleObject(_tx_win32_scheduler_event, 2U); #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_scheduler_wait_ticks, &_tx_win32_profile.tx_win32_profile_scheduler_wait_count, start_ticks); #endif return(wait_status); } DWORD _tx_win32_wait_for_thread_run_semaphore(HANDLE semaphore_handle) { DWORD wait_status; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif wait_status = WaitForSingleObject(semaphore_handle, INFINITE); #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_run_wait_ticks, &_tx_win32_profile.tx_win32_profile_thread_run_wait_count, start_ticks); #endif return(wait_status); } DWORD _tx_win32_wait_for_thread_start_semaphore(HANDLE semaphore_handle) { DWORD wait_status; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif wait_status = WaitForSingleObject(semaphore_handle, INFINITE); #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_start_wait_ticks, &_tx_win32_profile.tx_win32_profile_thread_start_wait_count, start_ticks); #endif return(wait_status); } DWORD _tx_win32_wait_for_thread_start_ack(TX_THREAD *thread_ptr, LONG start_sequence) { DWORD wait_status; #if (TX_WIN32_USE_ADDRESS_WAIT != 0) LONG current_sequence; #endif #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif #if (TX_WIN32_USE_ADDRESS_WAIT != 0) wait_status = WAIT_OBJECT_0; current_sequence = thread_ptr -> tx_thread_win32_start_sequence; while (current_sequence == start_sequence) { if (WaitOnAddress(&(thread_ptr -> tx_thread_win32_start_sequence), &start_sequence, sizeof(thread_ptr -> tx_thread_win32_start_sequence), INFINITE) == 0) { _tx_win32_system_error++; wait_status = WAIT_FAILED; break; } current_sequence = thread_ptr -> tx_thread_win32_start_sequence; } #else TX_PARAMETER_NOT_USED(start_sequence); wait_status = WaitForSingleObject(thread_ptr -> tx_thread_win32_thread_start_semaphore, INFINITE); #endif #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_thread_start_ack_wait_ticks, &_tx_win32_profile.tx_win32_profile_thread_start_ack_wait_count, start_ticks); #endif return(wait_status); } DWORD _tx_win32_wait_for_isr_semaphore(VOID) { DWORD wait_status; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif wait_status = WaitForSingleObject(_tx_win32_isr_semaphore, INFINITE); #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_isr_wait_ticks, &_tx_win32_profile.tx_win32_profile_isr_wait_count, start_ticks); #endif return(wait_status); } DWORD _tx_win32_wait_for_isr_rendezvous(VOID) { DWORD wait_status; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif wait_status = WaitForSingleObject(_tx_win32_isr_semaphore, INFINITE); #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_ticks, &_tx_win32_profile.tx_win32_profile_isr_rendezvous_wait_count, start_ticks); #endif return(wait_status); } DWORD _tx_win32_wait_for_timer_object(VOID) { DWORD wait_status; #ifdef TX_WIN32_PROFILE_ENABLE ULONG64 start_ticks; start_ticks = _tx_win32_profile_time_get(); #endif wait_status = WaitForSingleObject(_tx_win32_timer_handle, INFINITE); #ifdef TX_WIN32_PROFILE_ENABLE _tx_win32_profile_accumulate(&_tx_win32_profile.tx_win32_profile_timer_wait_ticks, &_tx_win32_profile.tx_win32_profile_timer_wait_count, start_ticks); #endif return(wait_status); } static DWORD WINAPI _tx_win32_timer_thread_entry(LPVOID thread_input) { TX_PARAMETER_NOT_USED(thread_input); while (1) { _tx_win32_wait_for_timer_object(); _tx_win32_timer_interrupt(); _tx_win32_timer_start(); } } VOID _tx_win32_timer_interrupt(VOID) { _tx_thread_context_save(); #if defined(CTEST) || defined(BATCH_TEST) test_interrupt_dispatch(); #if (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) _tx_win32_timer_tick_process(); #else _tx_timer_interrupt(); #endif #else _tx_timer_interrupt(); #endif _tx_thread_context_restore(); } #if (defined(CTEST) || defined(BATCH_TEST)) && (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) static VOID _tx_win32_timer_tick_process(VOID) { if (test_isr_dispatch != TX_NULL) { if (_tx_win32_timer_fast_active == TX_FALSE) { _tx_win32_timer_fast_active = TX_TRUE; _tx_win32_timer_fast_count = 0U; } else { _tx_win32_timer_fast_count++; if (_tx_win32_timer_fast_count < TX_WIN32_TIMER_INTERRUPTS_PER_TICK) { return; } _tx_win32_timer_fast_count = 0U; } } else { _tx_win32_timer_fast_active = TX_FALSE; _tx_win32_timer_fast_count = 0U; } _tx_timer_interrupt(); } #endif static VOID _tx_win32_timer_start(VOID) { LARGE_INTEGER due_time; LONGLONG timer_period; #if (defined(CTEST) || defined(BATCH_TEST)) && (TX_WIN32_ISR_PERIODIC < TX_TIMER_PERIODIC) if (test_isr_dispatch != TX_NULL) { timer_period = (LONGLONG) TX_WIN32_ISR_PERIODIC; } else #endif { timer_period = (LONGLONG) TX_TIMER_PERIODIC; } due_time.QuadPart = -(timer_period * 10000LL); #if (TX_WIN32_USE_HIGH_RESOLUTION_TIMER != 0) if (SetWaitableTimerEx(_tx_win32_timer_handle, &due_time, 0, NULL, NULL, NULL, 0) == 0) #else if (SetWaitableTimer(_tx_win32_timer_handle, &due_time, 0, NULL, NULL, FALSE) == 0) #endif { printf("ThreadX SMP Win64 error starting timer!\n"); while (1) { } } }