Implement kernel push lock infrastructure
All checks were successful
Builds / ExectOS (i686, release) (push) Successful in 33s
Builds / ExectOS (amd64, release) (push) Successful in 34s
Builds / ExectOS (i686, debug) (push) Successful in 43s
Builds / ExectOS (amd64, debug) (push) Successful in 46s

This commit is contained in:
2026-06-29 18:25:40 +02:00
parent 9ef8c569d5
commit 84d35e3c97
5 changed files with 1041 additions and 1 deletions

View File

@@ -47,8 +47,20 @@
/* APC pending state length */ /* APC pending state length */
#define KAPC_STATE_LENGTH (FIELD_OFFSET(KAPC_STATE, UserApcPending) + sizeof(BOOLEAN)) #define KAPC_STATE_LENGTH (FIELD_OFFSET(KAPC_STATE, UserApcPending) + sizeof(BOOLEAN))
/* Push lock total bits */ /* Indices used to access the PushLock data structure */
#define KPUSHLOCK_INDEX ((ULONG_PTR)0x00)
#define KPUSHLOCK_LOCK ((ULONG_PTR)0x01)
#define KPUSHLOCK_WAITING ((ULONG_PTR)0x02)
#define KPUSHLOCK_WAKING ((ULONG_PTR)0x04)
#define KPUSHLOCK_MULTIPLE_SHARED ((ULONG_PTR)0x08)
#define KPUSHLOCK_INCREMENT_SHARED ((ULONG_PTR)0x10)
/* Mask for the pointer bits */
#define KPUSHLOCK_PTR_BITS ((ULONG_PTR)0x0F)
/* PushLock related definitions */
#define KPUSH_LOCK_TOTAL_BITS (sizeof(ULONG_PTR) * 8) #define KPUSH_LOCK_TOTAL_BITS (sizeof(ULONG_PTR) * 8)
#define KPUSH_LOCK_SPIN_COUNT 1024
/* Kernel service descriptor tables count */ /* Kernel service descriptor tables count */
#define KSERVICE_TABLES_COUNT 4 #define KSERVICE_TABLES_COUNT 4

View File

