Esempio n. 1
0
/*++
 * @name ExfTryToWakePushLock
 * @implemented NT5.2
 *
 *     The ExfTryToWakePushLock attemps to wake a waiting pushlock.
 *
 * @param PushLock
 *        Pointer to a PushLock which is in the wait state.
 *
 * @return None.
 *
 * @remarks The pushlock must be in a wait state and must not be already waking.
 *
 *--*/
VOID
FASTCALL
ExfTryToWakePushLock(PEX_PUSH_LOCK PushLock)
{
    EX_PUSH_LOCK OldValue = *PushLock, NewValue;

    /*
     * If the Pushlock is not waiting on anything, or if it's already waking up
     * and locked, don't do anything
     */
    if ((OldValue.Waking) || (OldValue.Locked) || !(OldValue.Waiting)) return;

    /* Make it Waking */
    NewValue = OldValue;
    NewValue.Waking = TRUE;

    /* Write the New Value */
    if (InterlockedCompareExchangePointer(&PushLock->Ptr,
                                          NewValue.Ptr,
                                          OldValue.Ptr) == OldValue.Ptr)
    {
        /* Wake the Pushlock */
        ExfWakePushLock(PushLock, NewValue);
    }
}
Esempio n. 2
0
/*++
 * ExfReleasePushLockExclusive
 * @implemented NT5.2
 *
 *     The ExfReleasePushLockExclusive routine releases a previously
 *     exclusively acquired PushLock.
 *
 * @params PushLock
 *         Pointer to a previously acquired pushlock.
 *
 * @return None.
 *
 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
 *          This macro should usually be paired up with KeLeaveCriticalRegion.
 *
 *--*/
VOID
FASTCALL
ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock)
{
    EX_PUSH_LOCK NewValue, WakeValue;
    EX_PUSH_LOCK OldValue = *PushLock;

    /* Loop until we can change */
    for (;;)
    {
        /* Sanity checks */
        ASSERT(OldValue.Locked);
        ASSERT(OldValue.Waiting || OldValue.Shared == 0);

        /* Check if it's waiting and not yet waking */
        if ((OldValue.Waiting) && !(OldValue.Waking))
        {
            /* Remove the lock bit, and add the wake bit */
            NewValue.Value = (OldValue.Value &~ EX_PUSH_LOCK_LOCK) |
                              EX_PUSH_LOCK_WAKING;

            /* Sanity check */
            ASSERT(NewValue.Waking && !NewValue.Locked);

            /* Write the New Value. Save our original value for waking */
            WakeValue = NewValue;
            NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
                                                             NewValue.Ptr,
                                                             OldValue.Ptr);

            /* Check if the value changed behind our back */
            if (NewValue.Value == OldValue.Value)
            {
                /* Wake the Pushlock */
                ExfWakePushLock(PushLock, WakeValue);
                break;
            }
        }
        else
        {
            /* A simple unlock */
            NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_LOCK;

            /* Sanity check */
            ASSERT(NewValue.Waking && !NewValue.Waiting);

            /* Write the New Value */
            NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
                                                             NewValue.Ptr,
                                                             OldValue.Ptr);

            /* Check if the value changed behind our back */
            if (NewValue.Value == OldValue.Value) break;
        }

        /* Loop again */
        OldValue = NewValue;
    }
}
Esempio n. 3
0
NTKERNELAPI
VOID
DECLSPEC_NOINLINE
FASTCALL
ExfReleasePushLockExclusive (
     __inout PEX_PUSH_LOCK PushLock
     )
