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 #ifndef __INCLUDE_DDK_EXTYPES_H
#define __INCLUDE_DDK_EXTYPES_H #define __INCLUDE_DDK_EXTYPES_H
@ -39,16 +39,18 @@ typedef struct _ERESOURCE
} ERESOURCE, *PERESOURCE; } ERESOURCE, *PERESOURCE;
#define EX_RUNDOWN_ACTIVE 0x1 #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 { typedef struct _RUNDOWN_DESCRIPTOR {
ULONG References; ULONG_PTR References;
PKEVENT RundownEvent; KEVENT RundownEvent;
} RUNDOWN_DESCRIPTOR, *PRUNDOWN_DESCRIPTOR; } RUNDOWN_DESCRIPTOR, *PRUNDOWN_DESCRIPTOR;
typedef struct _EX_RUNDOWN_REF { typedef struct _EX_RUNDOWN_REF {
union { union {
ULONG_PTR Count; ULONG_PTR Count;
PRUNDOWN_DESCRIPTOR Pointer; PRUNDOWN_DESCRIPTOR Ptr;
}; };
} EX_RUNDOWN_REF, *PEX_RUNDOWN_REF; } EX_RUNDOWN_REF, *PEX_RUNDOWN_REF;

View file

@ -1,6 +1,6 @@
/* /*
* ReactOS kernel * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -54,11 +54,27 @@ ExAcquireRundownProtectionEx (
IN ULONG Count IN ULONG Count
) )
{ {
/* Make sure a Rundown is not in progress */ ULONG_PTR PrevCount, Current;
if (RunRef->Count & EX_RUNDOWN_ACTIVE) return FALSE;
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 */ #ifdef _WIN64
RunRef->Count += Count * 2; 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 Success */
return TRUE; return TRUE;
@ -87,7 +103,11 @@ ExReInitializeRundownProtection (
) )
{ {
/* Reset the count */ /* 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 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 */ /* Decrease Reference Count by RundownDescriptor->References */
if (RunRef->Count & EX_RUNDOWN_ACTIVE) { for (;;)
{
ULONG_PTR PrevCount, NewCount;
/* Get Pointer */ if ((Current >> EX_RUNDOWN_COUNT_SHIFT) == RundownDescriptor->References)
RundownDescriptor = RunRef->Pointer; {
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 */ /* Successfully decremented the counter, so bail! */
RundownDescriptor->References -= Count; break;
}
/* If anyone else is still referencing, don't signal the event */
if (RundownDescriptor->References) return; Current = PrevCount;
}
/* Signal the event so anyone waiting on it can now kill it */
KeSetEvent(RundownDescriptor->RundownEvent, 0, FALSE); break;
}
} else { else
/* Decrease Count */ {
RunRef->Count -= Count * 2; 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 */ /* 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 IN PEX_RUNDOWN_REF RunRef
) )
{ {
/* Remove pending rundown */ /* mark the */
RunRef->Count++; #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 IN PEX_RUNDOWN_REF RunRef
) )
{ {
ULONG_PTR PrevCount, NewPtr, PrevPtr;
RUNDOWN_DESCRIPTOR RundownDescriptor; 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 (PrevCount == 0 ||
if (!RunRef->Count) { PrevCount & EX_RUNDOWN_ACTIVE)
/* It's free, set it to Rundown Mode */ {
RunRef->Count = EX_RUNDOWN_ACTIVE; return;
} else {
/* Check if it's already in rundown */
if (RunRef->Count == EX_RUNDOWN_ACTIVE) return;
} }
/* Save number of references */ /* save the reference counter */
RundownDescriptor.References = RunRef->Count / 2; RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
/* Pending references... wait on them to be closed with an event */ /* Pending references... wait on them to be closed with an event */
KeInitializeEvent(RundownDescriptor.RundownEvent, NotificationEvent, FALSE); KeInitializeEvent(&RundownDescriptor.RundownEvent, NotificationEvent, FALSE);
/* Save Rundown Descriptor. This is safe because this stack won't be modified */ NewPtr = (ULONG_PTR)&RundownDescriptor | EX_RUNDOWN_ACTIVE;
RunRef->Pointer = &RundownDescriptor; PrevCount = EX_RUNDOWN_ACTIVE;
/* Set the Count to 1 so nobody else acquires and so release notifies us */ do
RunRef->Count++; {
#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 */ /* Wait for whoever needs to release to notify us */
KeWaitForSingleObject(RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL); KeWaitForSingleObject(&RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL);
} }
/* EOF */ /* EOF */