12#include "stk_config.h"
14#ifdef _STK_ARCH_X86_WIN32
21#define WIN32_LEAN_AND_MEAN
32#define WINAPI __stdcall
36typedef MMRESULT (WINAPI * timeBeginPeriodF)(
UINT uPeriod);
37static timeBeginPeriodF timeBeginPeriod =
nullptr;
39#define STK_X86_WIN32_CRITICAL_SECTION CRITICAL_SECTION
40#define STK_X86_WIN32_CRITICAL_SECTION_INIT(SES) ::InitializeCriticalSection(SES)
41#define STK_X86_WIN32_CRITICAL_SECTION_START(SES) ::EnterCriticalSection(SES)
42#define STK_X86_WIN32_CRITICAL_SECTION_END(SES) ::LeaveCriticalSection(SES)
43#define STK_X86_WIN32_MIN_RESOLUTION (1000)
44#define STK_X86_WIN32_GET_SP(STACK) (STACK + 2)
45#define SLK_UNLOCKED hw::SpinLock::UNLOCKED
46#define SLK_LOCKED hw::SpinLock::LOCKED
52 return (InterlockedCompareExchange(
53 reinterpret_cast<volatile LONG *
>(&lock), SLK_LOCKED, SLK_UNLOCKED) == SLK_UNLOCKED);
60 uint8_t sleep_time = 0;
61 uint32_t timeout = 0xFFFFFF;
64 while (!HW_SpinLockTryLock(lock))
72 for (
volatile int32_t spin = 100; (spin != 0); spin--)
77 if (lock == SLK_UNLOCKED)
91 InterlockedExchange(
reinterpret_cast<volatile LONG *
>(&lock), SLK_UNLOCKED);
94struct Win32ScopedCriticalSection
96 STK_X86_WIN32_CRITICAL_SECTION &m_sec;
98 explicit Win32ScopedCriticalSection(STK_X86_WIN32_CRITICAL_SECTION &sec) : m_sec(sec)
100 STK_X86_WIN32_CRITICAL_SECTION_START(&sec);
102 ~Win32ScopedCriticalSection()
104 STK_X86_WIN32_CRITICAL_SECTION_END(&m_sec);
110 LARGE_INTEGER m_freq;
111 LARGE_INTEGER m_start;
114 explicit HiResClockQPC()
116 QueryPerformanceFrequency(&m_freq);
117 QueryPerformanceCounter(&m_start);
120 static HiResClockQPC *GetInstance()
124 static HiResClockQPC clock;
130 LARGE_INTEGER current;
131 QueryPerformanceCounter(¤t);
134 return static_cast<Cycles>(current.QuadPart - m_start.QuadPart);
137 uint32_t GetFrequency()
139 return static_cast<uint32_t
>(m_freq.QuadPart);
147 : m_overrider(nullptr),
148 m_sleep_trap(nullptr),
149 m_exit_trap(nullptr),
150 m_winmm_dll(nullptr),
151 m_timer_thread(nullptr),
152 m_switch_event(nullptr),
153 m_pending_switch_id(TID_NONE),
154 m_tls(TLS_OUT_OF_INDEXES),
158 #if STK_TICKLESS_IDLE
167 void Initialize(IPlatform::IEventHandler *handler, IKernelService *service, Stack *exit_trap,
168 uint32_t resolution_us)
170 InitializeBase(handler, service, exit_trap, resolution_us);
172 m_sleep_trap =
nullptr;
173 m_exit_trap =
nullptr;
174 m_winmm_dll =
nullptr;
175 m_timer_thread =
nullptr;
177 m_stop_signal =
false;
180 m_pending_switch_id = TID_NONE;
181 #if STK_TICKLESS_IDLE
188 m_switch_event = CreateEventA(
nullptr, FALSE, FALSE,
nullptr);
192 if ((m_tls = TlsAlloc()) == TLS_OUT_OF_INDEXES)
199 STK_X86_WIN32_CRITICAL_SECTION_INIT(&m_cs);
207 if (m_tls != TLS_OUT_OF_INDEXES)
211 if (m_switch_event !=
nullptr)
213 CloseHandle(m_switch_event);
214 m_switch_event =
nullptr;
220 void LoadWindowsAPI()
222 HMODULE winmm = GetModuleHandleA(
"Winmm");
223 if (winmm ==
nullptr)
225 m_winmm_dll = winmm = LoadLibraryA(
"Winmm.dll");
229 timeBeginPeriod = (timeBeginPeriodF)GetProcAddress(winmm,
"timeBeginPeriod");
235 void UnloadWindowsAPI()
237 if (m_winmm_dll !=
nullptr)
239 FreeLibrary(m_winmm_dll);
240 m_winmm_dll =
nullptr;
246 TaskContext() : m_task(nullptr), m_stack(nullptr), m_thread(nullptr), m_thread_id(0), m_exiting(false)
249 void Initialize(ITask *task, Stack *stack)
263 const size_t stack_size = m_task->GetStackSize() *
sizeof(
Word);
265 m_thread = CreateThread(
nullptr, stack_size, &OnTaskRun,
this, CREATE_SUSPENDED, &m_thread_id);
268 static DWORD WINAPI OnTaskRun(LPVOID param);
274 volatile bool m_exiting;
277 void InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task);
278 void ConfigureTime();
279 void StartActiveTask();
280 void CreateTimerThreadAndJoin();
283 void SwitchContext();
285 void ForceContextSwitch(TId
id);
286 void OnForceContextSwitch();
287 void ReportTaskExit(TaskContext *task);
288 void Sleep(Timeout ticks);
289 bool SleepUntil(Ticks timestamp);
290 EWaitResult Wait(ISyncObject *sync_obj, IMutex *mutex, Timeout timeout);
292 Word GetCallerSP()
const;
298 return hw::PtrToWord(TlsGetValue(m_tls));
303 TlsSetValue(m_tls, hw::WordToPtr<void>(tp));
309 STK_X86_WIN32_CRITICAL_SECTION_START(&m_cs);
311 if (m_csu_nesting == 0)
314 if (GetCurrentThreadId() != m_timer_tid)
316 SuspendThread(m_timer_thread);
324 STK_KERNEL_PANIC(KERNEL_PANIC_CS_NESTING_OVERFLOW);
334 if (m_csu_nesting == 0)
337 if (GetCurrentThreadId() != m_timer_tid)
339 ResumeThread(m_timer_thread);
343 STK_X86_WIN32_CRITICAL_SECTION_END(&m_cs);
346 IPlatform::IEventOverrider *m_overrider;
350 HANDLE m_timer_thread;
351 HANDLE m_switch_event;
352 volatile TId m_pending_switch_id;
354 std::list<TaskContext *> m_tasks;
355 std::vector<HANDLE> m_task_threads;
360 STK_X86_WIN32_CRITICAL_SECTION m_cs;
361 uint8_t m_csu_nesting;
363 volatile bool m_stop_signal;
365s_StkPlatformContext[1];
385 return static_cast<DWORD
>((ticks *
GetContext().m_tick_resolution) / 1000U);
388static DWORD WINAPI TimerThread(LPVOID param)
393 DWORD wait_ms = TicksToMs(1U);
394 ctx.m_timer_tid = GetCurrentThreadId();
402 HANDLE wait_handles[] = { ctx.m_timer_thread, ctx.m_switch_event };
406 DWORD result = WaitForMultipleObjects(
STK_STATIC_ARRAY_SIZE(wait_handles), wait_handles, FALSE, wait_ms);
408 if (ctx.m_stop_signal)
413 if (result == (WAIT_OBJECT_0 + 1))
418 ctx.OnForceContextSwitch();
426 #if STK_TICKLESS_IDLE
427 wait_ms = TicksToMs(ctx.m_sleep_ticks);
434void Context::ConfigureTime()
437 if (m_tick_resolution < STK_X86_WIN32_MIN_RESOLUTION)
439 m_tick_resolution = STK_X86_WIN32_MIN_RESOLUTION;
446void Context::StartActiveTask()
452 ResumeThread(active_task->m_thread);
455void Context::CreateTimerThreadAndJoin()
463 m_handler->OnStart(m_stack_active);
468 m_timer_thread = CreateThread(
nullptr, 0, &TimerThread,
nullptr, 0,
nullptr);
470 SetThreadPriority(m_timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
472 while (!m_task_threads.empty())
474 DWORD result = WaitForMultipleObjects((DWORD)m_task_threads.size(), m_task_threads.data(), FALSE, INFINITE);
479 Win32ScopedCriticalSection __cs(m_cs);
482 for (std::vector<HANDLE>::iterator itr = m_task_threads.begin(); itr != m_task_threads.end(); ++itr)
484 if (result == (WAIT_OBJECT_0 + i))
488 m_task_threads.erase(itr);
498 if (m_timer_thread !=
nullptr)
500 WaitForSingleObject(m_timer_thread, INFINITE);
504void Context::Cleanup()
507 for (std::list<TaskContext *>::iterator itr = m_tasks.begin(); itr != m_tasks.end(); ++itr)
509 if ((*itr)->m_thread !=
nullptr)
511 CloseHandle((*itr)->m_thread);
512 (*itr)->m_thread =
nullptr;
518 if (m_timer_thread !=
nullptr)
520 CloseHandle(m_timer_thread);
521 m_timer_thread =
nullptr;
525 m_stop_signal =
false;
531void Context::ProcessTick()
533 Win32ScopedCriticalSection __cs(m_cs);
539 if (m_handler->OnTick(m_stack_idle, m_stack_active
540 #
if STK_TICKLESS_IDLE
549 m_sleep_ticks = ticks;
553void Context::SwitchContext()
556 if ((m_stack_idle != m_sleep_trap) && (m_stack_idle != m_exit_trap))
563 if (!idle_task->m_exiting)
565 SuspendThread(idle_task->m_thread);
570 if (m_stack_active == m_sleep_trap)
572 #if STK_TICKLESS_IDLE
573 const Timeout sleep_ticks = m_sleep_ticks;
578 if ((m_overrider ==
nullptr) || !m_overrider->OnSleep(sleep_ticks))
584 if (m_stack_active == m_exit_trap)
593 ResumeThread(active_task->m_thread);
597Word Context::GetCallerSP()
const
600 DWORD calling_tid = GetCurrentThreadId();
602 Win32ScopedCriticalSection __cs(
const_cast<STK_X86_WIN32_CRITICAL_SECTION &
>(m_cs));
604 for (std::list<TaskContext *>::const_iterator itr = m_tasks.begin(), end = m_tasks.end(); itr != end; ++itr)
606 if ((*itr)->m_thread_id == calling_tid)
608 caller_sp =
hw::PtrToWord(STK_X86_WIN32_GET_SP((*itr)->m_task->GetStack()));
619TId Context::GetTid()
const
625 result = m_handler->OnGetTid(GetCallerSP());
635void Context::SwitchToNext()
637 m_handler->OnTaskSwitch(GetCallerSP());
640void Context::ForceContextSwitch(
TId id)
642 if (GetCurrentThreadId() == m_timer_tid)
645 if (m_handler->OnForceContextSwitch(
id, m_stack_idle, m_stack_active))
654 m_pending_switch_id = id;
655 __stk_full_memfence();
656 SetEvent(m_switch_event);
660void Context::OnForceContextSwitch()
662 Win32ScopedCriticalSection __cs(m_cs);
666 const TId id = m_pending_switch_id;
668 if (m_handler->OnForceContextSwitch(
id, m_stack_idle, m_stack_active))
674void Context::ReportTaskExit(TaskContext *task)
688 Win32ScopedCriticalSection __cs(m_cs);
690 task->m_exiting =
true;
693 m_handler->OnTaskExit(task->m_stack);
698 ForceContextSwitch(tid);
701DWORD WINAPI Context::TaskContext::OnTaskRun(LPVOID param)
703 TaskContext *
const self =
static_cast<TaskContext *
>(param);
712void Context::Sleep(
Timeout ticks)
714 m_handler->OnTaskSleep(GetCallerSP(), ticks);
717bool Context::SleepUntil(
Ticks timestamp)
719 return m_handler->OnTaskSleepUntil(GetCallerSP(), timestamp);
724 return m_handler->OnTaskWait(GetCallerSP(), sync_obj, mutex, timeout);
729 m_stop_signal =
true;
735 InitStackMemory(stack_memory);
738 TaskContext *
const ctx =
reinterpret_cast<TaskContext *
>(STK_X86_WIN32_GET_SP(stack_mem));
743 ctx->Initialize(user_task, stack);
745 m_tasks.push_back(ctx);
746 m_task_threads.push_back(ctx->m_thread);
768 GetContext().Initialize(event_handler, service, exit_trap, resolution_us);
801 GetContext().InitStack(stack_type, stack, stack_memory, user_task);
811 return HiResClockQPC::GetInstance()->GetCycles();
816 return HiResClockQPC::GetInstance()->GetFrequency();
841 return GetContext().Wait(sync_obj, mutex, timeout);
876Word stk::hw::GetTls()
881void stk::hw::SetTls(
Word tp)
907 HW_SpinLockLock(m_lock);
912 HW_SpinLockUnlock(m_lock);
917 return HW_SpinLockTryLock(m_lock);
932 return HiResClockQPC::GetInstance()->GetCycles();
937 return HiResClockQPC::GetInstance()->GetFrequency();
Contains common inventory for platform implementation.
#define GetContext()
Get platform's context.
Hardware Abstraction Layer (HAL) declarations for the stk::hw namespace.
void STK_PANIC_HANDLER_DEFAULT(stk::EKernelPanicId id)
Default panic handler: disable interrupts, record the id, and spin in a tight loop - a defined,...
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
#define __stk_forceinline
Forces compiler to always inline the decorated function, regardless of optimisation level.
#define STK_CS_NESTINGS_MAX
Maximum allowable recursion depth for critical section entry (default: 16).
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
#define __stk_attr_noinline
Prevents compiler from inlining the decorated function (function prefix).
#define __stk_attr_noreturn
Declares that function never returns to its caller (function prefix).
#define STK_STATIC_ARRAY_SIZE(ARRAY)
Get size of the static array.
Namespace of STK package.
uintptr_t Word
Native processor word type.
static void Sleep(Timeout tick_count)
Put calling process into a sleep state.
EWaitResult
Wait result (see IKernelService::Wait).
int64_t Ticks
Ticks value.
EKernelPanicId
Identifies the source of a kernel panic.
@ KERNEL_PANIC_HRT_HARD_FAULT
Kernel running in KERNEL_HRT mode reported deadline failure of the task.
@ KERNEL_PANIC_NONE
Panic is absent (no fault).
@ KERNEL_PANIC_SPINLOCK_DEADLOCK
Spin-lock timeout expired: lock owner never released.
int32_t Timeout
Timeout time (ticks).
static void STK_KERNEL_PANIC(stk::EKernelPanicId id)
Called when the kernel detects an unrecoverable internal fault.
static constexpr TId TID_NONE
Reserved task/thread id representing zero/none thread id.
@ STACK_SLEEP_TRAP
Stack of the Sleep trap.
@ STACK_USER_TASK
Stack of the user task.
@ STACK_EXIT_TRAP
Stack of the Exit trap.
static constexpr TId GetTidFromUserTask(const ITask *task) noexcept
Get task identifier from ITask instance.
uint64_t Cycles
Cycles value.
Word TId
Task (thread) id.
bool IsPrivilegedContext()
Check if caller context is Privileged.
static constexpr T * WordToPtr(Word value) noexcept
Cast a CPU register-width integer back to a pointer.
static constexpr Word PtrToWord(T *const ptr) noexcept
Cast a pointer to a CPU register-width integer.
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
Base platform context for all platform implementations.
EWaitResult Wait(ISyncObject *sync_obj, IMutex *mutex, Timeout timeout) override
Put calling process into a waiting state until synchronization object is signaled or timeout occurs.
void Sleep(Timeout ticks) override
Put calling process into a sleep state.
void Resume(Timeout elapsed_ticks) override
Resume scheduling after a prior Suspend() call.
void Stop() override
Stop scheduling.
void Initialize(IEventHandler *event_handler, IKernelService *service, uint32_t resolution_us, Stack *exit_trap) override
Initialize scheduler's context.
Word GetCallerSP() const override
Get caller's Stack Pointer (SP).
void Start() override
Start scheduling.
uint32_t GetSysTimerFrequency() const override
Get system timer frequency.
uint32_t GetTickResolution() const override
Get resolution of the system tick timer in microseconds. Resolution means a number of microseconds be...
void SetCpuFrequency(uint8_t core_id, uint32_t frequency) override
Notify the scheduler of a CPU core's operating frequency.
void InitStack(EStackType stack_type, Stack *stack, IStackMemory *stack_memory, ITask *user_task) override
Initialize stack memory of the user task.
void ProcessHardFault() override
Cause a hard fault of the system.
void ProcessTick() override
Process one tick.
Cycles GetSysTimerCount() const override
Get system timer count value.
void SetEventOverrider(IEventOverrider *overrider, bool non_secure) override
Set platform event overrider.
bool SleepUntil(Ticks timestamp) override
Put calling process into a sleep state until the specified timestamp.
void ForceContextSwitch(TId id) override
Force context switch.
TId GetTid() const override
Get thread Id.
void SwitchToNext() override
Switch to a next task.
Timeout Suspend() override
Suspend scheduling.
static constexpr Session DEFAULT_SESSION
Default session value passed to Enter()/Exit() when the caller does not need to force a specific hand...
static Session Enter(const Session ses=DEFAULT_SESSION)
Enter a critical section.
uint8_t Session
Opaque session token returned by Enter() and consumed by Exit().
static void Exit(const Session ses=DEFAULT_SESSION)
Exit a critical section.
bool TryLock()
Attempt to acquire SpinLock in a single non-blocking attempt.
void Lock()
Acquire SpinLock, blocking until it is available.
void Unlock()
Release SpinLock, allowing another thread or core to acquire it.
static uint32_t GetFrequency()
Get clock frequency.
static Cycles GetCycles()
Get number of clock cycles elapsed.
Word SP
Offset 0: Stack Pointer (SP) register (note: must always be at offset 0).
Interface for a stack memory region.
virtual const Word * GetStack() const =0
Get pointer to the stack memory.
Synchronization object interface.
Interface for mutex synchronization primitive.
Interface for a user task.
Interface for the kernel services exposed to the user processes during run-time when Kernel started s...
static IKernelService * GetInstance()
Get CPU-local instance of the kernel service.
RISC-V specific event handler.