/*++

Routine Description:

    Release a push lock that was acquired exclusive

Arguments:

    PushLock - Push lock to be released

Return Value:

    None

--*/
{
    EX_PUSH_LOCK OldValue, NewValue, TopValue;

    OldValue = ReadForWriteAccess (PushLock);

    while (1) {

        ASSERT (OldValue.Locked);
        ASSERT (OldValue.Waiting || OldValue.Shared == 0);

        if (OldValue.Waiting && !OldValue.Waking) {
            NewValue.Value = OldValue.Value - EX_PUSH_LOCK_LOCK + EX_PUSH_LOCK_WAKING;
            ASSERT (NewValue.Waking && !NewValue.Locked);
            TopValue = NewValue;
            if ((NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
                                                                   NewValue.Ptr,
                                                                   OldValue.Ptr)) == OldValue.Ptr) {
                ExfWakePushLock (PushLock, TopValue);
                break;
            }
        } else {
            NewValue.Value = OldValue.Value - EX_PUSH_LOCK_LOCK;
            ASSERT (NewValue.Waking || !NewValue.Waiting);
            if ((NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
                                                                   NewValue.Ptr,
                                                                   OldValue.Ptr)) == OldValue.Ptr) {
                break;
            }
        }
        OldValue = NewValue;
    }
}
Esempio n. 4
0
VOID
FASTCALL
ExfTryToWakePushLock (
    __inout PEX_PUSH_LOCK PushLock
    )
/*++

Routine Description:

    Tries to set the wake bit and wake the pushlock

Arguments:

    PushLock - Push lock to be woken

Return Value:

    None

--*/
{
    EX_PUSH_LOCK OldValue, NewValue;

    OldValue = *PushLock;

    if (OldValue.Waking || OldValue.Locked || !OldValue.Waiting) {
        return;
    }

    NewValue.Value = OldValue.Value + EX_PUSH_LOCK_WAKING;
    if (InterlockedCompareExchangePointer (&PushLock->Ptr,
                                           NewValue.Ptr,
                                           OldValue.Ptr) == OldValue.Ptr) {
        ExfWakePushLock (PushLock, NewValue);
    }
}
Esempio n. 5
0
/*++
 * @name ExfReleasePushLockShared
 * @implemented NT5.2
 *
 *     The ExfReleasePushLockShared macro releases a previously acquired PushLock.
 *
 * @params PushLock
 *         Pointer to a previously acquired pushlock.
 *
 * @return None.
 *
 * @remarks Callers of ExReleasePushLockShared must be running at IRQL <= APC_LEVEL.
 *          This macro should usually be paired up with KeLeaveCriticalRegion.
 *
 *--*/