@@ -58,6 +58,7 @@ list(APPEND XTOSKRNL_SOURCE
${XTOSKRNL_SOURCE_DIR}/ke/krnlinit.cc ${XTOSKRNL_SOURCE_DIR}/ke/krnlinit.cc
${XTOSKRNL_SOURCE_DIR}/ke/kthread.cc ${XTOSKRNL_SOURCE_DIR}/ke/kthread.cc
${XTOSKRNL_SOURCE_DIR}/ke/kubsan.cc ${XTOSKRNL_SOURCE_DIR}/ke/kubsan.cc
${XTOSKRNL_SOURCE_DIR}/ke/pushlock.cc
${XTOSKRNL_SOURCE_DIR}/ke/runlevel.cc ${XTOSKRNL_SOURCE_DIR}/ke/runlevel.cc
${XTOSKRNL_SOURCE_DIR}/ke/schedule.cc ${XTOSKRNL_SOURCE_DIR}/ke/schedule.cc
${XTOSKRNL_SOURCE_DIR}/ke/semphore.cc ${XTOSKRNL_SOURCE_DIR}/ke/semphore.cc

View File

@@ -26,6 +26,7 @@
#include <ke/kthread.hh> #include <ke/kthread.hh>
#include <ke/kubsan.hh> #include <ke/kubsan.hh>
#include <ke/proc.hh> #include <ke/proc.hh>
#include <ke/pushlock.hh>
#include <ke/runlevel.hh> #include <ke/runlevel.hh>
#include <ke/schedule.hh> #include <ke/schedule.hh>
#include <ke/semphore.hh> #include <ke/semphore.hh>

View File

@@ -0,0 +1,42 @@
/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/includes/ke/pushlock.hh
* DESCRIPTION: XT Kernel Push Lock support
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#ifndef __XTOSKRNL_KE_PUSHLOCK_HH
#define __XTOSKRNL_KE_PUSHLOCK_HH
#include <xtos.hh>
/* Kernel Library */
namespace KE
{
class PushLock
{
public:
STATIC XTFASTCALL VOID AcquireExclusivePushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID AcquireSharedPushLock(PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID AcquireWaitExclusivePushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID AcquireWaitSharedPushLock(IN OUT PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID ReleaseExclusivePushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID ReleasePushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID ReleaseSharedPushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID ReleaseWaitExclusivePushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID ReleaseWaitSharedPushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID ReleaseWaitPushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL BOOLEAN TryAcquireExclusivePushLock(PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID TryWakePushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID WaitOnPushLock(IN PKPUSH_LOCK PushLock);
STATIC XTFASTCALL VOID WakePushLockWaiters(IN PKPUSH_LOCK PushLock, IN KPUSH_LOCK OldState);
private:
STATIC XTINLINE VOID OptimizePushLockList(IN PKPUSH_LOCK PushLock, IN KPUSH_LOCK OldValue);
STATIC XTINLINE XTFASTCALL VOID SpinPushLock(IN PKPUSH_LOCK_WAIT_BLOCK WaitBlock);
};
}
#endif /* __XTOSKRNL_KE_PUSHLOCK_HH */

984
xtoskrnl/ke/pushlock.cc Normal file
View File

@@ -0,0 +1,984 @@
/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/ke/pushlock.cc
* DESCRIPTION: XT Kernel Push Lock support
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/**
* Attempts to acquire a push lock for exclusive access via the fast-path.
*
* @param PushLock
* Supplies a pointer to the push lock to be acquired.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::AcquireExclusivePushLock(IN PKPUSH_LOCK PushLock)
{
/* Attempt to swap a NULL pointer with the EXCLUSIVE flag */
if(RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, NULLPTR, (PVOID)KPUSHLOCK_LOCK))
{
/* Lock contention detected, enter the slow-path */
AcquireWaitExclusivePushLock(PushLock);
}
}
/**
* Attempts to acquire a push lock for shared access via the fast-path.
*
* @param PushLock
* Supplies a pointer to the push lock to be acquired.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::AcquireSharedPushLock(PKPUSH_LOCK PushLock)
{
/* Attempt to set the reader flag */
if(RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, NULLPTR, (PVOID)(KPUSHLOCK_LOCK | KPUSHLOCK_INCREMENT_SHARED)))
{
/* Lock contention or existing readers detected, Enter the slow-path */
AcquireWaitSharedPushLock(PushLock);
}
}
/**
* Enqueues the current thread's wait block into the lock's wait list, conditionally assumes responsibility
* for list optimization and transitions the thread into a wait state until exclusive ownership is acquired.
*
* @param PushLock
* Supplies a pointer to the push lock to be acquired.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::AcquireWaitExclusivePushLock(IN PKPUSH_LOCK PushLock)
{
KPUSH_LOCK OldState, NewState, TempState;
KPUSH_LOCK_WAIT_BLOCK WaitBlock;
PVOID ActualState;
BOOLEAN Optimize;
/* Initialize a synchronization event */
KE::Event::InitializeEvent(&WaitBlock.WakeEvent, SynchronizationEvent, FALSE);
/* Capture the snapshot of the push lock pointer */
OldState.Ptr = PushLock->Ptr;
/* Enter contention loop */
while(TRUE)
{
/* Check if the lock is free */
if(!(OldState.Value & KPUSHLOCK_LOCK))
{
/* Assert the LOCK bit */
NewState.Value = OldState.Value | KPUSHLOCK_LOCK;
/* Commit the acquisition */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Lock acquired, return */
return;
}
/* The lock state changed, retry */
OldState.Ptr = ActualState;
continue;
}
else
{
/* Prepare the wait block for enqueuing */
WaitBlock.Flags = KPUSHLOCK_LOCK | KPUSHLOCK_WAITING;
WaitBlock.Previous = NULLPTR;
/* Assume no list optimization is required initially */
Optimize = FALSE;
/* Check if other threads are already queued */
if(OldState.Value & KPUSHLOCK_WAITING)
{
/* Hook into the head of the existing LIFO list */
WaitBlock.Last = NULLPTR;
WaitBlock.Next = (PKPUSH_LOCK_WAIT_BLOCK)(OldState.Value & ~KPUSHLOCK_PTR_BITS);
WaitBlock.ShareCount = 0;
/* Construct the new pointer containing wait block and preserved flags */
NewState.Value = (OldState.Value & KPUSHLOCK_MULTIPLE_SHARED) |
KPUSHLOCK_LOCK |
KPUSHLOCK_WAKING |
KPUSHLOCK_WAITING |
(ULONG_PTR)&WaitBlock;
/* Check if no one is currently organizing the list */
if(!(OldState.Value & KPUSHLOCK_WAKING))
{
/* Take responsibility for optimizing the wait list */
Optimize = TRUE;
}
}
else
{
/* Mark this block as the tail of the new wait list and preserve the shared reader count */
WaitBlock.Last = &WaitBlock;
WaitBlock.ShareCount = (ULONG)OldState.Shared;
/* Check if multiple readers currently hold the lock */
if(OldState.Shared > 1)
{
/* Inject the wait block and preserve the multiple shared state */
NewState.Value = KPUSHLOCK_MULTIPLE_SHARED | KPUSHLOCK_LOCK | KPUSHLOCK_WAITING | (ULONG_PTR)&WaitBlock;
}
else
{
/* Clear the share count for a single owner */
WaitBlock.ShareCount = 0;
/* Inject the wait block for a single owner scenario */
NewState.Value = KPUSHLOCK_LOCK | KPUSHLOCK_WAITING | (ULONG_PTR)&WaitBlock;
}
}
/* insert wait block into the PushLock */
TempState.Ptr = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(TempState.Ptr != OldState.Ptr)
{
/* Lost the queue race, retry */
OldState.Ptr = TempState.Ptr;
continue;
}
/* Check if this thread took the optimization role */
if(Optimize)
{
/* Convert the LIFO wait list into a FIFO list */
OptimizePushLockList(PushLock, NewState);
}
/* Block the thread */
SpinPushLock(&WaitBlock);
/* Reset state and retry */
WaitBlock.ShareCount = 0;
OldState.Ptr = PushLock->Ptr;
}
}
}
/**
* Enqueues the current thread into the wait list if the lock is held exclusively or if a writer is already waiting.
*
* @param PushLock
* Supplies a pointer to the push lock to be acquired.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::AcquireWaitSharedPushLock(IN PKPUSH_LOCK PushLock)
{
KPUSH_LOCK OldState, NewState, TempState;
KPUSH_LOCK_WAIT_BLOCK WaitBlock;
PVOID ActualState;
BOOLEAN Optimize;
/* Initialize a synchronization event */
KE::Event::InitializeEvent(&WaitBlock.WakeEvent, SynchronizationEvent, FALSE);
/* Capture the snapshot of the push lock pointer */
OldState.Ptr = PushLock->Ptr;
/* Enter contention loop */
while(TRUE)
{
/* Check if the lock is free */
if(!(OldState.Value & (KPUSHLOCK_LOCK | KPUSHLOCK_WAITING)))
{
/* Increment the shared reader count */
NewState.Value = OldState.Value + KPUSHLOCK_INCREMENT_SHARED;
/* Commit the shared acquisition */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Lock acquired, return */
return;
}
/* The lock state changed, retry */
OldState.Ptr = ActualState;
continue;
}
else
{
/* Prepare the wait block for enqueuing as a reader */
WaitBlock.Flags = KPUSHLOCK_WAITING;
WaitBlock.Previous = NULLPTR;
/* Assume no list optimization is required */
Optimize = FALSE;
/* Check if other threads are already queued */
if(OldState.Value & KPUSHLOCK_WAITING)
{
/* Hook into the head of the existing LIFO wait list */
WaitBlock.Last = NULLPTR;
WaitBlock.Next = (PKPUSH_LOCK_WAIT_BLOCK)(OldState.Value & ~KPUSHLOCK_PTR_BITS);
WaitBlock.ShareCount = 0;
/* Construct the new pointer containing wait block and preserved flags */
NewState.Value = (OldState.Value & KPUSHLOCK_MULTIPLE_SHARED) |
KPUSHLOCK_LOCK |
KPUSHLOCK_WAITING |
(ULONG_PTR)&WaitBlock;
/* Check if no one is currently organizing the list */
if(!(OldState.Value & KPUSHLOCK_WAKING))
{
/* Take responsibility for optimizing the wait list */
Optimize = TRUE;
}
}
else
{
/* Mark this block as the tail of the new wait list and preserve the shared reader count */
WaitBlock.Last = &WaitBlock;
WaitBlock.ShareCount = (ULONG)OldState.Shared;
/* Check if multiple readers currently hold the lock */
if(OldState.Shared > 1)
{
/* Inject the wait block and preserve the multiple shared state */
NewState.Value = KPUSHLOCK_MULTIPLE_SHARED | KPUSHLOCK_LOCK | KPUSHLOCK_WAITING | (ULONG_PTR)&WaitBlock;
}
else
{
/* Inject the wait block for a single owner */
NewState.Value = KPUSHLOCK_LOCK | KPUSHLOCK_WAITING | (ULONG_PTR)&WaitBlock;
}
}
/* Insert the wait block into the PushLock */
TempState.Ptr = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(TempState.Ptr != OldState.Ptr)
{
/* Lost the queue race, retry */
OldState.Ptr = TempState.Ptr;
continue;
}
/* Check if this thread took the optimization role */
if(Optimize)
{
/* Convert the LIFO wait list into a FIFO list */
OptimizePushLockList(PushLock, NewState);
}
/* Block the thread */
SpinPushLock(&WaitBlock);
/* Exit loop */
return;
}
}
}
/**
* Optimizes the push lock wait list by converting it from a singly-linked LIFO list into a doubly-linked FIFO list.
*
* @param PushLock
* Supplies a pointer to the push lock whose list is being optimized.
*
* @param OldValue
* Supplies the snapshot of the push lock state containing the pointer to the list head.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTINLINE
VOID
KE::PushLock::OptimizePushLockList(IN PKPUSH_LOCK PushLock,
IN KPUSH_LOCK OldValue)
{
PKPUSH_LOCK_WAIT_BLOCK CurrentBlock, NextBlock;
/* Extract the pointer to the head of the wait block list */
CurrentBlock = (PKPUSH_LOCK_WAIT_BLOCK)(OldValue.Value & ~KPUSHLOCK_PTR_BITS);
/* Ensure the list is not empty */
if(CurrentBlock)
{
/* Traverse the list */
while((NextBlock = CurrentBlock->Next))
{
/* Link the next block back to the current block */
NextBlock->Previous = CurrentBlock;
CurrentBlock = NextBlock;
}
}
}
/**
* Releases a push lock that was acquired for exclusive access.
*
* @param PushLock
* Supplies a pointer to the push lock to be released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::ReleaseExclusivePushLock(IN PKPUSH_LOCK PushLock)
{
KPUSH_LOCK OldState;
/* Subtract the LOCK bit */
OldState.Value = RTL::Atomic::ExchangeAdd64((PLONG_PTR)&PushLock->Value, -(SSIZE_T)KPUSHLOCK_LOCK);
/* Check if waiters are queued and no one is currently waking them */
if((OldState.Value & KPUSHLOCK_WAITING) && !(OldState.Value & KPUSHLOCK_WAKING))
{
/* Initiate the wake sequence */
TryWakePushLock(PushLock);
}
}
/**
* Attempts to release the lock immediately via the fast-path.
*
* @param PushLock
* Supplies a pointer to the push lock to be released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::ReleasePushLock(IN PKPUSH_LOCK PushLock)
{
KPUSH_LOCK NewState, OldState;
/* Capture the snapshot of the push lock pointer */
OldState.Ptr = PushLock->Ptr;
/* Check if the push lock is held by multiple readers */
if(OldState.Value & KPUSHLOCK_MULTIPLE_SHARED)
{
/* Decrement the shared reader count */
NewState.Value = OldState.Value - KPUSHLOCK_INCREMENT_SHARED;
}
else
{
/* Clear the push lock state */
NewState.Value = 0;
}
/* Check if there are waiting threads or if the swap fails due to state changes */
if((OldState.Value & KPUSHLOCK_WAITING) ||
(RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr) != OldState.Ptr))
{
/* Contention or waiters present, execute the slow-path release */
ReleaseWaitPushLock(PushLock);
}
}
/**
* Releases a push lock that was acquired for shared access.
*
* @param PushLock
* Supplies a pointer to the push lock to be released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTINLINE
XTFASTCALL
VOID
KE::PushLock::ReleaseSharedPushLock(IN PKPUSH_LOCK PushLock)
{
/* Attempt to clear the lock */
if(RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, (PVOID)(KPUSHLOCK_LOCK | KPUSHLOCK_INCREMENT_SHARED), NULLPTR)
!= (PVOID)(KPUSHLOCK_LOCK | KPUSHLOCK_INCREMENT_SHARED))
{
/* Contention or multiple readers detected, call slow-path */
ReleaseWaitSharedPushLock(PushLock);
}
}
/**
* Drops the lock bit and transitions into the waking phase.
*
* @param PushLock
* Supplies a pointer to the push lock to be released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::ReleaseWaitExclusivePushLock(IN PKPUSH_LOCK PushLock)
{
KPUSH_LOCK NewState, OldState;
PVOID ActualState;
/* Capture the snapshot of the push lock pointer */
OldState.Ptr = PushLock->Ptr;
/* Enter release loop */
while(TRUE)
{
/* Strip the lock bit from the current state */
NewState.Value = OldState.Value & ~KPUSHLOCK_LOCK;
/* Check if waiters are queued and no thread is waking them */
if((OldState.Value & KPUSHLOCK_WAITING) && !(OldState.Value & KPUSHLOCK_WAKING))
{
/* Add the waking flag to the state */
NewState.Value |= KPUSHLOCK_WAKING;
}
/* Commit the modified state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Verify if acquired the waking role */
if((OldState.Value & KPUSHLOCK_WAITING) && !(OldState.Value & KPUSHLOCK_WAKING))
{
/* Execute the wake sequence */
WakePushLockWaiters(PushLock, NewState);
}
return;
}
/* State changed, update snapshot and retry */
OldState.Ptr = ActualState;
}
}
/**
* Handles decrements under contention and delegates to the wake mechanism if threads are queued
* and the lock becomes fully available.
*
* @param PushLock
* Supplies a pointer to the push lock to be released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::ReleaseWaitPushLock(IN PKPUSH_LOCK PushLock)
{
PKPUSH_LOCK_WAIT_BLOCK WaitBlock;
KPUSH_LOCK NewState, OldState;
PVOID ActualState;
/* Capture the snapshot of the push lock pointer */
OldState.Ptr = PushLock->Ptr;
/* Enter release loop */
while(TRUE)
{
/* Check if there are no waiters queued */
if(!(OldState.Value & KPUSHLOCK_WAITING))
{
/* Check if the lock is held by multiple readers */
if(OldState.Value & KPUSHLOCK_MULTIPLE_SHARED)
{
/* Decrement the shared reader count in the new state */
NewState.Value = OldState.Value - KPUSHLOCK_INCREMENT_SHARED;
}
else
{
/* Clear the new state */
NewState.Value = 0;
}
/* Commit the new state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Lock released, return */
return;
}
/* State changed, retry */
OldState.Ptr = ActualState;
continue;
}
/* Check if multiple readers hold the lock while waiters are queued */
if(OldState.Value & KPUSHLOCK_MULTIPLE_SHARED)
{
/* Extract the wait block list head */
WaitBlock = (PKPUSH_LOCK_WAIT_BLOCK)(OldState.Value & ~KPUSHLOCK_PTR_BITS);
/* Traverse to find the last wait block */
while(WaitBlock->Next)
{
/* Check if the last block pointer is already cached */
if(WaitBlock->Last)
{
/* Jump directly to the cached last block */
WaitBlock = WaitBlock->Last;
break;
}
/* Advance to the next wait block */
WaitBlock = WaitBlock->Next;
}
/* Check if the last block is a reader block */
if(WaitBlock->ShareCount > 0)
{
/* Decrement the block's reader share count */
if(RTL::Atomic::Decrement32((PLONG)&WaitBlock->ShareCount) > 0)
{
/* There are still active readers, return */
return;
}
}
}
/* Strip the lock and multiple shared flags from the current state */
NewState.Value = OldState.Value & ~(KPUSHLOCK_LOCK | KPUSHLOCK_MULTIPLE_SHARED);
/* Check if another thread is already handling the wake process */
if(OldState.Value & KPUSHLOCK_WAKING)
{
/* Commit the new state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Lock released, return */
return;
}
}
else
{
/* Add the waking flag to the new state */
NewState.Value |= KPUSHLOCK_WAKING;
/* Commit the new state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Assumed the waking role, execute wake sequence and return */
WakePushLockWaiters(PushLock, NewState);
return;
}
}
/* State changed, update snapshot and retry */
OldState.Ptr = ActualState;
}
}
/**
* Handles decrements under contention and delegates to the wake mechanism if needed.
*
* @param PushLock
* Supplies a pointer to the push lock to be released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::ReleaseWaitSharedPushLock(IN PKPUSH_LOCK PushLock)
{
PKPUSH_LOCK_WAIT_BLOCK WaitBlock;
KPUSH_LOCK NewState, OldState;
PVOID ActualState;
/* Capture the snapshot of the pointer */
OldState.Ptr = PushLock->Ptr;
/* Enter release loop */
while(TRUE)
{
/* Check if there are no waiters currently queued */
if(!(OldState.Value & KPUSHLOCK_WAITING))
{
/* Check if the lock is held by multiple readers */
if(OldState.Value & KPUSHLOCK_MULTIPLE_SHARED)
{
/* Decrement the shared reader count in the new state */
NewState.Value = OldState.Value - KPUSHLOCK_INCREMENT_SHARED;
}
else
{
/* Clear the new state completely */
NewState.Value = 0;
}
/* Commit the new state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr) return;
/* State changed, retry */
OldState.Ptr = ActualState;
continue;
}
/* Check if multiple readers hold the lock while waiters are queued */
if(OldState.Value & KPUSHLOCK_MULTIPLE_SHARED)
{
/* Extract the wait block list head */
WaitBlock = (PKPUSH_LOCK_WAIT_BLOCK)(OldState.Value & ~KPUSHLOCK_PTR_BITS);
/* Traverse to find the last wait block */
while(WaitBlock->Next)
{
/* Check if the last block pointer is already cached */
if(WaitBlock->Last)
{
/* Jump to the cached last block */
WaitBlock = WaitBlock->Last;
break;
}
/* Advance to the next wait block */
WaitBlock = WaitBlock->Next;
}
/* Check if the last block is a reader block */
if(WaitBlock->ShareCount > 0)
{
/* Decrement the block's reader share count */
if(RTL::Atomic::Decrement32((PLONG)&WaitBlock->ShareCount) > 0) return;
}
}
/* Strip the lock and multiple shared flags from the current state */
NewState.Value = OldState.Value & ~(KPUSHLOCK_LOCK | KPUSHLOCK_MULTIPLE_SHARED);
/* Check if another thread is already handling the wake process */
if(OldState.Value & KPUSHLOCK_WAKING)
{
/* Commit the new state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr) return;
}
else
{
/* Add the waking flag to the new state */
NewState.Value |= KPUSHLOCK_WAKING;
/* Commit the new state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Execute wake sequence */
WakePushLockWaiters(PushLock, NewState);
return;
}
}
/* State changed, update snapshot and retry */
OldState.Ptr = ActualState;
}
}
/**
* Handles the blocking phase for a thread waiting on a push lock.
*
* @param WaitBlock
* Supplies a pointer to the thread's local wait block allocated on the stack.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTINLINE
XTFASTCALL
VOID
KE::PushLock::SpinPushLock(IN PKPUSH_LOCK_WAIT_BLOCK WaitBlock)
{
ULONG SpinCount;
/* Initialize the spin count */
SpinCount = KPUSH_LOCK_SPIN_COUNT;
/* Enter adaptive Spin loop */
while(SpinCount--)
{
/* Check if the unlocker cleared our wait flag */
if(!(WaitBlock->Flags & KPUSHLOCK_WAITING))
{
/* Wait flag cleared, return */
return;
}
/* Yield processor */
AR::CpuFunctions::YieldProcessor();
}
/* Test and clear the WAIT bit */
if(RTL::Atomic::BitTestAndReset((PLONG)&WaitBlock->Flags, 1))
{
/* Delegate to the Dispatcher */
KE::Dispatcher::WaitForSingleObject(&WaitBlock->WakeEvent, WrPushLock, KernelMode, FALSE, NULLPTR);
}
}
/**
* Attempts to acquire a push lock for exclusive access without entering a wait state.
*
* @param PushLock
* Supplies a pointer to the push lock to be acquired.
*
* @return This routine returns TRUE if the exclusive lock was successfully acquired, or FALSE otherwise.
*
* @since XT 1.0
*/
XTFASTCALL
BOOLEAN
KE::PushLock::TryAcquireExclusivePushLock(PKPUSH_LOCK PushLock)
{
/* Acquire the lock */
if(RTL::Atomic::BitTestAndSet((PLONG)&PushLock->Value, KPUSHLOCK_INDEX))
{
/* Lock not acquired, return FALSE */
return FALSE;
}
/* Lock acquired, return TRUE */
return TRUE;
}
/**
* Initiates the wake sequence for queued waiters if the lock is free and no other thread is already performing
* the wake operation.
*
* @param PushLock
* Supplies a pointer to the push lock to evaluate.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::TryWakePushLock(IN PKPUSH_LOCK PushLock)
{
KPUSH_LOCK NewState, OldState;
PVOID ActualState;
/* Capture the snapshot of the pointer */
OldState.Ptr = PushLock->Ptr;
/* Enter evaluation loop */
while(TRUE)
{
/* Check if already waking, locked, or no one is waiting */
if((OldState.Value & KPUSHLOCK_WAKING) ||
(OldState.Value & KPUSHLOCK_LOCK) ||
!(OldState.Value & KPUSHLOCK_WAITING))
{
/* Abort */
return;
}
/* Add the waking flag to the state */
NewState.Value = OldState.Value | KPUSHLOCK_WAKING;
/* Commit the waking state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Assumed the waking role, execute wake sequence */
WakePushLockWaiters(PushLock, NewState);
return;
}
/* State changed, update snapshot and retry */
OldState.Ptr = ActualState;
}
}
/**
* Wakes threads waiting on a PushLock.
*
* @param PushLock
* Supplies a pointer to the push lock being processed.
*
* @param OldState
* Supplies the current snapshot of the push lock state.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::WakePushLockWaiters(IN PKPUSH_LOCK PushLock, IN KPUSH_LOCK OldState)
{
PKPUSH_LOCK_WAIT_BLOCK FirstWaitBlock, PreviousWaitBlock, WaitBlock;
KPUSH_LOCK NewState;
PVOID ActualState;
/* Loop while the push lock is held by another thread */
while(OldState.Value & KPUSHLOCK_LOCK)
{
/* Strip the waking flag */
NewState.Value = OldState.Value & ~KPUSHLOCK_WAKING;
/* Commit the modified state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Lock released, return */
return;
}
/* Update the snapshot pointer */
OldState.Ptr = ActualState;
}
/* Extract the first wait block pointer */
FirstWaitBlock = (PKPUSH_LOCK_WAIT_BLOCK)(OldState.Value & ~KPUSHLOCK_PTR_BITS);
WaitBlock = FirstWaitBlock;
/* Loop through the wait block list */
while(WaitBlock->Next)
{
/* Check if the last block pointer is cached */
if(WaitBlock->Last)
{
/* Jump to the cached last block */
WaitBlock = WaitBlock->Last;
break;
}
/* Store the current block as the previous block */
PreviousWaitBlock = WaitBlock;
/* Advance to the next block and set the back-pointer to the previous block */
WaitBlock = WaitBlock->Next;
WaitBlock->Previous = PreviousWaitBlock;
}
/* Point WaitBlock to the oldest waiter */
PreviousWaitBlock = WaitBlock->Previous;
/* Check if the target is a reader block */
if(!(WaitBlock->Flags & KPUSHLOCK_LOCK) || (PreviousWaitBlock == NULLPTR))
{
/* Enter clearing loop */
while(TRUE)
{
/* Clear the new state */
NewState.Value = 0;
/* Commit the cleared state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Cleared the lock state, break the loop */
break;
}
/* Update the snapshot pointer */
OldState.Ptr = ActualState;
}
}
else
{
/* Update the cached last block pointer and unlink the target block */
FirstWaitBlock->Last = PreviousWaitBlock;
WaitBlock->Previous = NULLPTR;
/* Enter clearing loop */
while(TRUE)
{
/* Strip the waking flag from the current state */
NewState.Value = OldState.Value & ~KPUSHLOCK_WAKING;
/* Commit the modified state */
ActualState = RTL::Atomic::CompareExchangePointer(&PushLock->Ptr, OldState.Ptr, NewState.Ptr);
if(ActualState == OldState.Ptr)
{
/* Cleared the waking flag, break the loop */
break;
}
/* Update the snapshot pointer */
OldState.Ptr = ActualState;
}
}
/* Raise runlevel to DISPATCH level if there are multiple blocks */
KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL, WaitBlock->Previous != NULLPTR);
/* Traverse the reversed list */
while(WaitBlock)
{
/* Save previous wait block */
PreviousWaitBlock = WaitBlock->Previous;
/* Clear the WAIT flag */
if(!RTL::Atomic::BitTestAndReset((PLONG)&WaitBlock->Flags, 1))
{
/* Thread is fully asleep, Signal its event and boost priority */
KE::Event::SetEventBoostPriority(&WaitBlock->WakeEvent, NULLPTR);
}
/* Go to the next block */
WaitBlock = PreviousWaitBlock;
}
}
/**
* Synchronizes the current thread with the PushLock.
*
* @param PushLock
* Supplies a pointer to the push lock to wait on.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
KE::PushLock::WaitOnPushLock(IN PKPUSH_LOCK PushLock)
{
KPUSH_LOCK State;
/* Capture the snapshot of the pointer */
State.Ptr = PushLock->Ptr;
/* Verify if the lock is actively held by any thread */
if(State.Value & KPUSHLOCK_LOCK)
{
/* Force serialization by acquiring the lock */
AcquireExclusivePushLock(PushLock);
/* Immediately release the lock */
ReleaseExclusivePushLock(PushLock);
}
}