hopefully a better implementation of rundown protections, thanks also to Alex.

svn path=/trunk/; revision=11395
This commit is contained in:
Thomas Bluemel 2004-10-22 22:49:00 +00:00
parent 1270089c9f
commit 75009fd35f
2 changed files with 129 additions and 49 deletions

View file

@ -1,4 +1,4 @@
/* $Id: extypes.h,v 1.24 2004/10/03 03:03:53 ion Exp $ */
/* $Id: extypes.h,v 1.25 2004/10/22 22:49:00 weiden Exp $ */
#ifndef __INCLUDE_DDK_EXTYPES_H
#define __INCLUDE_DDK_EXTYPES_H
@ -39,16 +39,18 @@ typedef struct _ERESOURCE
} ERESOURCE, *PERESOURCE;
#define EX_RUNDOWN_ACTIVE 0x1
#define EX_RUNDOWN_COUNT_SHIFT 0x1
#define EX_RUNDOWN_COUNT_INC (0x1 << EX_RUNDOWN_COUNT_SHIFT)
typedef struct _RUNDOWN_DESCRIPTOR {
ULONG References;
PKEVENT RundownEvent;
ULONG_PTR References;
KEVENT RundownEvent;
} RUNDOWN_DESCRIPTOR, *PRUNDOWN_DESCRIPTOR;
typedef struct _EX_RUNDOWN_REF {
union {
ULONG_PTR Count;
PRUNDOWN_DESCRIPTOR Pointer;
PRUNDOWN_DESCRIPTOR Ptr;
};
} EX_RUNDOWN_REF, *PEX_RUNDOWN_REF;

View file

