diff --git a/sdk/xtdk/ketypes.h b/sdk/xtdk/ketypes.h index a94ee37..a6343df 100644 --- a/sdk/xtdk/ketypes.h +++ b/sdk/xtdk/ketypes.h @@ -67,6 +67,9 @@ #define READY_SKIP_QUANTUM 2 #define THREAD_QUANTUM 6 +/* Dispatcher object type mask */ +#define DISPATCHER_OBJECT_TYPE_MASK 0x7L + /* Thread priority levels */ #define THREAD_LOW_PRIORITY 0 #define THREAD_LOW_REALTIME_PRIORITY 16 diff --git a/xtoskrnl/CMakeLists.txt b/xtoskrnl/CMakeLists.txt index a028591..e167f2d 100644 --- a/xtoskrnl/CMakeLists.txt +++ b/xtoskrnl/CMakeLists.txt @@ -49,6 +49,7 @@ list(APPEND XTOSKRNL_SOURCE ${XTOSKRNL_SOURCE_DIR}/ke/crash.cc ${XTOSKRNL_SOURCE_DIR}/ke/data.cc ${XTOSKRNL_SOURCE_DIR}/ke/dispatch.cc + ${XTOSKRNL_SOURCE_DIR}/ke/dispobj.cc ${XTOSKRNL_SOURCE_DIR}/ke/dpc.cc ${XTOSKRNL_SOURCE_DIR}/ke/event.cc ${XTOSKRNL_SOURCE_DIR}/ke/exports.cc diff --git a/xtoskrnl/includes/ke.hh b/xtoskrnl/includes/ke.hh index 0051258..4032b0c 100644 --- a/xtoskrnl/includes/ke.hh +++ b/xtoskrnl/includes/ke.hh @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/xtoskrnl/includes/ke/dispatch.hh b/xtoskrnl/includes/ke/dispatch.hh index 79523bb..6da5a9d 100644 --- a/xtoskrnl/includes/ke/dispatch.hh +++ b/xtoskrnl/includes/ke/dispatch.hh @@ -23,10 +23,20 @@ namespace KE STATIC XTCDECL VOID HandleDispatchInterrupt(IN PKTRAP_FRAME TrapFrame); STATIC XTFASTCALL BOOLEAN SwitchContext(IN PKTHREAD CurrentThread, IN KRUNLEVEL RunLevel); + STATIC XTAPI XTSTATUS WaitForSingleObject(IN PVOID Object, + IN KWAIT_REASON WaitReason, + IN KPROCESSOR_MODE WaitMode, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout); STATIC XTAPI VOID UpdateRunTime(IN PKTRAP_FRAME TrapFrame, IN KRUNLEVEL RunLevel); private: + STATIC XTFASTCALL PLARGE_INTEGER ComputeWaitInterval(IN PLARGE_INTEGER OriginalDueTime, + IN PLARGE_INTEGER PreviousDueTime, + IN OUT PLARGE_INTEGER NewDueTime); + STATIC XTFASTCALL XTSTATUS SatisfyWaitingObject(IN PDISPATCHER_HEADER Object, + IN PKTHREAD Thread); STATIC XTFASTCALL BOOLEAN SwitchThreadContext(IN PKTHREAD CurrentThread, IN BOOLEAN ApcBypass); STATIC XTFASTCALL BOOLEAN SwitchThreadStack(IN PKTHREAD CurrentThread, diff --git a/xtoskrnl/includes/ke/dispobj.hh b/xtoskrnl/includes/ke/dispobj.hh new file mode 100644 index 0000000..569fdf1 --- /dev/null +++ b/xtoskrnl/includes/ke/dispobj.hh @@ -0,0 +1,29 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ke/dispobj.hh + * DESCRIPTION: Kernel Thread Dispatcher Objects + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_KE_DISPOBJ_HH +#define __XTOSKRNL_KE_DISPOBJ_HH + +#include + + +/* Kernel Library */ +namespace KE +{ + class DispatcherObject + { + public: + STATIC XTFASTCALL VOID SatisfyWaitingMutexObject(IN PKMUTEX MutexObject, + IN PKTHREAD Thread); + STATIC XTFASTCALL VOID SatisfyWaitingNonMutexObject(IN PDISPATCHER_HEADER MutexObject); + STATIC XTFASTCALL VOID SatisfyWaitingObject(IN PDISPATCHER_HEADER MutexObject, + IN PKTHREAD Thread); + }; +} + +#endif /* __XTOSKRNL_KE_DISPOBJ_HH */ diff --git a/xtoskrnl/ke/dispatch.cc b/xtoskrnl/ke/dispatch.cc index b051b62..0e6161a 100644 --- a/xtoskrnl/ke/dispatch.cc +++ b/xtoskrnl/ke/dispatch.cc @@ -10,6 +10,45 @@ #include +/** + * Calculates the remaining wait interval for a thread after a wait operation has been interrupted. + * + * @param OriginalDueTime + * Supplies the original timeout value requested by the caller. + * + * @param PreviousDueTime + * Supplies the base timestamp, usually the time when the wait was first initiated. + * + * @param NewDueTime + * Supplies a pointer to a LARGE_INTEGER buffer where the recalculated time will be stored. + * + * @return This routine returns a pointer to the resulting due time. + * + * @since XT 1.0 + */ +XTFASTCALL +PLARGE_INTEGER +KE::Dispatcher::ComputeWaitInterval(IN PLARGE_INTEGER OriginalDueTime, + IN PLARGE_INTEGER PreviousDueTime, + IN OUT PLARGE_INTEGER NewDueTime) +{ + /* Check if the timeout is absolute */ + if(OriginalDueTime->QuadPart >= 0) + { + /* No recalculation is needed, return the original value */ + return OriginalDueTime; + } + + /* Fetch the current system interrupt time for the recalculation base */ + KE::SystemTime::GetInterruptTime(NewDueTime); + + /* Calculate the delta */ + NewDueTime->QuadPart -= PreviousDueTime->QuadPart; + + /* Return the new due time */ + return NewDueTime; +} + /** * Enters the system idle thread loop for the current processor, running continuously when no other * threads are scheduled for execution. @@ -149,7 +188,7 @@ KE::Dispatcher::HandleDispatchInterrupt(IN PKTRAP_FRAME TrapFrame) /* Start a guarded code block */ { /* Lock the processor control block */ - KE::SpinLockGuard ControlBlockGuard(&Prcb->PrcbLock); + KE::SpinLockGuard PrcbGuard(&Prcb->PrcbLock); /* Capture the outgoing (preempted) and incoming threads */ CurrentThread = Prcb->CurrentThread; @@ -175,6 +214,60 @@ KE::Dispatcher::HandleDispatchInterrupt(IN PKTRAP_FRAME TrapFrame) AR::CpuFunctions::ClearInterruptFlag(); } + +/** + * Evaluates the state of a dispatcher object to determine if a wait can be satisfied immediately without + * suspending the current thread. + * + * @param Object + * Supplies a pointer to the dispatcher object header. + * + * @param Thread + * Supplies a pointer to the current thread attempting to acquire the object. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTFASTCALL +XTSTATUS +KE::Dispatcher::SatisfyWaitingObject(IN PDISPATCHER_HEADER Object, + IN PKTHREAD Thread) +{ + PKMUTEX Mutex; + + /* Check if the object is a Mutex */ + if(Object->Type == MutexObject) + { + /* Get a pointer to the Mutex object */ + Mutex = (PKMUTEX)Object; + + /* Check if the mutex is free or if the current thread already owns it */ + if((Mutex->Header.SignalState > 0) || (Thread == Mutex->OwnerThread)) + { + /* Verify that recursive acquisition has not hit the mathematical lower bound */ + if(Mutex->Header.SignalState != MINLONG) + { + /* Satisfy the wait and inherit priority if applicable */ + KE::DispatcherObject::SatisfyWaitingMutexObject(Mutex, Thread); + return (XTSTATUS)Thread->WaitStatus; + } + + /* The mutex counter has overflowed */ + return STATUS_MUTEX_LIMIT_EXCEEDED; + } + } + else if(Object->SignalState > 0) + { + /* Apply generic satisfaction rules */ + KE::DispatcherObject::SatisfyWaitingNonMutexObject(Object); + return STATUS_WAIT_0; + } + + /* Object is not signaled, the thread must be queued */ + return STATUS_PENDING; +} + /** * Updates the runtime quantum of the currently executing thread and handles preemption. * @@ -291,3 +384,83 @@ KE::Dispatcher::UpdateRunTime(IN PKTRAP_FRAME TrapFrame, HL::Irq::SendSoftwareInterrupt(DISPATCH_LEVEL); } } + +/** + * Places the current thread into a wait state until the specified dispatcher object is set to a signaled state, + * or until the optional timeout expires. + * + * @param Object + * Supplies a pointer to the dispatcher object to wait on. + * + * @param WaitReason + * Supplies the reason for the wait, utilized for diagnostic and profiling purposes. + * + * @param WaitMode + * Supplies the processor mode in which the wait is occurring (KernelMode or UserMode). + * + * @param Alertable + * Specifies whether the wait is alertable by asynchronous procedure calls (APCs). + * + * @param Timeout + * Supplies an optional pointer to an absolute or relative timeout value. + * + * @return This routine returns the completion status of the wait operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +KE::Dispatcher::WaitForSingleObject(IN PVOID Object, + IN KWAIT_REASON WaitReason, + IN KPROCESSOR_MODE WaitMode, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout) +{ + PDISPATCHER_HEADER Header; + LARGE_INTEGER CurrentTime; + PKTHREAD CurrentThread; + + /* Not implemented, active polling only */ + UNIMPLEMENTED; + + /* Get the dispatcher header */ + Header = (PDISPATCHER_HEADER)Object; + + /* Get the current thread */ + CurrentThread = KE::Processor::GetCurrentThread(); + + /* Check if the object is already signaled or if it is an already owned Mutex */ + if((Header->SignalState > 0) || ((Header->Type == MutexObject) && (CurrentThread == ((PKMUTEX)Object)->OwnerThread))) + { + /* Satisfy the object and return status code */ + SatisfyWaitingObject(Header, CurrentThread); + return STATUS_WAIT_0; + } + + /* Enter cctive polling loop */ + while(Header->SignalState <= 0) + { + /* Check if the timeout has expired */ + if(Timeout != NULLPTR) + { + /* Get the current interrupt time */ + KE::SystemTime::GetInterruptTime(&CurrentTime); + + /* Check if current time exceeds the timeout value */ + if(CurrentTime.QuadPart >= Timeout->QuadPart) + { + /* Wait expired, return status code */ + return STATUS_TIMEOUT; + } + } + + /* Yield the processor */ + AR::CpuFunctions::YieldProcessor(); + } + + /* Apply acquisition rules */ + SatisfyWaitingObject(Header, CurrentThread); + + /* Return status code */ + return STATUS_WAIT_0; +} diff --git a/xtoskrnl/ke/dispobj.cc b/xtoskrnl/ke/dispobj.cc new file mode 100644 index 0000000..faf504d --- /dev/null +++ b/xtoskrnl/ke/dispobj.cc @@ -0,0 +1,113 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ke/dispobj.cc + * DESCRIPTION: Kernel Thread Dispatcher Objects + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Satisfies a wait operation specifically for a Mutex object. + * + * @param Mutex + * Supplies a pointer to the Mutex object being acquired. + * + * @param Thread + * Supplies a pointer to the thread that is acquiring the Mutex. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +KE::DispatcherObject::SatisfyWaitingMutexObject(IN PKMUTEX Mutex, + IN PKTHREAD Thread) +{ + /* Decrement the signal state */ + Mutex->Header.SignalState--; + + /* Check if the mutex is now fully acquired */ + if(!Mutex->Header.SignalState) + { + /* Bind the mutex ownership to the current acquiring thread */ + Mutex->OwnerThread = Thread; + + /* Inherit the APC disable from the mutex */ + Thread->KernelApcDisable = Thread->KernelApcDisable - Mutex->ApcDisable; + + /* Check if the previous owner terminated without releasing the mutex */ + if(Mutex->Abandoned) + { + /* Clear the abandonment flag */ + Mutex->Abandoned = FALSE; + + /* Alert the thread */ + Thread->WaitStatus = STATUS_ABANDONED; + } + + /* Track this mutex in the thread's local list */ + RTL::LinkedList::InsertHeadList(Thread->MutexListHead.Blink, &Mutex->MutexListEntry); + } +} + +/** + * Satisfies a wait operation for non-mutex dispatcher objects, such as Synchronization Events and Semaphores. + * + * @param Object + * Supplies a pointer to the dispatcher object. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +KE::DispatcherObject::SatisfyWaitingNonMutexObject(IN PDISPATCHER_HEADER Object) +{ + /* Check if the object is a Synchronization Event */ + if((Object->Type & DISPATCHER_OBJECT_TYPE_MASK) == EventSynchronizationObject) + { + /* Reset the event */ + Object->SignalState = 0; + } + else if(Object->Type == SemaphoreObject) + { + /* Decrement the signal state */ + Object->SignalState--; + } +} + +/** + * Evaluates the object type and routes the wait satisfaction logic to the appropriate specialized handler. + * + * @param Object + * Supplies a pointer to the dispatcher object being acquired. + * + * @param Thread + * Supplies a pointer to the thread acquiring the object. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +KE::DispatcherObject::SatisfyWaitingObject(IN PDISPATCHER_HEADER Object, + IN PKTHREAD Thread) +{ + /* Check if the object is a Mutex */ + if(Object->Type == MutexObject) + { + /* Apply mutex-specific acquisition rules */ + SatisfyWaitingMutexObject((PKMUTEX)Object, Thread); + } + else + { + /* Apply generic acquisition rules for events, semaphores, and timers */ + SatisfyWaitingNonMutexObject(Object); + } +}