VOID
FASTCALL
ExfReleasePushLockShared(PEX_PUSH_LOCK PushLock)
{
    EX_PUSH_LOCK OldValue = *PushLock, NewValue, WakeValue;
    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock;

    /* Check if someone is waiting on the lock */
    while (!OldValue.Waiting)
    {
        /* Check if it's shared */
        if (OldValue.Shared > 1)
        {
            /* Write the Old Value but decrease share count */
            NewValue = OldValue;
            NewValue.Shared--;
        }
        else
        {
            /* Simply clear the lock */
            NewValue.Value = 0;
        }

        /* Write the New Value */
        NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
                                                         NewValue.Ptr,
                                                         OldValue.Ptr);
        if (NewValue.Value == OldValue.Value) return;

        /* Did it enter a wait state? */
        OldValue = NewValue;
    }

    /* Ok, we do know someone is waiting on it. Are there more then one? */
    if (OldValue.MultipleShared)
    {
        /* Get the wait block */
        WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
                                               ~EX_PUSH_LOCK_PTR_BITS);

        /* Loop until we find the last wait block */
        while (TRUE)
        {
            /* Get the last wait block */
            LastWaitBlock = WaitBlock->Last;

            /* Did it exist? */
            if (LastWaitBlock)
            {
                /* Choose it */
                WaitBlock = LastWaitBlock;
                break;
            }

            /* Keep searching */
            WaitBlock = WaitBlock->Next;
        }

        /* Sanity checks */
        ASSERT(WaitBlock->ShareCount > 0);
        ASSERT(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE);

        /* Do the decrease and check if the lock isn't shared anymore */
        if (InterlockedDecrement(&WaitBlock->ShareCount) > 0) return;
    }

    /*
     * If nobody was waiting on the block, then we possibly reduced the number
     * of times the pushlock was shared, and we unlocked it.
     * If someone was waiting, and more then one person is waiting, then we
     * reduced the number of times the pushlock is shared in the wait block.
     * Therefore, at this point, we can now 'satisfy' the wait.
     */
    for (;;)
    {
        /* Now we need to see if it's waking */
        if (OldValue.Waking)
        {
            /* Remove the lock and multiple shared bits */
            NewValue.Value = OldValue.Value;
            NewValue.MultipleShared = FALSE;
            NewValue.Locked = FALSE;

            /* Sanity check */
            ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);

            /* Write the new value */
            NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
                                                             NewValue.Ptr,
                                                             OldValue.Ptr);
            if (NewValue.Value == OldValue.Value) return;
        }
        else
        {
            /* Remove the lock and multiple shared bits */
            NewValue.Value = OldValue.Value;
            NewValue.MultipleShared = FALSE;
            NewValue.Locked = FALSE;

            /* It's not already waking, so add the wake bit */
            NewValue.Waking = TRUE;

            /* Sanity check */
            ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);

            /* Write the new value */
            WakeValue = NewValue;
            NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
                                                             NewValue.Ptr,
                                                             OldValue.Ptr);
            if (NewValue.Value != OldValue.Value) continue;

            /* The write was successful. The pushlock is Unlocked and Waking */
            ExfWakePushLock(PushLock, WakeValue);
            return;
        }
    }
}
Esempio n. 6
0
/*++
 * @name ExpOptimizePushLockList
 *
 *     The ExpOptimizePushLockList routine optimizes the list of waiters
 *     associated to a pushlock's wait block.
 *
 * @param PushLock
 *        Pointer to a pushlock whose waiter list needs to be optimized.
 *
 * @param OldValue
 *        Last known value of the pushlock before this routine was called.
 *
 * @return None.
 *
 * @remarks At the end of the optimization, the pushlock will also be wakened.
 *
 *--*/
VOID
FASTCALL
ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock,
                        EX_PUSH_LOCK OldValue)
{
    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock, PreviousWaitBlock, FirstWaitBlock;
    EX_PUSH_LOCK NewValue;

    /* Start main loop */
    for (;;)
    {
        /* Check if we've been unlocked */
        if (!OldValue.Locked)
        {
            /* Wake us up and leave */
            ExfWakePushLock(PushLock, OldValue);
            break;
        }

        /* Get the wait block */
        WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
                                               ~EX_PUSH_LOCK_PTR_BITS);

        /* Loop the blocks */
        FirstWaitBlock = WaitBlock;
        while (TRUE)
        {
            /* Get the last wait block */
            LastWaitBlock = WaitBlock->Last;
            if (LastWaitBlock)
            {
                /* Set this as the new last block, we're done */
                FirstWaitBlock->Last = LastWaitBlock;
                break;
            }

            /* Save the block */
            PreviousWaitBlock = WaitBlock;

            /* Get the next block */
            WaitBlock = WaitBlock->Next;

            /* Save the previous */
            WaitBlock->Previous = PreviousWaitBlock;
        }

        /* Remove the wake bit */
        NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING;

        /* Sanity checks */
        ASSERT(NewValue.Locked);
        ASSERT(!NewValue.Waking);

        /* Update the value */
        NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
                                                         NewValue.Ptr,
                                                         OldValue.Ptr);

        /* If we updated correctly, leave */
        if (NewValue.Value == OldValue.Value) break;

        /* Update value */
        OldValue = NewValue;
    }
}
Esempio n. 7
0
NTKERNELAPI
VOID
DECLSPEC_NOINLINE
FASTCALL
ExfReleasePushLock (
     __inout PEX_PUSH_LOCK PushLock
     )