@ -1,6 +1,6 @@
/*
* ReactOS kernel
* Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
* Copyright (C) 1998 - 2004 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -54,11 +54,27 @@ ExAcquireRundownProtectionEx (
IN ULONG Count
)
{
/* Make sure a Rundown is not in progress */
if (RunRef->Count & EX_RUNDOWN_ACTIVE) return FALSE;
ULONG_PTR PrevCount, Current;
Count <<= EX_RUNDOWN_COUNT_SHIFT;
/* Loop until successfully incremented the counter */
do
{
Current = RunRef->Count;
/* Make sure a rundown is not active */
if (Current & EX_RUNDOWN_ACTIVE)
{
return FALSE;
}
/* Increment Reference Count */
RunRef->Count += Count * 2;
#ifdef _WIN64
PrevCount = (ULONG_PTR)InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, (LONGLONG)Count);
#else
PrevCount = (ULONG_PTR)InterlockedExchangeAdd((LONG*)&RunRef->Count, (LONG)Count);
#endif
} while (PrevCount != Current);
/* Return Success */
return TRUE;
@ -87,7 +103,11 @@ ExReInitializeRundownProtection (
)
{
/* Reset the count */
RunRef->Count = 0;
#ifdef _WIN64
InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, 0LL);
#else
InterlockedExchangeAdd((LONG*)&RunRef->Count, 0);
#endif
}
@ -101,26 +121,67 @@ ExReleaseRundownProtectionEx (
IN ULONG Count
)
{
PRUNDOWN_DESCRIPTOR RundownDescriptor;
Count <<= EX_RUNDOWN_COUNT_SHIFT;
for (;;)
{
ULONG_PTR Current = RunRef->Count;
/* Check if Rundown is active */
if (Current & EX_RUNDOWN_ACTIVE)
{
/* Get Pointer */
PRUNDOWN_DESCRIPTOR RundownDescriptor = (PRUNDOWN_DESCRIPTOR)((ULONG_PTR)RunRef->Ptr & ~EX_RUNDOWN_ACTIVE);
/* Check if Rundown is in progress */
if (RunRef->Count & EX_RUNDOWN_ACTIVE) {
/* Decrease Reference Count by RundownDescriptor->References */
for (;;)
{
ULONG_PTR PrevCount, NewCount;
/* Get Pointer */
RundownDescriptor = RunRef->Pointer;
if ((Current >> EX_RUNDOWN_COUNT_SHIFT) == RundownDescriptor->References)
{
NewCount = 0;
}
else
{
NewCount = (((Current >> EX_RUNDOWN_COUNT_SHIFT) - RundownDescriptor->References) << EX_RUNDOWN_COUNT_SHIFT) | EX_RUNDOWN_ACTIVE;
}
#ifdef _WIN64
PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)NewCount, (LONGLONG)Current);
#else
PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Count, (LONG)NewCount, (LONG)Current);
#endif
if (PrevCount == Current)
{
if (NewCount == 0)
{
/* Signal the event so anyone waiting on it can now kill it */
KeSetEvent(&RundownDescriptor->RundownEvent, 0, FALSE);
}
/* Decrease Reference Count */
RundownDescriptor->References -= Count;
/* If anyone else is still referencing, don't signal the event */
if (RundownDescriptor->References) return;
/* Signal the event so anyone waiting on it can now kill it */
KeSetEvent(RundownDescriptor->RundownEvent, 0, FALSE);
} else {
/* Decrease Count */
RunRef->Count -= Count * 2;
/* Successfully decremented the counter, so bail! */
break;
}
Current = PrevCount;
}
break;
}
else
{
ULONG_PTR PrevCount, NewCount = Current - (ULONG_PTR)Count;
#ifdef _WIN64
PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)NewCount, (LONGLONG)Current);
#else
PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Count, (LONG)NewCount, (LONG)Current);
#endif
if (PrevCount == Current)
{
/* Successfully decremented the counter, so bail! */
break;
}
}
}
}
@ -134,7 +195,7 @@ ExReleaseRundownProtection (
)
{
/* Call the general function with only 1 reference removal */
ExReleaseRundownProtectionEx(RunRef, 1);
ExReleaseRundownProtectionEx(RunRef, 1);
}
/*
@ -146,8 +207,12 @@ ExRundownCompleted (
IN PEX_RUNDOWN_REF RunRef
)
{
/* Remove pending rundown */
RunRef->Count++;
/* mark the */
#ifdef _WIN64
InterlockedExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)EX_RUNDOWN_ACTIVE);
#else
InterlockedExchange((LONG*)&RunRef->Count, EX_RUNDOWN_ACTIVE);
#endif
}
/*
@ -159,30 +224,43 @@ ExWaitForRundownProtectionRelease (
IN PEX_RUNDOWN_REF RunRef
)
{
ULONG_PTR PrevCount, NewPtr, PrevPtr;
RUNDOWN_DESCRIPTOR RundownDescriptor;
#ifdef _WIN64
PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)EX_RUNDOWN_ACTIVE, 0LL);
#else
PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, EX_RUNDOWN_ACTIVE, 0);
#endif
/* Check if anyone is referencing the structure */
if (!RunRef->Count) {
/* It's free, set it to Rundown Mode */
RunRef->Count = EX_RUNDOWN_ACTIVE;
} else {
/* Check if it's already in rundown */
if (RunRef->Count == EX_RUNDOWN_ACTIVE) return;
if (PrevCount == 0 ||
PrevCount & EX_RUNDOWN_ACTIVE)
{
return;
}
/* Save number of references */
RundownDescriptor.References = RunRef->Count / 2;
/* save the reference counter */
RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
/* Pending references... wait on them to be closed with an event */
KeInitializeEvent(RundownDescriptor.RundownEvent, NotificationEvent, FALSE);
/* Save Rundown Descriptor. This is safe because this stack won't be modified */
RunRef->Pointer = &RundownDescriptor;
/* Set the Count to 1 so nobody else acquires and so release notifies us */
RunRef->Count++;
KeInitializeEvent(&RundownDescriptor.RundownEvent, NotificationEvent, FALSE);
NewPtr = (ULONG_PTR)&RundownDescriptor | EX_RUNDOWN_ACTIVE;
PrevCount = EX_RUNDOWN_ACTIVE;
do
{
#ifdef _WIN64
PrevPtr = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)NewPtr, (LONGLONG)PrevCount);
#else
PrevPtr = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, (LONG)NewPtr, (LONG)PrevCount);
#endif
PrevCount = PrevPtr;
} while (PrevPtr != PrevCount);
/* Wait for whoever needs to release to notify us */
KeWaitForSingleObject(RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL);
KeWaitForSingleObject(&RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL);
}
/* EOF */