windows-nt/Source/XPSP1/NT/base/ntdll/alpha/critsect.s
2020-09-26 16:20:57 +08:00

425 lines
15 KiB
ArmAsm
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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