windows-nt/Source/XPSP1/NT/base/ntdll/ia64/critsect.s

554 lines
14 KiB
ArmAsm
Raw Normal View History

2020-09-26 03:20:57 -05:00
//++
//
// Copyright (c) 1989 Microsoft Corporation
//
// Module Name:
//
// critsect.s
//
// Abstract:
//
// This module implements functions to support user mode critical sections.
//
// Author:
//
// William K. Cheung (wcheung) 18-Sep-95
//
// Revision History:
//
// 07-Jul-97 bl Updated to EAS2.3
//
// 08-Feb-96 Updated to EAS2.1
//
//--
#include "ksia64.h"
.file "critsect.s"
//
// intra-module functions to be called.
//
PublicFunction(RtlpWaitForCriticalSection)
PublicFunction(RtlpUnWaitCriticalSection)
//++
//
// NTSTATUS
// RtlEnterCriticalSection(
// IN PRTL_CRITICAL_SECTION CriticalSection
// )
//
// Routine Description:
//
// This function enters a critical section.
//
// Arguments:
//
// CriticalSection (a0) - supplies a pointer to a critical section.
//
// Return Value:
//
// STATUS_SUCCESS or raises an exception if an error occured.
//
// Algorithm in C:
//
// NTSTATUS
// RtlEnterCriticalSection(
// IN PRTL_CRITICAL_SECTION CriticalSection
// )
// {
// PTEB Teb;
// LONG OriginalValue;
// HANDLE CurrentThreadId;
// DWORD SpinCount;
//
// Teb = NtCurrentTeb();
// CurrentThreadId = Teb->ClientId.UniqueThread;
// SpinCount = CriticalSection->SpinCount;
//
// if (SpinCount == 0) {
// nospin:
// OriginalValue = AtomicIncrement(CriticalSection->LockCount);
//
// if (OriginalValue != -1) {
// if (CriticalSection->OwningThread != CurrentThreadId) {
// RtlpWaitForCriticalSection(CriticalSection);
// CriticalSection->OwningThread = CurrentThreadId;
// CriticalSection->RecursionCount = 0;
// } else {
// CriticalSection->RecursionCount++;
// }
// } else {
// CriticalSection->OwningThread = CurrentThreadId;
// CriticalSection->RecursionCount = 0;
// }
//
// return STATUS_SUCCESS;
//
// } else { /* spin count not 0 */
//
// if (CriticalSection->OwningThread == CurrentThread) {
// AtomicIncrement(CriticalSection->LockCount);
// CriticalSection->RecursionCount++;
// return STATUS_SUCCESS;
// } else {
// do {
// if (!CmpXchg(CriticalSection->LockCount,0,-1)) {
// Lock acquired
// CriticalSection->OwningThread = CurrentThreadId;
// CriticalSection->RecursionCount = 0;
// return STATUS_SUCCESS;
// }
// if (CriticalSection->LockCount >= 1) goto nospin;
//
// while (CriticalSection->LockCount == 0) {
// if (!--SpinCount) goto nospin;
// }
// } while (1) // CriticalSection->LockCount == -1, not owned
// }
// }
// }
//
//--
NESTED_ENTRY(RtlEnterCriticalSection)
//
// register aliases
//
rpThreadId=t3
rOldLockCount=t4
rpLockCount=t5
rRecursionCount=t6
rOwnerId=t7
rpSpinCount=t9
rSpinCount=t10
rT1=t11
rT2=t12
pSpin=pt0
pNoSpin=pt1
pNotOwn=pt2
pNotAcq=pt3
pFree=pt5
pHeld=pt6
pGo=pt7
pWait=pt8
//
// alloc regs and save brp & pfs
//
NESTED_SETUP(1,5,1,0)
add rpLockCount = CsLockCount, a0
;;
//
// more register aliases
//
rThreadId = loc2
rpOwner = loc3
rpCsRecursion = loc4
PROLOGUE_END
//
// Swizzle pointers to address the RecursionCount and
// LockCount fields in the critical section structure.
//
lfetch.nt1 [rpLockCount]
nop.f 0
add rpSpinCount = CsSpinCount, a0
add rpCsRecursion = CsRecursionCount, a0
nop.f 0
add rpThreadId = TeClientId+CidUniqueThread, teb
;;
//
// Load the id of the currently running thread, anticipating
// that it may be needed.
//
ld8 rThreadId = [rpThreadId]
ld4 rSpinCount = [rpSpinCount]
add rpOwner = CsOwningThread, a0
;;
//
// Branch out if spin count is non-zero
//
cmp4.ne pSpin, pNoSpin = rSpinCount, zero
mov v0 = STATUS_SUCCESS
(pSpin) br.spnt RecsSpin
//
// Atomically increment the lock count at location (rpLockCount)
// and put the old value in register "rOldLockCount".
// Swizzle a pointer to address the thread id field in teb structure.
//
RecsNoSpin:
fetchadd4.acq rOldLockCount = [rpLockCount], 1
ld4.nt1 rRecursionCount = [rpCsRecursion]
;;
//
// Check the original value of lock count to determine if the
// lock is free. If the value is -1, it is free.
//
cmp4.eq pFree, pHeld = -1, rOldLockCount
;;
//
// if lock is not free, get the thread id of its current owner
// otherwise, save the currently running thread's id.
//
(pHeld) ld8 rOwnerId = [rpOwner]
(pFree) st8 [rpOwner] = rThreadId
(pFree) st4 [rpCsRecursion] = zero
(pFree) br.ret.sptk.clr brp // return
;;
//
// if lock is not free, compare the owner id of the critical section against
// that of the thread to determine if this thread is the owner.
// otherwise, return to caller.
//
cmp.eq pGo, pWait = rThreadId, rOwnerId
mov out0 = a0
add rRecursionCount = 1, rRecursionCount
;;
//
// if the thread has already owned the lock, save the updated
// recursion count and return.
// otherwise, wait for the critical section to be released.
//
(pGo) st4 [rpCsRecursion] = rRecursionCount
(pGo) br.ret.sptk brp
(pWait) br.call.spnt.many brp = RtlpWaitForCriticalSection
;;
st8.rel [rpOwner] = rThreadId
st4 [rpCsRecursion] = zero
mov v0 = STATUS_SUCCESS
NESTED_RETURN
// A nonzero spin count is specified
//
RecsSpin:
ld8 rOwnerId = [rpOwner]
mov rT1 = -1
;;
zxt4 rT1 = rT1 // zero extend for compare with
;; // 4 byte lock value
mov ar.ccv = rT1 // compare value
cmp.ne pNotOwn = rOwnerId, rThreadId
(pNotOwn) br.spnt RecsNotOwn
//
// The critical section is owned by the current thread. Increment the lock
// count and the recursion count.
//
ld4 rRecursionCount = [rpCsRecursion]
fetchadd4.acq rOldLockCount = [rpLockCount], 1
;;
add rRecursionCount = 1, rRecursionCount
;;
st4 [rpCsRecursion] = rRecursionCount
br.ret.sptk.clr brp // return
//
// A nonzero spin count is specified and the current thread is not the owner.
//
RecsNotOwn:
cmpxchg4.acq rT1 = [rpLockCount], zero // try to acquire lock
;;
cmp4.ne pNotAcq = -1, rT1
(pNotAcq) br.spnt RecsNotAcq
//
// The critical section has been acquired. Set the owning thread and the initial
// recursion count and return success.
//
st8 [rpOwner] = rThreadId
st4 [rpCsRecursion] = zero
br.ret.sptk.clr brp // return
//
// The critical section is currently owned. Spin until it is either unowned
// or the spin count has reached zero.
//
// If LockCount > 0, then there are waiters. Don't spin because
// the lock will not free.
//
RecsNotAcq:
ld4 rOldLockCount = [rpLockCount]
;;
cmp4.eq pNotOwn = -1, rOldLockCount
cmp4.gt pNoSpin = rOldLockCount, zero
(pNoSpin) br.spnt RecsNoSpin
(pNotOwn) br.spnt RecsNotOwn
add rSpinCount = -1, rSpinCount
;;
cmp4.eq pNoSpin, pSpin = zero, rSpinCount
(pNoSpin) br.spnt RecsNoSpin
(pSpin) br.sptk RecsNotAcq
;;
NESTED_EXIT(RtlEnterCriticalSection)
//++
//
// NTSTATUS
// RtlLeaveCriticalSection(
// IN PRTL_CRITICAL_SECTION CriticalSection
// )
//
// Routine Description:
//
// This function enters a critical section.
//
// N.B. This function is duplicated in the runtime library.
//
// Arguments:
//
// CriticalSection (a0) - Supplies a pointer to a critical section.
//
// Return Value:
//
// STATUS_SUCCESS is returned as the function value.
//
// Algorithm in C:
//
// NTSTATUS
// RtlLeaveCriticalSection(
// IN PRTL_CRITICAL_SECTION CriticalSection
// )
// {
// LONG NewRecursionCount;
// LONG OldLockCount;
// BOOL ToRelease;
//
// ASSERT(CriticalSection->RecursionCount >= 0)
//
// if (CriticalSection->RecursionCount != 0) {
// CriticalSection->RecursionCount -= 1;
// AtomicDecrement(CriticalSection->LockCount);
// return STATUS_SUCCESS;
// }
//
// CriticalSection->OwningThread = 0;
// OldLockCount = AtomicDecrement(CriticalSection->LockCount);
//
// if ( OldLockCount != 0 ) {
// RtlpUnWaitCriticalSection(CriticalSection);
// }
//
// return STATUS_SUCCESS;
// }
//
//--
//
// register aliases
//
NESTED_ENTRY(RtlLeaveCriticalSection)
rpOwner=t0
rOldLockCount=t1
rRecursionCount=t2
rpLockCount=t5
rpCsRecursion=t9
pHold=pt0
pRel=pt1
pGo=pt7
pWait=pt8
NESTED_SETUP(1,2,1,0)
add rpCsRecursion = CsRecursionCount, a0
;;
PROLOGUE_END
//
// load recursion count
// swizzle pointers to address the LockCount and OwningThread
// fields in the critical section structure.
//
ld4 rRecursionCount = [rpCsRecursion]
add rpOwner = CsOwningThread, a0
add rpLockCount = CsLockCount, a0
;;
//
// check if the original value of the recursion count to determine
// if the lock is to be released.
//
// decrement the register copy of recursion count by 1 and save
// the new value in temp register
//
cmp.ne pHold, pRel = zero, rRecursionCount
add rRecursionCount = -1, rRecursionCount
add v0 = STATUS_SUCCESS, zero // return STATUS_SUCCESS
;;
//
// save the updated recursion count into the critical section structure.
//
// atomically decrement the lock count.
//
// if lock is still held, return to caller.
//
// Note: An atomic fetch & add with release form is used here
// all previous memory accesses are visible at this point.
//
// Note: All updates to the Critical Section structure MUST be complete
// prior to the lock count being decremented which releases the
// lock.
//
(pHold) st4 [rpCsRecursion] = rRecursionCount
(pRel) st8 [rpOwner] = zero // clear the owner field
;;
fetchadd4.rel rOldLockCount = [rpLockCount], -1
(pHold) br.ret.sptk.clr brp // return to caller
;;
//
// The lock is now free, check the original value of the lock count to
// determine if any other thread is waiting for this critical section.
// If no thread is waiting, return to caller immediately.
//
cmp4.ge pGo, pWait = zero, rOldLockCount
(pGo) br.ret.sptk.clr brp // return to caller
mov out0 = a0
(pWait) br.call.spnt.many brp = RtlpUnWaitCriticalSection
mov v0 = STATUS_SUCCESS // return STATUS_SUCCESS
NESTED_RETURN
;;
NESTED_EXIT(RtlLeaveCriticalSection)
//++
//
// BOOL
// RtlTryEnterCriticalSection(
// IN PRTL_CRITICAL_SECTION CriticalSection
// )
//
// Routine Description:
//
// This function attempts to enter a critical section without blocking.
//
// Arguments:
//
// CriticalSection (a0) - Supplies a pointer to a critical section.
//
// Return Value:
//
// If the critical section was successfully entered, then a value of TRUE
// is returned as the function value. Otherwise, a value of FALSE is returned
//
//--
LEAF_ENTRY(RtlTryEnterCriticalSection)
//
// register aliases
//
rT0=t0
rT1=t1
rpThreadId=t3
rOldLockCount=t4
rpLockCount=t5
rRecursionCount=t6
rOwnerId=t7
rpCsRecursion=t8
rThreadId=t9
rpOwner=t10
pFree=pt5
pHeld=pt6
pOwn=pt7
pFail=pt8
alloc rT0 = ar.pfs, 1, 0, 0, 0
movl rT1 = 0xffffffff
;;
mov ar.ccv = rT1
add rpOwner = CsOwningThread, a0
add rpLockCount = CsLockCount, a0
;;
cmpxchg4.acq rOldLockCount = [rpLockCount], r0, ar.ccv
add rpCsRecursion = CsRecursionCount, a0
add rpThreadId = TeClientId+CidUniqueThread, teb
;;
ld8 rOwnerId = [rpOwner]
cmp4.eq pFree, pHeld = rT1, rOldLockCount
;;
ld8.nt1 rThreadId = [rpThreadId]
(pHeld) ld4.nt1 rRecursionCount = [rpCsRecursion]
mov v0 = TRUE
;;
(pFree) st4 [rpCsRecursion] = zero
(pFree) st8 [rpOwner] = rThreadId
(pHeld) cmp.eq pOwn, pFail = rThreadId, rOwnerId
(pFree) br.ret.sptk.clr brp
;;
(pOwn) fetchadd4.acq rT0 = [rpLockCount], 1
(pOwn) add rRecursionCount = 1, rRecursionCount
nop.i 0
;;
(pOwn) st4.rel [rpCsRecursion] = rRecursionCount
(pFail) mov v0 = FALSE
br.ret.sptk.clr brp
LEAF_EXIT(RtlTryEnterCriticalSection)