/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    spinlock.c

Abstract:

    This module implements the platform specific functions for acquiring
    and releasing spin locks.

Author:

    David N. Cutler (davec) 12-Jun-2000

Environment:

    Kernel mode only.

Revision History:

--*/

#include "ki.h"

__forceinline
VOID
KxAcquireSpinLock (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function acquires a spin lock at the current IRQL.

Arguments:

    SpinLock - Supplies a pointer to an spin lock.

Return Value:

    None.

--*/

{

    PKTHREAD Thread;

    //
    // Acquire the specified spin lock at the current IRQL.
    //

#if !defined(NT_UP)

#if DBG

    Thread = KeGetCurrentThread();
    while (InterlockedCompareExchange64((PLONGLONG)SpinLock,
                                        (LONGLONG)Thread,
                                        0) != 0) {

#else

    while (InterlockedExchange64((PLONGLONG)SpinLock,
                                 (LONGLONG)SpinLock) != 0) {

#endif // DBG

        do {
        } while (*(volatile LONGLONG *)SpinLock != 0);
    }

#endif // !defined(NT_UP)

    return;
}

__forceinline
BOOLEAN
KxTryToAcquireSpinLock (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function attempts acquires a spin lock at the current IRQL. If
    the spinlock is already owned, then FALSE is returned. Otherwise,
    TRUE is returned.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    If the spin lock is acquired a value TRUE is returned. Otherwise, FALSE
    is returned as the function value.

--*/

{

    PKTHREAD Thread;

    //
    // Try to acquire the specified spin lock at the current IRQL.
    //

#if !defined(NT_UP)

    if (*(volatile ULONGLONG *)SpinLock == 0) {

#if DBG

        Thread = KeGetCurrentThread();
        return InterlockedCompareExchange64((PLONGLONG)SpinLock,
                                            (LONGLONG)Thread,
                                            0) == 0;

#else

        return InterlockedExchange64((PLONGLONG)SpinLock,
                                     (LONGLONG)SpinLock) == 0;

#endif // DBG

    } else {
        return FALSE;
    }

#else

    return TRUE;

#endif // !defined(NT_UP)

}

__forceinline
VOID
KxReleaseSpinLock (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function releases the specified spin lock at the current IRQL.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    None.

--*/

{

#if !defined(NT_UP)

#if DBG

    ASSERT(*(volatile ULONGLONG *)SpinLock == (ULONGLONG)KeGetCurrentThread());

#endif // DBG

    *(volatile ULONGLONG *)SpinLock = 0;

#endif // !defined(NT_UP)

    return;
}

VOID
KeInitializeSpinLock (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function initializes a spin lock.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    None.

--*/

{

    *(volatile ULONGLONG *)SpinLock = 0;
    return;
}

KIRQL
KeAcquireSpinLockRaiseToDpc (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function raises IRQL to DISPATCH_LEVEL and acquires the specified
    spin lock.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    The previous IRQL is returned.

--*/

{

    KIRQL OldIrql;

    //
    // Raise IRQL to DISPATCH_LEVEL and acquire the specified spin lock.
    //

    OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
    KxAcquireSpinLock(SpinLock);
    return OldIrql;
}

KIRQL
KeAcquireSpinLockRaiseToSynch (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function raises IRQL to SYNCH_LEVEL and acquires the specified
    spin lock.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    The previous IRQL is returned as the function value.

--*/

{

    KIRQL OldIrql;

    //
    // Raise IRQL to SYNCH_LEVEL and acquire the specified spin lock.
    //

    OldIrql = KfRaiseIrql(SYNCH_LEVEL);
    KxAcquireSpinLock(SpinLock);
    return OldIrql;
}

VOID
KeAcquireSpinLockAtDpcLevel (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function acquires a spin lock at the current IRQL.

Arguments:

    SpinLock - Supplies a pointer to an spin lock.

Return Value:

    None.

--*/

{

    //
    // Acquired the specified spin lock at the current IRQL.
    //

    KxAcquireSpinLock(SpinLock);
    return;
}

BOOLEAN
KeTryToAcquireSpinLock (
    IN PKSPIN_LOCK SpinLock,
    OUT PKIRQL OldIrql
    )

/*++

Routine Description:

    This function raises IRQL to DISPATCH level and attempts to acquire a
    spin lock. If the spin lock is already owned, then IRQL is restored to
    its previous value and FALSE is returned. Otherwise, the spin lock is
    acquired and TRUE is returned.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

    OldIrql - Supplies a pointer to a variable that receives the old IRQL.

Return Value:

    If the spin lock is acquired a value TRUE is returned. Otherwise, FALSE
    is returned.

--*/

{

    //
    // Raise IRQL to DISPATCH level and attempt to acquire the specified
    // spin lock.
    //

    *OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
    if (KxTryToAcquireSpinLock(SpinLock) == FALSE) {
        KeLowerIrql(*OldIrql);
        return FALSE;
    }

    return TRUE;
}

BOOLEAN
KeTryToAcquireSpinLockAtDpcLevel (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function attempts acquires a spin lock at the current IRQL. If
    the spinlock is already owned, then FALSE is returned. Otherwise,
    TRUE is returned.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    If the spin lock is acquired a value TRUE is returned. Otherwise, FALSE
    is returned as the function value.

--*/

{

    //
    // Try to acquire the specified spin lock at the current IRQL.
    //

    return KxTryToAcquireSpinLock(SpinLock);
}

VOID
KeReleaseSpinLock (
    IN PKSPIN_LOCK SpinLock,
    IN KIRQL OldIrql
    )

/*++

Routine Description:

    This function releases the specified spin lock and lowers IRQL to a
    previous value.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

    OldIrql - Supplies the previous IRQL value.

Return Value:

    None.

--*/

{

    //
    // Release the specified spin lock and lower IRQL.
    //

    KxReleaseSpinLock(SpinLock);
    KeLowerIrql(OldIrql);
    return;
}

VOID
KeReleaseSpinLockFromDpcLevel (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function releases a spin lock at the current IRQL.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    None.

--*/

{

    //
    // Release the specified spin lock.
    //

    KxReleaseSpinLock(SpinLock);
    return;
}

#if defined(KeTestSpinLock)
#undef KeTestSpinLock
#endif


BOOLEAN
KeTestSpinLock (
    IN PKSPIN_LOCK SpinLock
    )

/*++

Routine Description:

    This function tests a spin lock to determine if it is currently owned.
    If the spinlock is already owned, then FALSE is returned. Otherwise,
    TRUE is returned.

Arguments:

    SpinLock - Supplies a pointer to a spin lock.

Return Value:

    If the spin lock is currently owned, then a value of FALSE is returned.
    Otherwise, a value of TRUE is returned.

--*/

{

#if !defined(NT_UP)
    return (*(volatile ULONGLONG *)SpinLock == 0);
#else
    return TRUE;
#endif // !defined(NT_UP)
}