/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    semphobj.c

Abstract:

    This module implements the kernel semaphore object. Functions
    are provided to initialize, read, and release semaphore objects.

--*/

#include "ki.h"

//
// The following assert macro is used to check that an input semaphore is
// really a ksemaphore and not something else, like deallocated pool.
//

#define ASSERT_SEMAPHORE(E) {                    \
    ASSERT((E)->Header.Type == SemaphoreObject); \
}

VOID
KeInitializeSemaphore (
    __out PRKSEMAPHORE Semaphore,
    __in LONG Count,
    __in LONG Limit
    )

/*++

Routine Description:

    This function initializes a kernel semaphore object. The initial
    count and limit of the object are set to the specified values.

Arguments:

    Semaphore - Supplies a pointer to a dispatcher object of type
        semaphore.

    Count - Supplies the initial count value to be assigned to the
        semaphore.

    Limit - Supplies the maximum count value that the semaphore
        can attain.

Return Value:

    None.

--*/

{

    //
    // Initialize standard dispatcher object header and set initial
    // count and maximum count values.
    //

    Semaphore->Header.Type = SemaphoreObject;
    Semaphore->Header.Size = sizeof(KSEMAPHORE) / sizeof(LONG);
    Semaphore->Header.SignalState = Count;
    InitializeListHead(&Semaphore->Header.WaitListHead);
    Semaphore->Limit = Limit;
    return;
}

LONG
KeReadStateSemaphore (
    __in PRKSEMAPHORE Semaphore
    )

/*++

Routine Description:

    This function reads the current signal state of a semaphore object.

Arguments:

    Semaphore - Supplies a pointer to a dispatcher object of type
        semaphore.

Return Value:

    The current signal state of the semaphore object.

--*/

{

    ASSERT_SEMAPHORE( Semaphore );

    //
    // Return current signal state of semaphore object.
    //

    return Semaphore->Header.SignalState;
}

LONG
KeReleaseSemaphore (
    __inout PRKSEMAPHORE Semaphore,
    __in KPRIORITY Increment,
    __in LONG Adjustment,
    __in BOOLEAN Wait
    )

/*++

Routine Description:

    This function releases a semaphore by adding the specified adjustment
    value to the current semaphore count and attempts to satisfy as many
    Waits as possible. The previous signal state of the semaphore object
    is returned as the function value.

Arguments:

    Semaphore - Supplies a pointer to a dispatcher object of type
        semaphore.

    Increment - Supplies the priority increment that is to be applied
        if releasing the semaphore causes a Wait to be satisfied.

    Adjustment - Supplies value that is to be added to the current
        semaphore count.

    Wait - Supplies a boolean value that signifies whether the call to
        KeReleaseSemaphore will be immediately followed by a call to one
        of the kernel Wait functions.

Return Value:

    The previous signal state of the semaphore object.

--*/

{

    LONG NewState;
    KIRQL OldIrql;
    LONG OldState;
    PRKTHREAD Thread;

    ASSERT_SEMAPHORE( Semaphore );
    ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

    //
    // Raise IRQL to dispatcher level and lock dispatcher database.
    //

    KiLockDispatcherDatabase(&OldIrql);

    //
    // Capture the current signal state of the semaphore object and
    // compute the new count value.
    //

    OldState = ReadForWriteAccess(&Semaphore->Header.SignalState);
    NewState = OldState + Adjustment;

    //
    // If the new state value is greater than the limit or a carry occurs,
    // then unlock the dispatcher database, and raise an exception.
    //

    if ((NewState > Semaphore->Limit) || (NewState < OldState)) {
        KiUnlockDispatcherDatabase(OldIrql);
        ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED);
    }

    //
    // Set the new signal state of the semaphore object and set the wait
    // next value. If the previous signal state was Not-Signaled (i.e.
    // the count was zero), and the wait queue is not empty, then attempt
    // to satisfy as many Waits as possible.
    //

    Semaphore->Header.SignalState = NewState;
    if ((OldState == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) {
        KiWaitTest(Semaphore, Increment);
    }

    //
    // If the value of the Wait argument is TRUE, then return to the
    // caller with IRQL raised and the dispatcher database locked. Else
    // release the dispatcher database lock and lower IRQL to its
    // previous value.
    //

    if (Wait != FALSE) {
        Thread = KeGetCurrentThread();
        Thread->WaitNext = Wait;
        Thread->WaitIrql = OldIrql;

    } else {
        KiUnlockDispatcherDatabase(OldIrql);
    }

    //
    // Return previous signal state of semaphore object.
    //

    return OldState;
}

