|
|
//++ // // 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: YIELD 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)
|