|
|
// TITLE("Enter and Leave Critical Section") //++ // // Copyright (c) 1991 Microsoft Corporation // Copyright (c) 1992 Digital Equipment Corporation // // Module Name: // // critsect.s // // Abstract: // // This module implements functions to support user mode critical sections. // // Author: // // David N. Cutler 1-May-1992 // // Environment: // // Any mode. // // Revision History: // // Thomas Van Baak (tvb) 21-May-1992 // // Adapted for Alpha AXP. // //--
#include "ksalpha.h"
SBTTL("Enter Critical Section") //++ // // NTSTATUS // RtlEnterCriticalSection ( // 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. // //--
.struct 0 EcRa: .space 8 // saved return address EcA0: .space 8 // saved critical section address EcA1: .space 8 // saved unique thread id .space 1 * 8 // required for 16-byte stack alignment EcFrameLength: // length of stack frame
NESTED_ENTRY(RtlEnterCriticalSection, EcFrameLength, zero)
lda sp, -EcFrameLength(sp) // allocate stack frame stq ra, EcRa(sp) // save return address
PROLOGUE_END
GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) get TEB address in v0
ldl t3, CsSpinCount(a0) // check if spin count is zero LDP a1, TeClientId + CidUniqueThread(v0) // get current thread id bne t3, 50f // if ne, spin count specified
// // Attempt to enter the critical section. //
10: ldl_l t0, CsLockCount(a0) // get addend value - locked addl t0, 1, t0 // increment addend value mov t0, t1 // stl_c t1, CsLockCount(a0) // store conditionally beq t1, 40f // if eq, conditional store failed mb // synchronize memory access
// // If the critical section is not already owned, then initialize the owner // thread id, initialize the recursion count, and return a success status. // The updated lock value is now in t0. //
bne t0, 20f // if ne, lock already owned STP a1, CsOwningThread(a0) // set critical section owner ldil v0, STATUS_SUCCESS // set return status lda sp, EcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
// // The critical section is owned. If the current thread is the owner, then // increment the recursion count, and return a success status. Otherwise, // wait for critical section ownership. // The current thread unique id is in a1. //
20: LDP t0, CsOwningThread(a0) // get unique id of owner thread cmpeq a1, t0, t1 // check if current thread is owner beq t1, 30f // if eq, current thread not owner ldl t0, CsRecursionCount(a0) // increment the recursion count addl t0, 1, t2 // stl t2, CsRecursionCount(a0) // ldil v0, STATUS_SUCCESS // set return status lda sp, EcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
// // The critical section is owned by a thread other than the current thread. // Wait for ownership of the critical section. // N.B. a1 is just a temp register below, not an argument to the function. //
30: stq a0, EcA0(sp) // save address of critical section stq a1, EcA1(sp) // save unique thread id bsr ra, RtlpWaitForCriticalSection // wait for critical section ldq a0, EcA0(sp) // restore address of critical section ldq a1, EcA1(sp) // restore unique thread id STP a1, CsOwningThread(a0) // set critical section owner ldil v0, STATUS_SUCCESS // set return status ldq ra, EcRa(sp) // restore return address lda sp, EcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
// // We expect the store conditional will usually succeed the first time so it // is faster to branch forward (predicted not taken) to here and then branch // backward (predicted taken) to where we wanted to go. //
40: br zero, 10b // go try lock again
// // A nonzero spin count is specified //
50: LDP t4, CsOwningThread(a0) // get owner thread id cmpeq t4, a1, t5 // check if current thread is owner beq t5, 60f // if eq, current thread not owner
// // The critical section is owned by the current thread. Increment the lock // count and the recursion count. //
55: ldl_l t0, CsLockCount(a0) // get addend value - locked addl t0, 1, t1 // increment addend value stl_c t1, CsLockCount(a0) // store conditionally beq t1, 59f // if lock-flag eq zero, store failed mb // synchronize memory access ldl t3, CsRecursionCount(a0) // increment recursion count addl t3, 1, t4 // stl t4, CsRecursionCount(a0) // ldil v0, STATUS_SUCCESS // set return status lda sp, EcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
// // Store conditional attempt failed. //
59: br zero, 55b // go try lock again
// // A nonzero spin count is specified and the current thread is not the owner. //
60: ldl_l t0, CsLockCount(a0) // get addend value - locked addl t0, 1, t1 // increment addend value bne t1, 70f // if ne, critical section is owned stl_c t1, CsLockCount(a0) // set new lock count beq t1, 69f // if eq, conditional store failed mb // synchronize memory access
// // The critical section has been acquired. Set the owning thread and the initial // recursion count. //
STP a1, CsOwningThread(a0) // set critical section owner ldil v0, STATUS_SUCCESS // set return status lda sp, EcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
// // Store conditional attempt failed. //
69: br zero, 60b //
// // The critical section is currently owned. Spin until it is either unowned // or the spin count has reached zero. Spin count is in t3 // // If waiters are present, don't spin on the lock since we will never see it // go free. //
70: ldl t0, CsLockCount(a0) // check if lock is owned addl t0, 1, t1 // bgt t0, 10b // if >=, then do not spin beq t1, 60b // if eq, lock is not owned subl t3, 1, t3 // decrement spin count bne t3, 70b // if nz, continue spinning br 10b // spin expired, go wait for lock
.end RtlEnterCriticalSection
SBTTL("Leave Critical Section") //++ // // NTSTATUS // RtlLeaveCriticalSection ( // IN PRTL_CRITICAL_SECTION CriticalSection // ) // // Routine Description: // // This function leaves 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. // //--
.struct 0 LcRa: .space 8 // saved return address .space 1 * 8 // required for 16-byte stack alignment LcFrameLength: // length of stack frame
NESTED_ENTRY(RtlLeaveCriticalSection, LcFrameLength, zero)
lda sp, -LcFrameLength(sp) // allocate stack frame stq ra, LcRa(sp) // save return address
PROLOGUE_END
// // If the current thread is not the owner of the critical section, then // raise an exception. //
#if DBG
GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) get TEB address in v0
LDP a1, TeClientId + CidUniqueThread(v0) // get current thread id LDP t0, CsOwningThread(a0) // get owner thread id cmpeq a1, t0, t1 // check if current thread owner bne t1, 10f // if ne, current thread is owner bsr ra, RtlpNotOwnerCriticalSection // raise exception ldil v0, STATUS_INVALID_OWNER // set completion status ldq ra, LcRa(sp) // restore return address lda sp, LcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
#endif
// // Decrement the recursion count. If the result is zero, then the lock // is no longer owned. //
10: ldl t0, CsRecursionCount(a0) // decrement recursion count subl t0, 1, t0 // bge t0, 30f // if ge, lock still owned STP zero, CsOwningThread(a0) // clear owner thread id
// // Decrement the lock count and check if a waiter should be continued. // //
20: mb // synchronize memory access ldl_l t0, CsLockCount(a0) // get addend value - locked subl t0, 1, t0 // decrement addend value mov t0, t1 // copy updated value to t1 for store stl_c t1, CsLockCount(a0) // store conditionally beq t1, 60f // if eq, conditional store failed blt t0, 50f // if lt, no waiter present bsr ra, RtlpUnWaitCriticalSection // unwait thread ldil v0, STATUS_SUCCESS // set completion status ldq ra, LcRa(sp) // restore return address lda sp, LcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
// // Decrement the lock count and return a success status since the lock // is still owned. //
30: stl t0, CsRecursionCount(a0) // store updated recursion count 40: ldl_l t0, CsLockCount(a0) // get addend value - locked subl t0, 1, t0 // decrement addend value stl_c t0, CsLockCount(a0) // store conditionally beq t0, 70f // if lock-flag eq zero, store failed 50: ldil v0, STATUS_SUCCESS // set completion status lda sp, LcFrameLength(sp) // deallocate stack frame ret zero, (ra) // return
// // We expect the store conditional will usually succeed the first time so it // is faster to branch forward (predicted not taken) to here and then branch // backward (predicted taken) to where we wanted to go. //
60: br zero, 20b // go try lock again
70: br zero, 40b // go try lock again
.end RtlLeaveCriticalSection
SBTTL("Try to Enter Critical Section") //++ // // BOOLEAN // 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. // //--
.struct 0 EcRa: .space 8 // saved return address EcA0: .space 8 // saved critical section address EcA1: .space 8 // saved unique thread id .space 1 * 8 // required for 16-byte stack alignment EcFrameLength: // length of stack frame
LEAF_ENTRY(RtlTryEnterCriticalSection)
GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) get TEB address in v0
LDP a1, TeClientId + CidUniqueThread(v0) // get current thread unique id
// // Attempt to enter the critical section. //
10: ldl_l t0, CsLockCount(a0) // get addend value - locked addl t0, 1, t1 // increment addend value bne t1, 20f // critical section owned stl_c t1, CsLockCount(a0) // store conditionally beq t1, 40f // if eq, conditional store failed mb // synchronize memory access
// // The critical section is now owned by this thread. Initialize the owner // thread id and return a successful status. //
STP a1, CsOwningThread(a0) // set critical section owner ldil v0, TRUE // set success status ret zero, (ra)
// // The critical section is already owned. If it is owned by another thread, // return FALSE immediately. If it is owned by this thread, we must increment // the lock count here. //
20: LDP t2, CsOwningThread(a0) // get current owner cmpeq t2, a1, t3 // check if current thread owner bne t3, 30f // if ne, current thread owner bis zero,zero,v0 // set failure status ret zero, (ra) // return
// // This thread is already the owner of the critical section. Perform an atomic // increment of the LockCount and a normal increment of the RecursionCount and // return success. //
30: ldl_l t0, CsLockCount(a0) // get addend value - locked addl t0, 1, t1 // increment addend value stl_c t1, CsLockCount(a0) // store conditionally beq t1, 50f // if eq, conditional store failed ldl t0, CsRecursionCount(a0) // increment recursion count addl t0, 1, t1 // stl t1, CsRecursionCount(a0) // ldil v0, TRUE // set success status ret zero, (ra) // return
// // We expect the store conditional will usually succeed the first time so it // is faster to branch forward (predicted not taken) to here and then branch // backward (predicted taken) to where we wanted to go. //
40: br zero, 10b // go try lock again
50: br zero, 30b // retry lock
.end RtlTryEnterCriticalSection
|