/*++

Routine Description:

    Release a push lock that was acquired exclusively or shared

Arguments:

    PushLock - Push lock to be released

Return Value:

    None

--*/
{
    EX_PUSH_LOCK OldValue, NewValue, TopValue;
    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, NextWaitBlock;

    OldValue = ReadForWriteAccess (PushLock);

    ASSERT (OldValue.Locked);

    while (1) {

        if (!OldValue.Waiting) {

            if (OldValue.Shared > 1) {
                NewValue.Value = OldValue.Value - EX_PUSH_LOCK_SHARE_INC;
            } else {
                NewValue.Value = 0;
            }

            NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
                                                              NewValue.Ptr,
                                                              OldValue.Ptr);
            if (NewValue.Ptr == OldValue.Ptr) {
                return;
            }
            OldValue = NewValue;
        } else {

            //
            // See if we need to walk the list to find the share count
            //

            if (OldValue.MultipleShared) {
                WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK) (OldValue.Value & ~(ULONG_PTR)EX_PUSH_LOCK_PTR_BITS);

                while (1) {

                    NextWaitBlock = WaitBlock->Last;

                    if (NextWaitBlock != NULL) {
                        WaitBlock = NextWaitBlock;
                        break;
                    }

                    WaitBlock = WaitBlock->Next;
                }

                if (WaitBlock->ShareCount > 0) {
                    ASSERT (WaitBlock->Flags&EX_PUSH_LOCK_FLAGS_EXCLUSIVE);

                    if (InterlockedDecrement (&WaitBlock->ShareCount) > 0) {
                        return;
                    }
                }
            }

            while (1) {
                if (OldValue.Waking) {
                    NewValue.Value = OldValue.Value & ~(EX_PUSH_LOCK_LOCK |
                                                        EX_PUSH_LOCK_MULTIPLE_SHARED);
                    ASSERT (NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
                    if ((NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
                                                                           NewValue.Ptr,
                                                                           OldValue.Ptr)) == OldValue.Ptr) {
                        return;
                    }
                } else {
                    NewValue.Value = (OldValue.Value & ~(EX_PUSH_LOCK_LOCK |
                                                         EX_PUSH_LOCK_MULTIPLE_SHARED)) |
                                           EX_PUSH_LOCK_WAKING;
                    ASSERT (NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
                    TopValue = NewValue;
                    if ((NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
                                                                           NewValue.Ptr,
                                                                           OldValue.Ptr)) == OldValue.Ptr) {
                        ExfWakePushLock (PushLock, TopValue);
                        return;
                    }
                }
                OldValue = NewValue;
            }
        }
    }
}
Esempio n. 8
0
VOID
FASTCALL
ExpOptimizePushLockList (
    IN PEX_PUSH_LOCK PushLock,
    IN EX_PUSH_LOCK TopValue
    )
/*++

Routine Description:

    Walks the pushlock waiting list during contention and optimizes it

Arguments:

    PushLock - Push lock to be walked

    TopValue - Start of the chain (*PushLock)

Return Value:

    None

--*/
{
    EX_PUSH_LOCK OldValue, NewValue;
    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, PreviousWaitBlock, FirstWaitBlock, NextWaitBlock;

    OldValue = TopValue;
    while (1) {
        if (!OldValue.Locked) {
            ExfWakePushLock (PushLock, OldValue);
            break;
        }

        WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value & ~(EX_PUSH_LOCK_PTR_BITS));

        FirstWaitBlock = WaitBlock;

        while (1) {

            NextWaitBlock = WaitBlock->Last;
            if (NextWaitBlock != NULL) {
                FirstWaitBlock->Last = NextWaitBlock;
                break;
            }

            PreviousWaitBlock = WaitBlock;
            WaitBlock = WaitBlock->Next;
            WaitBlock->Previous = PreviousWaitBlock;
        }

        NewValue.Value = OldValue.Value - EX_PUSH_LOCK_WAKING;
        ASSERT (NewValue.Locked);
        ASSERT (!NewValue.Waking);
        if ((NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
                                                               NewValue.Ptr,
                                                               OldValue.Ptr)) == OldValue.Ptr) {
            break;
        }
        OldValue = NewValue;
    }
}