windows-nt/Source/XPSP1/NT/net/homenet/bridge/sys/brdgwref.c
2020-09-26 16:20:57 +08:00

446 lines
10 KiB
C

/*++
Copyright(c) 1999-2000 Microsoft Corporation
Module Name:
brdgwait.c
Abstract:
Ethernet MAC level bridge.
WAIT_REFCOUNT implementation
Author:
Mark Aiken
Environment:
Kernel mode driver
Revision History:
Feb 2000 - Original version
--*/
#define NDIS_MINIPORT_DRIVER
#define NDIS50_MINIPORT 1
#define NDIS_WDM 1
#pragma warning( push, 3 )
#include <ndis.h>
#include <ntddk.h>
#pragma warning( pop )
#include "bridge.h"
// ===========================================================================
//
// PUBLIC FUNCTIONS
//
// ===========================================================================
VOID
BrdgInitializeWaitRef(
IN PWAIT_REFCOUNT pRefcount,
IN BOOLEAN bResettable
)
/*++
Routine Description:
Initializes a wait-refcount
Arguments:
pRefcount The wait-refcount to initialize
Return Value:
none
--*/
{
NdisInitializeEvent(&pRefcount->Event);
pRefcount->Refcount = 0L;
pRefcount->state = WaitRefEnabled;
pRefcount->bResettable = bResettable;
NdisAllocateSpinLock( &pRefcount->lock );
// The event starts life signaled since the refcount starts at zero
NdisSetEvent(&pRefcount->Event);
}
BOOLEAN
BrdgIncrementWaitRef(
IN PWAIT_REFCOUNT pRefcount
)
/*++
Routine Description:
Increments (acquires) a wait-refcount
Arguments:
pRefcount The wait-refcount to acquire
Return Value:
TRUE if the wait-refcount was successfully acquired, FALSE otherwise (this can happen
if the wait-refcount has been shut down)
--*/
{
BOOLEAN bSuccess;
LONG Scratch = 0L;
SAFEASSERT( pRefcount != NULL );
NdisAcquireSpinLock( &pRefcount->lock );
if( pRefcount->state == WaitRefEnabled )
{
SAFEASSERT( pRefcount->Refcount >= 0L );
Scratch = ++pRefcount->Refcount;
bSuccess = TRUE;
}
else
{
// The wait-refcount isn't enabled.
SAFEASSERT( (pRefcount->state == WaitRefShutdown) ||
(pRefcount->state == WaitRefShuttingDown) );
bSuccess = FALSE;
}
if( bSuccess && (Scratch == 1L) )
{
// We incremented from zero. Reset the event.
NdisResetEvent( &pRefcount->Event );
}
NdisReleaseSpinLock( &pRefcount->lock );
return bSuccess;
}
VOID
BrdgReincrementWaitRef(
IN PWAIT_REFCOUNT pRefcount
)
/*++
Routine Description:
Re-increments a wait-refcount. This is guaranteed to succeed.
It is only legal to use this if the caller has already acquired the
wait-refcount (i.e., it is guaranteed that the refcount is > 0).
CALLING THIS WITHOUT HAVING FIRST ACQUIRED THE WAIT-REFCOUNT WITH
BrdgIncrementWaitRef IS A GREAT WAY TO SCREW UP YOUR CODE!
Arguments:
pRefcount The wait-refcount to re-acquire
Return Value:
none
--*/
{
LONG Scratch;
SAFEASSERT( pRefcount != NULL );
NdisAcquireSpinLock( &pRefcount->lock );
SAFEASSERT( (pRefcount->state == WaitRefEnabled) ||
(pRefcount->state == WaitRefShuttingDown) );
SAFEASSERT( pRefcount->Refcount >= 0L );
Scratch = ++pRefcount->Refcount;
NdisReleaseSpinLock( &pRefcount->lock );
// Should be impossible for us to have incremented from zero to one
SAFEASSERT( Scratch >= 2L );
}
VOID
BrdgDecrementWaitRef(
IN PWAIT_REFCOUNT pRefcount
)
/*++
Routine Description:
Decrements (releases) a previously incremented (acquired) wait-refcount.
Arguments:
pRefcount The wait-refcount to decrement
Return Value:
none
--*/
{
LONG Scratch;
SAFEASSERT( pRefcount != NULL );
NdisAcquireSpinLock( &pRefcount->lock );
SAFEASSERT( (pRefcount->state == WaitRefEnabled) ||
(pRefcount->state == WaitRefShuttingDown) );
Scratch = --pRefcount->Refcount;
SAFEASSERT( Scratch >= 0L );
if( Scratch == 0L )
{
// Signal anyone waiting for the refount to go to zero
NdisSetEvent( &pRefcount->Event );
}
NdisReleaseSpinLock( &pRefcount->lock );
}
VOID
BrdgBlockWaitRef(
IN PWAIT_REFCOUNT pRefcount
)
/*++
Routine Description:
Puts the wait-refcount in the shutting-down state, making it impossible
for the refcount to be incremented anymore.
This can be used to block further acquires of the wait-refcount in
advance of the shutdown process. Because shutting down the wait-refcount
involves waiting for it to hit zero, this can be called at high IRQL to
prevent further acquires of the wait-refcount in advance of a low-IRQL
call to BrdgShutdownWaitRef().
Arguments:
pRefcount The wait-refcount to block
Return Value:
none
--*/
{
SAFEASSERT( pRefcount != NULL );
NdisAcquireSpinLock( &pRefcount->lock );
if( pRefcount->state == WaitRefEnabled )
{
pRefcount->state = WaitRefShuttingDown;
}
else
{
// Do nothing; the wait-refcount is already
// shutting down or is already shut down.
SAFEASSERT( (pRefcount->state == WaitRefShutdown) ||
(pRefcount->state == WaitRefShuttingDown) );
}
NdisReleaseSpinLock( &pRefcount->lock );
}
BOOLEAN
BrdgShutdownWaitRefInternal(
IN PWAIT_REFCOUNT pRefcount,
IN BOOLEAN bRequireBlockedState
)
/*++
Routine Description:
Blocks new acquisitions of the wait-refcount and waits for the
number of consumers to go to zero. If TRUE is returned, the caller
can free any resources protected by the wait-refcount
Arguments:
pRefcount The wait-refcount to shut down
bRequireBlockedState TRUE means the shutdown attempt will fail if
the wait-refcount isn't in the shutting-down
state
Return Value:
TRUE if the wait-refcount was shut down
FALSE indicates that either the wait-refcount was reset or that
another thread of execution had already shut down the wait-refcount.
In both cases, the shared resources protected by the wait-refcount
should NOT be freed.
--*/
{
BOOLEAN bSuccess;
SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL);
SAFEASSERT( pRefcount != NULL );
NdisAcquireSpinLock( &pRefcount->lock );
if( pRefcount->state == WaitRefEnabled )
{
if( bRequireBlockedState )
{
// Caller was expecting the refcount to be shutting
// down. It must have been reset. That had better
// be OK!
SAFEASSERT( pRefcount->bResettable );
bSuccess = FALSE;
}
else
{
// Caller doesn't require that the refcount be
// shutting down. Transition to the shutting-down
// state.
pRefcount->state = WaitRefShuttingDown;
bSuccess = TRUE;
}
}
else if( pRefcount->state == WaitRefShutdown )
{
// Someone else already shut down the waitref.
// This always means failure.
SAFEASSERT( pRefcount->Refcount == 0L );
bSuccess = FALSE;
}
else
{
// The refcount is already shutting down.
// This is always goodness.
SAFEASSERT( pRefcount->state == WaitRefShuttingDown );
bSuccess = TRUE;
}
NdisReleaseSpinLock( &pRefcount->lock );
if( bSuccess )
{
// Wait for all consumers to be done
NdisWaitEvent( &pRefcount->Event, 0/*Wait forever*/ );
NdisAcquireSpinLock( &pRefcount->lock );
if( pRefcount->state == WaitRefEnabled )
{
// Someone reactivated us while we were sleeping.
SAFEASSERT( pRefcount->bResettable );
bSuccess = FALSE;
}
else if( pRefcount->state == WaitRefShutdown )
{
// Someone else shut us down while we were sleeping.
SAFEASSERT( pRefcount->Refcount == 0L );
bSuccess = FALSE;
}
else
{
if( pRefcount->Refcount == 0L )
{
// We completed the shutdown.
pRefcount->state = WaitRefShutdown;
bSuccess = TRUE;
}
else
{
// The waitref must have been reactivated and
// shut down again while we were asleep!
SAFEASSERT( pRefcount->bResettable );
bSuccess = FALSE;
}
}
NdisReleaseSpinLock( &pRefcount->lock );
}
return bSuccess;
}
BOOLEAN
BrdgShutdownWaitRef(
IN PWAIT_REFCOUNT pRefcount
)
{
return BrdgShutdownWaitRefInternal( pRefcount, FALSE );
}
BOOLEAN
BrdgShutdownBlockedWaitRef(
IN PWAIT_REFCOUNT pRefcount
)
{
return BrdgShutdownWaitRefInternal( pRefcount, TRUE );
}
VOID
BrdgResetWaitRef(
IN PWAIT_REFCOUNT pRefcount
)
/*++
Routine Description:
Re-enables a wait-refcount. Safe to call in any refcount
state; if the refcount is shut down, this will re-enable it.
If the refcount is in the middle of shutting down, this
will flag it to be re-enabled if the code shutting down the
waitref is using BrdgShutdownOrResetWaitRef().
Arguments:
pRefcount The wait-refcount
Return Value:
none
--*/
{
SAFEASSERT( pRefcount != NULL );
NdisAcquireSpinLock( &pRefcount->lock );
if( pRefcount->state == WaitRefShutdown )
{
// The wait-refcount is completely shut down. We
// can reactivate it.
SAFEASSERT( pRefcount->Refcount == 0L );
pRefcount->state = WaitRefEnabled;
}
else if( pRefcount->state == WaitRefShuttingDown )
{
if( pRefcount->bResettable )
{
// Re-enable. The call to BrdgShutdownWaitRef()
// or BrdgShutdownBlockedWaitRef() will return
// FALSE.
pRefcount->state = WaitRefEnabled;
}
else
{
// Not allowed to reset this refcount when
// in the middle of shutting down
SAFEASSERT( FALSE );
}
}
else
{
// The wait-refcount is already enabled
SAFEASSERT( pRefcount->state == WaitRefEnabled );
}
NdisReleaseSpinLock( &pRefcount->lock );
}